Steven Schveighoffer Wrote:
> 
> What if a thread no longer exists?  Would there be a way to mark such  
> dtors that need this feature?  I don't know, because it seems to me like a  
> lot of band-aiding for something that is infrequently used (I've gotten  
> along fine in D without reference counting for as long as I've used it).

If a thread no longer exists then it doesn't matter who calls the finalizers so 
long as they aren't finalized concurrently by more than one thread.  To 
simplify things, the thread could notify the GC on termination so it could 
eagerly do whatever maintenance it wants to.

The only time this dtor wouldn't be called is for threads that terminate 
abnormally via pthread_exit or Windows TerminateThread, but because such 
threads have been left in an invalid state (uncalled dtors or other scoped code 
may have left mutexes locked, etc), finalizing any known data risks a deadlock 
or worse.  However, since these thread termination routines aren't supported in 
D (or C++ for that matter, as far as I know), not finalizing the data would 
simply be another effect of the resulting undefined behavior.

> You also need to provide a pointer in the object for a linked list for the  
> work-list.

The work-list could be a list of delegates.  Then the footprint of objects 
doesn't need to be altered.

> I'm thinking I would prefer to have the destructor and finalizer split, so  
> the 'destructor' can tell whether it's being called synchronously or from  
> the GC.  Then the dtor itself can handle the whole worklist implementation.

This is actually very easy, as druntime already differentiates between the two 
(the 'det' flag in rt_finalize).  I haven't thought enough about where the 
worklist code should live though.  All the GC knows about finalization is that 
it calls rt_finalize() on blocks with the FINALIZE bit set, but at the same 
time it's a perfect choke-point for iterating on the work-list when allocations 
are performed.

There are three situations that have th be dealt with:

1. The GC finalizes a shared object.
2. The GC finalizes an unshared object, but the GC was invoked by the object's 
owner thread.
3. The GC finalizes an unshared object, but the GC was invoked by another 
thread.

The GC would probably be the best place to track which thread owned what object 
so it would probably be the best place to store the work-list as well.  This is 
somewhat similar to how HOARD handles freed memory, so there is certainly a 
precedent for an allocator taking care of this sort of thing.

> I guess the only extra thing you would need in addition to that is a hook  
> so memory allocation from a given thread can be hooked to process the  
> worklist.  Or maybe the worklist becomes a standard feature of the thread  
> class, and you manually add the task from the destructor instead of the GC  
> doing it for you.

Hm... this would delay the finalization of shared objects as well, but perhaps 
that doesn't matter since they can be safely finalized by any thread.  I'm 
still inclined to say that the GC should take care of the grunt work though, 
unless someone wants to argue that owner thread ID of every object should be 
stored in a new hidden field in the object itself.

Reply via email to