www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - issue with calling destructors from GC

reply Ben Hinkle <bhinkle4 juno.com> writes:
I have a question about calling destructors from the GC - in particular at
the program exit. I'm fixing some bugs in std.stream and one of the things
I'm doing is adding a destructor for BufferedStream that closes the source
stream. The problem is that when the GC runs the source stream can be
destroyed before the buffered stream's destructor runs. So I end up trying
to reference an object that has been destroyed. Is there some way I can
tell if an object is still valid - or somehow control the order in which
the GC runs the destructors?

-Ben
Aug 18 2004
next sibling parent "Ben Hinkle" <bhinkle mathworks.com> writes:
I should probably add that a fallback position is to have the buffered
stream's destructor print a warning to stderr if the stream is still open
and the buffer is non-empty. This isn't so bad IMO since managing non-memory
resources though garbage collection is problematic in any case - the least
of which is that the destructor may never run. Failing to close the buffered
stream could mean the final buffered content is never written to the source
stream, but at least a warning to stderr would alert users to the problem.

-Ben


"Ben Hinkle" <bhinkle4 juno.com> wrote in message
news:cg02ss$4fp$1 digitaldaemon.com...
 I have a question about calling destructors from the GC - in particular at
 the program exit. I'm fixing some bugs in std.stream and one of the things
 I'm doing is adding a destructor for BufferedStream that closes the source
 stream. The problem is that when the GC runs the source stream can be
 destroyed before the buffered stream's destructor runs. So I end up trying
 to reference an object that has been destroyed. Is there some way I can
 tell if an object is still valid - or somehow control the order in which
 the GC runs the destructors?

 -Ben

Aug 18 2004
prev sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Ben Hinkle wrote:
 I have a question about calling destructors from the GC - in particular at
 the program exit. I'm fixing some bugs in std.stream and one of the things
 I'm doing is adding a destructor for BufferedStream that closes the source
 stream. The problem is that when the GC runs the source stream can be
 destroyed before the buffered stream's destructor runs. So I end up trying
 to reference an object that has been destroyed. Is there some way I can
 tell if an object is still valid - or somehow control the order in which
 the GC runs the destructors?

AFAIK, the GC doesn't let you control in what order things are destructed. However, if you care about order, here's a way to make sure things appen in order. Basically, the concept is that you have to keep a global list of references to the objects that you want to clean up last. This global list prevents those objects from being garbage. When the other object is cleaned up, you remove this reference in your destructor. Thus, the former object only becomes garbage after you've cleaned up. class Used {} class User { static Object destructionOrderingArray_syncObj; static int[Used] destructionOrderingArray; static this() { destructionOrderingArray_syncObj = new Object; } void removeFromOrderingArray(Used u) { assert(u in destructionOrderingArray); destructionOrderingArray[u]--; if(destructionOrderingArray[u] == 0) delete /*from array*/ destructionOrderingArray[u]; } void addToOrderingArray(Used u) { if(u in destructionOrderingArray) destructionOrderingArray[u]++; else destructionOrderingArray[u] = 1; } Used _myUsed; void used(Used u) { // settor property if(u === _myUsed) return; synchronized(destructionOrderingArray_syncObj) { if(_myUsed !== null) removeFromOrderingArray(_myUsed); addToOrderingArray(_myUsed); } _myUsed = used; } Used used() { // gettor property return _myUsed; } ~this() { if(_myUsed !== null) removeFromOrderingArray(_myUsed); } }
Aug 18 2004
parent Berin Loritsch <bloritsch d-haven.org> writes:
Russ Lewis wrote:

 Ben Hinkle wrote:
 
 I have a question about calling destructors from the GC - in 
 particular at
 the program exit. I'm fixing some bugs in std.stream and one of the 
 things
 I'm doing is adding a destructor for BufferedStream that closes the 
 source
 stream. The problem is that when the GC runs the source stream can be
 destroyed before the buffered stream's destructor runs. So I end up 
 trying
 to reference an object that has been destroyed. Is there some way I can
 tell if an object is still valid - or somehow control the order in which
 the GC runs the destructors?

AFAIK, the GC doesn't let you control in what order things are destructed. However, if you care about order, here's a way to make sure things appen in order. Basically, the concept is that you have to keep a global list of references to the objects that you want to clean up last. This global list prevents those objects from being garbage. When the other object is cleaned up, you remove this reference in your destructor. Thus, the former object only becomes garbage after you've cleaned up.

The thing is that GC is essentially nondeterministic, and efforts to make it deterministic only cause the language implementation to become very complex--and worse, it invites people to try to out-think the GC. If you accept that the GC is nondeterministic then there are ways to deal with it. So, if you call delete on a deleted object would an exception be thrown, or would the system ignore the extraneous call to delete? This would allow the BufferedStream to delete the underlying stream (and if written correctly consequently release the resources held by it), and do nothing if the underlying stream has been collected.
Aug 18 2004