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.