Boost logo

Boost :

From: Malcolm Noyes (boost_at_[hidden])
Date: 2004-06-07 14:52:34

> I don't see any implementation
>problems with this approach as long as it would only be used inside
>executable modules (i.e. static linkage), but as I haven't actually tried to
>implement it ...

IIRC, the complexity arises when you start to consider global scope
thread specific variables in different translation units, together
with the possibility that threads can be started before 'main' and
waited on, i.e. joined, after 'main has completed. Ah, I see the
remains of the bad dream now . . . imagine you need to wait for
completion of some thread that is joined in the destructor of a global
in a different translation unit. You need to make sure nothing will
wait for your thread for any reason (otherwise you deadlock), and you
need to somehow make sure that any object cleanup is handled by the
thread local object and doesn't rely on any statics since they may
have been cleaned up before the global in the other translation unit.

I have a vague recollection that I was trying to ensure that the
thread that I started actually completed by joining it, but it makes
no sense to do that so I think now that this approach might work,
although it's a little complex. Note that the overhead of one
additional 'thread per thread' might not be too great since the
secondary (cleanup) thread gets started only once (this could be
either on the first access, or when a boost thread starts) and then
goes to sleep until the primary thread ends. The simplicity of this
design might be preferable to the limitation/complexity imposed by

Also you should be aware that the contract that states that the
cleanup handler *will* be called when the primary terminates is
technically invalid, since there is no way to ensure that the cleanup
thread will ever be scheduled when the primary thread ends. In most
cases it will be scheduled, but there are no actual guarantees.
Looking through the archives, I believe Bill Kempf was very keen to
preserve this contract (I can understand why and I think I agree).

One other advantage to 'one thread per thread' is that the cleaup
thread only has to manage one set of cleanup routines (for the thread
that it's waiting for) and it is *guaranteed* to stay asleep until the
primary has ended. For this reason you wouldn't need any locks
guarding the thread local object, as seem to be needed with the dll
design (at least my recollection of the code in thread_dev is that
there is a lock on the tss cleanup collection, although it may be
possible to design that away). It might be possible to do this with
WFMO too, but it may get a bit complex.

The other alternative approach (requiring an instance of an object
allocated in the thread function and/or functor) also requires no
locks, and guarantees that the cleanup must be called as long as the
destructor gets called, which will happen when the thread/functor
exits normally or via an exception (i.e. it won't happen if you call
TerminateThread(), but all bets are off then anyway). Oh, and it
feels more in keeping with C++ since the language enforces cleanup via
a destructor and it doesn't require any additional threads either ;-)

Malcolm Noyes

Boost list run by bdawes at, gregod at, cpdaniel at, john at