On Sep 15, 2005, at 18:29, Nicholas Clark wrote:


On Wed, Sep 07, 2005 at 02:37:33PM +0200, Leopold Toetsch wrote:

Parrot calls the "destroy" vtable function for all PMCs that have marked themselves with PObj_active_destroy_FLAG, when the object gets recycled
due to GC *or* at program exit.

If I have 2 objects in a self-referencing loop, what happens?

    A---+
    ^   |
    |   v
    +---B

Clearly mark and sweep can spot these things before the end of the world, but how does all the destruction work? The GC has to call one PMC's destroy
first, so let's call that one A.

A's destroy gets called. At this time A is pointing to a valid B object,
so it can make calls on B.

destruction (the low-level kind aka the free(3) thingy) doesn't have problems with such circular dependencies. At least if these rules are followed:
* code your vtable destroy handler conservatively:

   if (SELF->mem) {
      free(SELF->mem);
      SELF->mem = NULL;
   }

* don't call VTABLE_destroy on parent PMCs or such - GC calls destroy for you (if the object is marked with the active_destroy flag bit)

Therefore above case can't happen - you just don't call B's destroy method.

But all you are mentioning is true for VTABLE_finalize, which is unimplemented. It will have to deal with finalize dependencies and even with user code that tries to revive already dead objects.

At the end of this call, A is (presumably) in a consistent state, but not
a state consistent with having other methods called on it. But is its
vtable still active? Or is the vtable replaced with something else?

For the finalize case: we have to put PMCs that need finalization on a separate list during GC, then sort this list (to our best knowledge), call finalize handlers on all, and the eventually destroy the real dead (or maybe revive some others (if possible). The objects are still intact at this time, they just have the state "doomed".

Because what must come next is that B's destroy gets called. And as B still
has a pointer to A, B might (naïvely) try to make calls on A.

I think there are two kinds of implementation schemes:
- Parrot calls finalize in MRO order
- no automatic finalize calls are done - it's up to user code to do the rigth thing

This isn't speced yet. The finalize relations A->B and B->A could also be arbitray and outside the OO model. Then it's of course up to the user code, to set some flags, or nullify some fields, so that it's known that finalization is already done.

Or are all objects that actively destroy themselves also expected to
actively swap their vtables to something safe?

That's probably as safe as demanding: just don't do unsafe operations ;-)

Nicholas Clark

leo

Reply via email to