From: Johan Nilsson (johan.nilsson_at_[hidden])
Date: 2004-06-08 04:23:38
"Malcolm Noyes" <boost_at_[hidden]> wrote in message
> > 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
> >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.
Yes, global (implicit or explicit) tss data is likely cause
non-deterministic behaviour in such a solution. I'm not so sure you should
wait for other threads to complete in the context of exiting the
process/terminating the rtl (actually I'm convinced it's not, even if this
is not inside a dll).
> 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
I don't agree, but having different opinions is definitely allowed :-)
> 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).
I'm more and more leaning towards a non-worker-thread, lazy tss cleanup
implementation (if any at all for static linking). Look further down as
> 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.
See comments below.
> 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 ;-)
I believe all of the above suggestions (including the WFMO + event
variation) would render it impossible to use boost's tss functionality
outside threads created using Boost.Thread.
As I mentioned above, perhaps a pure lazy-tss-cleanup would be the way to go
(possibly in combination with a timer-activated cleanup thread). To
guarantee cleanup of thread data at primary thread exit; the thread-id <->
data map could clean up all data on destruction (assuming it is a
global/singleton object). The details would need to be fleshed out and a
test implementation heavily excercised, I guess.
The only real problem I can foresee is for threads requiring deterministic
cleanup of their resources. A possibility to explicitly clean up their tss
data could also be provided for this purpose.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk