[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