
"Malcolm Noyes" <boost@alchemise.com> wrote in message news:9lsub0pe67kve6uk1hfaif6o374hs9agg9@4ax.com...
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 haven't had enough 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 signaled, 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 ;-)
This is an interesting approach, and worth looking into in spite of the problems you mention.
ii) Implement a thread watchdog via a dll using thread detach. This is the idea that Michael referred to that Roland proposed. I haven't tried this for 2 reasons; first it *requires* the distribution of a dll,
Actually, if I understand it correctly, the whole point of Roland's approach is that it doesn't require the distribution of a dll. The "dll" was created on-the-fly by the Boost.Thread code.
and I'm aware from my own experience how difficult that is in some organisations from a political and logistical point of view.
Unfortunately true.
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.
This is a good point.
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 . . .
I'd be very interested in the results. Mike