[cpp-threads] Update on N2889/N2880/N2901

Anthony Williams anthony at justsoftwaresolutions.co.uk
Mon Jun 22 08:56:40 BST 2009


It's Monday morning already here, but I know it's still Sunday for some 
in the US, so I'll comment anyway.

Herb Sutter wrote:
> 1A: No detaching. Remove thread::detach from the standard. An
> argument against this is that Boost.Thread has thread::detach.
> Nevertheless, the feature is fundamentally flawed. Boost.Thread
> likewise has a thread::~thread with detach semantics, which has
> already been recognized is flawed in N2802 for similar reasons and
> was changed to terminate semantics in the WP at the Summit meeting. 

Just because detached threads cannot reliably use global or static 
objects during static destruction does not make them nearly useless. 
That is grossly overstating the case. Many uses of detached threads will 
be guaranteed to be completed before static destruction, because the 
rest of the program relies on their side effects. In particular, 
anything that uses manual synchronization such as condition variables, 
futures or atomic values to identify when the thread has finished will 
have defined behaviour.

This is particularly the case for programmers coming from a POSIX 
threads background, where pthread_join is explicitly referred to as a 
convenience function. For example, in Programming with POSIX Threads, 
David Butenhof says "pthread_join is a convenience, not a rule" (p43 in 
my copy).

Ruling out detached threads makes useful cases with custom 
synchronization harder to write. I am strongly against removing detached 
threads.

std::thread is supposed to be the lowest possible layer. If we want a 
non-detachable, safer layer it should be built on top. We should not 
restrict std::thread in order to make some use cases safer, at the cost 
of making other useful cases much more expensive.

> 1B: Automatic joining. Require that the end of main automatically
> join with all still-running non-main threads before static
> destruction begins. An argument against this option is that some
> implementers want to write std::thread as a zero-overhead wrapper
> around another C-based API, such as pthreads. C-based APIs support
> detaching much more readily because they are designed for a language
> where shutdown is almost trivial: run atexit handlers then flush and
> close streams/files. However, discussion should address what the
> overhead actually is, especially considering that not joining will
> essentially result in undefined behavior.

Joining all threads before static destruction prevents globals starting 
threads in their constructor and joining them in the destructor (perhaps 
after setting a flag which will trigger the thread to end). This is a 
useful scenario, particularly where the object in question is a thread 
pool manager: it is typical for the pool threads to continue running 
until the manager explicitly sets a flag indicating "no more work, stop 
now". If we try and join the threads before the pool destructor has run 
then the waiting thread will block forever as the pool hasn't told the 
threads to shut down yet!

> 1C: Undefined behavior
> (punt). Require that a program must join with all non-main threads,
> including detached threads and heap-based new threads, before the end
> of main. Failing to do so is undefined behavior.

The issue here is how to identify when a program has "joined with" a 
detached thread --- the thread is detached, so it must use a custom 
mechanism. Again, it also rules out creating thread pools from global or 
static objects.

I would prefer 1D (which I believe is already the case): Accessing an 
object for which the destructor has already been called is undefined 
behaviour.

It might be worth adding a note to make this clear in the context of 
detached threads though.

> Incidentally, note also that thread_local objects with nontrivial
> destructors are a novelty. As mentioned in N2880, they are “new in
> C++0x, and not widely implemented.” 

This is incorrect. The POSIX C API has destructor functions for 
thread-specific values, and boost::thread_specific_ptr provides a 
similar mechanism for C++ on many platforms (including Windows).

> (I believe someone else might be writing a
> paper for this same mailing to propose a thread state context for
> thread_local variables that accomplishes some but perhaps not all
> similar effects.) 

That would be me. The paper is N2907, attached for reference.

> 2A: thread_local objects must have trivial destructors.
> This matches existing practice and eliminates the need for novel and
> problematic language destruction ordering rules. It would be
> desirable to also add a non-normative note showing one of the above
> recommended techniques to get a thread-local nontrivial object.

This is a backwards step cf existing practice. Non-trivial destructors 
are also essential for anything that allocates resources (whether memory 
or non-memory resources).

Anthony
-- 
Author of C++ Concurrency in Action | http://www.manning.com/williams
just::thread C++0x thread library   | http://www.stdthread.co.uk
Just Software Solutions Ltd         | http://www.justsoftwaresolutions.co.uk
15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.decadentplace.org.uk/pipermail/cpp-threads/attachments/20090622/ffef5f72/attachment-0001.html>


More information about the cpp-threads mailing list