[cpp-threads] Asynchronous Execution Issues
Anthony Williams
anthony at justsoftwaresolutions.co.uk
Mon Apr 27 15:14:07 BST 2009
At Mon 27 Apr 2009 14:48:11 BST, Herb Sutter <hsutter at microsoft.com> wrote:
>> Let me be more explicit. Let's assume that I have a thread that's
>> created by, and used by, some library. The library detaches the
>> thread. Since the library designed wants to allow the process to shut
>> down safely without the use of quick_exit, it provides an API call to
>> shut down this helper thread before process exit. My mental picture of
>> how this could work was something like
>>
>> atomic<bool> shutdown_requested;
>>
>> void shutdown_helper()
>> {
>> shutdown_requested = true;
>> // wait for helper thread to acknowledge request using cv, future or
>> the like.
>> }
>>
>> The helper thread occasionally tests shutdown_requested, and if it's
>> set acknowledges the request by notifying the cv and then exits.
>>
>> I think we're saying that this doesn't work, because destructors will
>> be invoked between the notification (or setting the promise) and the
>> thread actually exiting. Those destructors may continue to be invoked
>> past the beginning of process shutdown. Since the destructors are
>> likely to call into the library, the program has undefined behavior,
>> and may crash in practice.
>
> I haven't been following this, but why not arrange for a guard
> object that is destroyed last and signals the cv from its destructor?
>
> void thread_mainline( CV& cv ) {
> Guard g(cv);
> ...
> } // ~Guard signals cv
IIRC, this is what Hans is suggesting. It doesn't get away from the
problem that destructors of thread_local variables are destroyed after
thread_mainline returns, which is thus also after the cv has been
notified, and potentially after any waiting thread has resumed and
after destruction of static-storage-duration objects has started.
>> It seems to me that this is a fairly fundamental problem, which is
>> certainly not limited to asyncs. It seems to me that the presence of
>> detached threads really requires the use of quick_exit() (or a
>> nonterminating program). Effectively, the combination of detached
>> threads and static destructors doesn't ever work.
I agree that it's an issue, but I think "doesn't ever work" is an
exaggeration.
I see several options:
1) Make it undefined behaviour for a thread_local destructor to access
any objects of static storage duration.
2) Provide a function which a thread can call that says "destroy
thread locals NOW", and make it undefined to access any thread_local
after that function returns.
3) Provide a means for a thread to register a single function to be
called after thread_local objects have been destroyed. It is undefined
behaviour for that function to access thread_local objects, but it may
call library functions. This function could then set the "done" flag.
Another thought: if you need to know when a thread is finished, don't
detach it: keep the handle around so you can join with it. This then
requires that waiting on the future for an async() call also joins
with the async() thread so we don't have this particular issue, but
that doesn't strike me as a big deal. The downside here is that it
makes it very hard to write async() yourself, since we haven't
provided the means to write custom future internals.
Anthony
--
Anthony Williams
Author of C++ Concurrency in Action | http://www.manning.com/williams
just::thread C++0x thread library | http://www.stdthread.co.uk
Custom Software Development | http://www.justsoftwaresolutions.co.uk
Just Software Solutions Ltd, Registered in England, Company Number 5478976.
Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK
More information about the cpp-threads
mailing list