Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

No single safe way to notify other objects of my termination from both GC and delete

Moderators: kris

Posted: 04/24/10 15:27:05

I'm having problems maintaining proper safe resource-management in D1/Tango. My basic scenario is that my application holds an asset open through a Socket to upstream nodes, while forwarding it to downstream nodes. Whenever downstream nodes release the Asset, it should also be released upstream.

My problem is finding a safe way to do this, that does not rely on forced explicit termination from downstream nodes. Essentially, I can't use the dtor, since I can't call any members on the upstream socket (it may have been GC:ed). I could use dispose, but that won't get called by explicit delete. I could write a complex harness using a combo of rt_attachDisposeEvent, but AFAIU that has the basic problems of dispose, it's not called on explicit delete. Could perhaps be worked around by keeping a list of remote references, which is purged by rt_attachDisposeEvent, and cleansed by the dtor, but it seems like something like this should be made easier than that by the runtime/stdlib.

Here:s a small diagram showing the problem. http://imgpaste.com/i/llmkp.png. In short; there is no single runtime-supported way to notify other objects of my termination from both GC and delete.

One ?simple? solution would be to ensure that dispose/rt_attachDisposeEvent gets called also for explicit delete, in which case dispose would be a good point to cleanup resources (in which case one may question the purpose of the dtor?).

Another could perhaps be to make the GC work in multiple passes, one collection-pass, one finalization, and one destruction, where the dtor is called during finalization, ensuring calling methods on other objects is safe in the dtor. (Eliminating the need for dispose, I think?)

Yet a third option could perhaps be to implement some kind of RefCounting? base-class in Tango, with smart-pointers for other objects to reference it. D1 is missing a few overloadable operators for fully mimicking pointers, but it could work with some compromises. The advantage would be more predictable timing for when resources are released, the drawback would be performance-hits for referencing those objects, and those objects can not inherit any other base-class, imposing design-limitations, a third drawback is of course that the programmer must actively remember when to access through smart-pointer instead of direct reference. (Also, can class-allocators be overridden in child-classes?)

Personally, without much understanding of the entire complexity of a garbage collector, I'd think that option 2 AND 3 should both be implemented, making the dtor in general more usable (as of now, I really don't see ANY other sensible use for it than closing C-API-resources, or explicit non-GC-allocated objects). Then additionally, a ref-counting structure would be useful for deterministic destruction in some scenarios. (Also, I think it should be much easier to implement if the dtor is deterministicly safe.)

Thoughts?

Author Message

Posted: 04/24/10 18:07:49

Hi rawler,

The primary issue is that a dtor is actually a finalizer instead - D documentation is thoroughly misleading on this point. In order to skirt some of the associated issues, Tango introduced Object.dispose() which is invoked on an explicit delete and upon a scope completion with all references intact. However, dispose() can't be invoked during a finalizer phase since the GC does not have predictable ordering (where the initial finalizer issues arise from).

As you indicated, one way to fix the issue is to rewrite the GC - that's a decidedly non-trivial task, and one which would require a volunteer. Another approach would be to use C heap-allocation, or self-managed free-lists to maintain the crucial structures involved (move them out of GC space so they may be safely referenced from within a finalizer). Ref counting could be a nice alternative, but is perhaps a bit heavyweight implemented as a superclass rather than a struct? D1 cannot effectively use structs for ref-counting, although D2 reputedly will have appropriate mechanisms for that.

I fully agree D is poorly equipped in this regard, yet folks responsible for the language have been unresponsive on the topic for the last five years and I'm not sure how much further we can patch around the root cause without replacing the GC itself.

Posted: 04/24/10 20:10:38

The finalizer issue seems to be a hard one? AFAIK, Java has not solved it, and their GC is world class.

I don't really see the issue with rt_disposeEvent - if you explicitly delete the object, you don't need to be notified of it?

Either call dispose explicitly, or call it from the disposeEvent. I suppose there may be pitfalls in this method, but I believe the idea is sound.