auto classes and finalizers
Bruno Medeiros
brunodomedeirosATgmail at SPAM.com
Sun Apr 9 11:18:12 PDT 2006
Sean Kelly wrote:
> Jarrett Billingsley wrote:
>> "Sean Kelly" <sean at f4.ca> wrote in message
>> news:e10pk7$2khb$1 at digitaldaemon.com...
>>> - a type can have a destructor and/or a finalizer
>>> - the destructor is called upon a) explicit delete or b) at end
>>> of scope for auto objects
>>> - the finalizer is called if allocated on the gc heap and the
>>> destructor has not been called
>>
>> Would you mind explaining why exactly there needs to be a difference
>> between destructors and finalizers? I've been following all the
>> arguments about this heap vs. auto classes and dtors vs. finalizers,
>> and I still can't figure out why destructors _can't be the
>> finalizers_. Do finalizers do something fundamentally different from
>> destructors?
>
> Since finalizers are called when the GC destroys an object, they are
> very limited in what they can do. They can't assume any GC managed
> object they have a reference to is valid, etc. By contrast, destructors
> can make this assumption, because the object is being destroyed
> deterministically. I think having both may be too confusing to be
> worthwhile, but it would allow for things like this:
>
> class LinkedList {
> ~this() { // called deterministically
> for( Node n = top; n; ) {
> Node t = n->next;
> delete n;
> n = t;
> }
> finalize();
> }
>
> void finalize() { // called by GC
> // nodes may have already been destroyed
> // so leave them alone, but special
> // resources could be reclaimed
> }
> }
>
> The argument against finalizers, as Mike mentioned, is that you
> typically want to reclaim such special resources deterministically, so
> letting the GC take care of this 'someday' is of questionable utility.
>
>
> Sean
Ok, I think we can tackle this problem in a better way. So far, people
have been thinking about the fact that when destructors are called in a
GC cycle, they are called with finalizer semantics (i.e., you don't know
if the member references are valid or not, thus you can't use them).
This is a problem when in a destructor, one would like to destroy
component objects (as the Nodes of the LinkedList example).
Some ideas where discussed here, but I didn't think any were fruitful. Like:
*Forcing all classes with destructors to be auto classes -> doesn't
add any usefulness, instead just nuisances.
*Making the GC destroy objects in an order that makes members
references valid -> has a high performance cost and/or is probably just
not possible (circular references?).
Perhaps another way would be to have the following behavior:
- When a destructor is called during a GC (i.e., "as a finalizer") for
an object, then the member references are not valid and cannot be
referenced, *but they can be deleted*. It will be deleted iff it has not
been deleted already.
I think this can be done without significant overhead. At the end of a
GC cycle, the GC has already a list of all objects that are to be
deleted. Thus, on the release phase, it could be modified to have a flag
indicating whether the object was already deleted or not. Thus when
LinkedList deletes a Node, the delete is only made if the object has
already been deleted or not.
Still, while the previous idea might be good, it's not the optimal,
because we are not clearly apperceiving the problem/issue at hand. What
we *really* want is to directly couple the lifecycle of a component
(member) object with it's composite (owner) object. A Node of a
LinkedList has the same lifecycle of it's LinkedList, so Node shouldn't
even be a independent Garbage Collection managing element.
What we want is an allocator that allocates memory that is not to be
claimed by the GC (but which is to be scanned by the GC). It's behavior
is exactly like the allocator of
http://www.digitalmars.com/d/memory.html#newdelete but it should come
with the language and be available for all types. With usage like:
class LinkedList {
...
Add(Object obj) {
Node node = mnew Node(blabla);
...
}
Thus, when the destructor is called upon a LinkedList, either
explicitly, or by the GC, the Node references will always be valid. One
has to be careful now, as mnew'ed object are effectively under manual
memory management, and so every mnew must have a corresponding delete,
lest there be dangling pointer ou memory leaks. Nonetheless it seems to
be only sane solution to this problem.
Another interesting addition, is to extend the concept of auto to class
members. Just as currently auto couples the lifecycle of a variable to
the enclosing function, an auto class member would couple the lifecycle
of its member to it's owner object. It would get deleted implicitly when
then owner object got deleted. Here is another (made up) example:
class SomeUIWidget {
auto Color fgcolor;
auto Color bgcolor;
auto Size size;
auto Image image;
...
The auto members would then have to be initialized on a constructor or
something (the exact restrictions might vary, such as being final or not).
--
Bruno Medeiros - CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
More information about the Digitalmars-d
mailing list