Writing and using Classes in Dyalog APL


Postby petermsiegel on Wed Jun 08, 2016 6:23 am

According to the Dyalog APL Pgmer's Guide & Lang Ref, a destructor for a class instance will be called [also] "On the execution of )CLEAR, )LOAD, ⎕LOAD, )OFF or ⎕OFF." This is true even if the object has not been deleted, and is "actively" referred to in the currently saved version of the workspace. This is very useful as a way of "cleaning up" session resources which are dynamic or volatile when the session ends (e.g. storage used in association with libraries called from APL, temporary files, etc.). It could even be used as a generalized session "ONEXIT" to make sure all kinds of session resources are appropriately updated (not just a single object) when APL exits. So far so good.

Here's the complication. Let's say you have an object [in a w/s] which has been )SAVEd, and the object has a destructor. You want that destructor to be called exactly once. But as of yet, you haven't deleted all references or otherwise "cleaned it up". In fact, as far as you are concerned, the OBJECT has NOT been deleted-- in fact its APL fields are still very much up-to-date and in use.

But you have executed )OFF or ⎕OFF or a ⎕LOAD of another W/S. Now APL calls the destructor on this active object as it exits. [Fair enough, but it won't just call this destructor ONCE. Keep reading.]

Since the object was saved, it is still there in the saved w/s, but you've just run the DESTRUCTOR code-- which may impact files, other active tasks, or memory outside APL.

Then, you )LOAD the W/S again, do some new work (possibly UNRELATED to that object). Then you issue a new )OFF. ONCE AGAIN, the destructor is called-- the W/S when saved before had no record that the object's destructor had been called, since the object in the SAVED w/s is still intact from a time BEFORE the destructor was called [only the in-memory version has been destroyed).

So, this time, whatever DESTRUCTion you intended has already been done, but now APL tries to do it again and boom! You try to release an already released or NON-EXISTENT resource. (In my case, trying to FREE storage that I've already released causes the interpreter to die immediately in OS X). You can't get any control, since the action is taken after the save.

Let's assume you LIKE the destructor action, but want it to happen exactly once and only in the CURRENT session-- not EVERY SESSION from now on until the object is ACTUALLY deleted. (Again, it's fully intact in the SAVED W/S).

My workaround is inelegant-- create a unique id for the current session (I've done so, using the timestamp ⎕TS and ⎕AI[2+⎕IO] to calculate the starting second of this session as the unique id, which I save with my DESTRUCT-able object) and ONLY do useful destruction if the destructor is called in the same SESSION that the object was built or updated in. In my case, I've allocated a lot of non-APL storage via MALLOC (of large integers of 10's-100's of thousands of bytes) and I want to FREE it during the ACTIVE session, but not tomorrow. This is a tad kludgey, but it works. And did I say, it's kludgey?

While the APL behavior is documented, and useful, it's easy to create less esoteric scenarios where this DESTRUCTOR behavior creates odd side effects: e.g. deleting temporary files-- you may NOT want a saved instance variable's destructor to be called at EVERY logoff or similar, especially if you actually are still USING the instance. In some cases, (e.g. if the destructor fails), you see an error on an object you can no longer get access to. Only solution is to COPY the w/s and reSAVE it-- now without phantom or half-digested objects.

Is there a better way to manage this feature of DESTRUCTORS?
Posts: 98
Joined: Thu Nov 11, 2010 11:04 pm

Return to Object Oriented Programming

Who is online

Users browsing this forum: No registered users and 1 guest