|
Boost : |
From: Malcolm Noyes (boost_at_[hidden])
Date: 2004-06-03 14:45:39
> unless I can implement tss cleanup properly on
>Win32 (which I haven't proved yet) the static library option would
>have to disappear again on Win32.
There are 3 possible ways that I'm aware of to attempt to fix this
problem, although all have their drawbacks. I've tested at least 2 of
them on some samples so I know that in principle they work, but I
havn't had enogh time to test these to identify all the issues. The
3 methods that I know of are:
i) At the first use of tss data, start a new thread to wait on the
original thread handle. The theory is that when the original thread
ends, the handle gets signalled, the 'watchdog' thread wakes up and
cleans up the tss data for the original thread. An obvious
optimisation is to have a watchdog class that can wait for several
threads per instance (up to 63 since you'd have to have an event [or
similar] to 'wake up' the thread when you wanted to add a new thread
handle to wait for). This sounds easy but the optimisation
complicates the implementation, as does the possibility that you might
be waiting on a thread that doesn't end until after 'main' has ended
(maybe the thread is joined in a static destructor). This is the
solution that I tried first and I recall that there was some problem
with termination that made it unworkable, but I'm sure that it should
be possible. The idea of a watchdog has of course been mentioned
before ;-)
ii) Implement a thread watchdog via a dll using thread detach. This
is the idea that Michael referred to that Roland proposed. I havn't
tried this for 2 reasons; first it *requires* the distribution of a
dll, and I'm aware from my own experience how difficult that is in
some organisations from a political and logistical point of view.
Second, there are things that you can't do in DllMain, for example
MSDN mentions this: "Calling Win32 functions other than TLS,
object-creation, and file functions may result in problems that are
difficult to diagnose" (as usual with MS documentation, it's not clear
whether this restriction applies to DLL_THREAD_DETACH). So whilst it
would be possible to create a dll that had a 'C' interface (so that we
could 'LoadLibrary' easily) and provided a callback for a cleanup
function (passing a thread id so that we can use it to lookup in a map
and perform the cleanup), there may be (i.e. probably will be)
restrictions on what 'cleanup' might be allowed to do, so basically
any non-trivial cleanup (e.g. calling socket functions) would not be
allowed.
iii) Implement a 'cleanup guard' as part of the thread function.
Basically this means that we have an object that is responsible for
managing the lifetime of the tss data and the lifetime of the guard
object is limited by the 'scope' of the thread function (i.e. an
instance of an object is declared at the beginning of the thread
function and the destructor cleans up). Clearly this is easy to
implement automatically for boost initialised threads, and would
probably also be possible for use of tss data in the primary thread
(i.e dynamic initialisation of statics and 'main') by introducing a
static. That would leave 'adopted' threads which would have to
declare an instance of the guard object to be able to use tss data.
This solution has the advantage of simplicity (it's trivial to
implement the allocation and cleanup of the tss data using a vector,
for example) but has the disadvantage that it would require a change
to the published interface for tss data - users of adopted threads
with tss data requirements have to declare an instance of the guard to
manage the scope of the tss data. It also has one other significant
advantage IMO, which is that it should be possible to allow each new
instance of the guard to manage all tss data for the thread until it
gets destroyed, which would allow for an instance of the guard to be
declared in a functor that was passed to a thread pool. This would
mean that tss data used by functors when a thread pool thread executes
the function would be cleaned up when the *functor* exits, not when
the thread exits (which conceptually may never happen). This seems to
me to be a more sensible place to apply the cleanup that when the
thread exits.
I'm trying to pull together a test implementation of all three methods
- if I ever get there I'll post the results . . .
Malcolm Noyes
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk