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