Some recent posts got me thinking about the fact that the garbage collector
collects garbage in random order and so one can't rely on destructors being
run in any particular order. To work around this I've hacked up destructor
events. There is one interface and one mixin template.

Ben, I like what you've done here. I coded something similar a while back that
was in the same vein, did you take a look at this before? Then again, maybe
wer're solving two different problems:
http://svn.dsource.org/svn/projects/dsp/trunk/misc/systemFinalizer.d
Maybe we could mesh our designs? I like the idea of having a mixin that can be
dropped into a class to register it for deterministic finalization. Its
something that D could really use.
Also, I thought the problem was that destructors only run in an unpredictable
order at program termination (if at all). Is this still the case, or is there a
deeper problem during runtime?
- Pragma
[[ Eric Anderton at yahoo dot com ]]

They look to be related but slightly different. With your code a reference
to the object being tracked is put in a static table so the object is never
collected until program exit (and maybe not even then?). If one uses the
AutoSystemFinalizer the objects will get collected after the scope is exited
so at least then the objects don't sit around forever. With my code I was
trying to keep all the references just between the object being tracked and
the object doing the tracking so they can be collected at any time they both
become garbage.

Maybe we could mesh our designs? I like the idea of having a mixin that

dropped into a class to register it for deterministic finalization. Its
something that D could really use.

I wasn't going to develop that code any more. It was just to illustrate how
one could go about enforcing an order. It is slightly subtle but doable. If
you want to expand/merge/whatever my posed code feel free to. I am tempted
to try to add something like this to std.stream to make a BufferedStream
automatically flush the buffer and close the backing stream in destructors
but the whole DtorEvent thing needs the backing stream to fire the event and
if someone has a Stream subclass that doesn't fire the event it messes up
the whole thing. So for now I'm leaning towards forcing Stream users to
always close their streams explicitly.

Also, I thought the problem was that destructors only run in an

order at program termination (if at all). Is this still the case, or is

deeper problem during runtime?

The unpredictable order is any time the GC runs. Since the GC always runs at
exit people tend to see the problem at exit. The way the GC works is it
marks all garbage and then loops over the marked memory and calls any
destructors for a given memory block. It has no idea if one block of memory
needs to be collected before another block.

Perhaps we should just add functionality to the GC to cover this. We
could add a function to the GC that looks like this:
void gc.SetDestroyOrder(void *a,void *b);
This function would register a destruction ordering; a would always get
cleaned up before b. The function would throw an exception if this
created a loop. Then you could write classes like this:
class A {
B b;
this() {
b = new B;
gc.SetDestroyOrder(this,b);
}
~this() {
b.DoSomething();
}
}

Perhaps we should just add functionality to the GC to cover this. We
could add a function to the GC that looks like this:
void gc.SetDestroyOrder(void *a,void *b);
This function would register a destruction ordering; a would always get
cleaned up before b. The function would throw an exception if this
created a loop. Then you could write classes like this:
class A {
B b;
this() {
b = new B;
gc.SetDestroyOrder(this,b);
}
~this() {
b.DoSomething();
}
}

yeah - that would be a better solution. I wonder why collectors in C# and
Java don't have that ability? It seems like it would be pretty nice. I
wonder if it is a performance thing.

Perhaps we should just add functionality to the GC to cover this. We
could add a function to the GC that looks like this:
void gc.SetDestroyOrder(void *a,void *b);

I suspect that it would be more practical and flexible, long term, to stop
thinking of /the/ garbage collecter - instead, think only of /a/ garbage
collector. That is, the built-in GC may not necessarily be the only one.
If you link with a DLL, maybe that DLL will have its own GC. Maybe that DLL
wasn't even written in D, but some other language (maybe even a D++ of the
future).
Or maybe you'd like to write a custom allocator which uses its own GC for some
specialized purpose. Right now, you can't do this, because the existing GC won't
co-operate with other GCs. It believes itself to be the only one in existence.
What makes more sense to me would be to separate out the function of garbage
/management/ from the function of garbage /collection/. A garbage collector
could allocate memory (by whatever own means) and then register that memory with
the (unique) garbage manager. The GM would decide when a particular block of
memory was unreachable, but instead of destructing/freeing it, all it would have
to do is notify the particular GC which registered it that it was now unused.
The particular GC which allocated it could then destruct/free it (and this in
turn could be a two-stage process, if a custom allocator/deallocator had been
used).
So, back to...

void gc.SetDestroyOrder(void *a,void *b);
This function would register a destruction ordering; a would always get
cleaned up before b. The function would throw an exception if this
created a loop.

That could now be implemented as a member function of /a/ (not the) garbage
collector - perhaps even a subclass of a Phobos class called GarbageCollector.
New GC => new functionality.
Maybe this is getting way too sophisticated, but the DLL issue will come back to
haunt us if we ignore it. Not everything wants to be statically linked, and
there are sufficient problems with destruction events that systems programmers
are going to want to take control of memory management somehow. Right now, we
can do that only if we completely ignore the GC. It would be so much nicer to be
able to co-operate with it.
Arcane Jill

I think you make a lot of sense here.
Question: Should we have the capacity for different GMs? Some objects
may be subject to different types of garbage collection criteria, or
some types of data may be more easily managed with a different type of
collector (like a refcounting collector, or a by-hand collector).
Imagine a collector tied into a windowing protocol; as long as the
window is active, the windowing manager holds a virtual reference to the
window object; when the window is destroyed, the virtual reference to
the window object goes away and the window object MIGHT be collected.
Arcane Jill wrote:

In article <ci7rqu$cah$1 digitaldaemon.com>, Russ Lewis says...

Perhaps we should just add functionality to the GC to cover this. We
could add a function to the GC that looks like this:
void gc.SetDestroyOrder(void *a,void *b);

I suspect that it would be more practical and flexible, long term, to stop
thinking of /the/ garbage collecter - instead, think only of /a/ garbage
collector. That is, the built-in GC may not necessarily be the only one.
If you link with a DLL, maybe that DLL will have its own GC. Maybe that DLL
wasn't even written in D, but some other language (maybe even a D++ of the
future).
Or maybe you'd like to write a custom allocator which uses its own GC for some
specialized purpose. Right now, you can't do this, because the existing GC
won't
co-operate with other GCs. It believes itself to be the only one in existence.
What makes more sense to me would be to separate out the function of garbage
/management/ from the function of garbage /collection/. A garbage collector
could allocate memory (by whatever own means) and then register that memory
with
the (unique) garbage manager. The GM would decide when a particular block of
memory was unreachable, but instead of destructing/freeing it, all it would
have
to do is notify the particular GC which registered it that it was now unused.
The particular GC which allocated it could then destruct/free it (and this in
turn could be a two-stage process, if a custom allocator/deallocator had been
used).
So, back to...

void gc.SetDestroyOrder(void *a,void *b);
This function would register a destruction ordering; a would always get
cleaned up before b. The function would throw an exception if this
created a loop.

That could now be implemented as a member function of /a/ (not the) garbage
collector - perhaps even a subclass of a Phobos class called GarbageCollector.
New GC => new functionality.
Maybe this is getting way too sophisticated, but the DLL issue will come back
to
haunt us if we ignore it. Not everything wants to be statically linked, and
there are sufficient problems with destruction events that systems programmers
are going to want to take control of memory management somehow. Right now, we
can do that only if we completely ignore the GC. It would be so much nicer to
be
able to co-operate with it.
Arcane Jill