Boost logo

Boost :

From: William Kempf (williamkempf_at_[hidden])
Date: 2001-08-24 17:07:25


From: "Peter Dimov" <pdimov_at_[hidden]>
>From: "William Kempf" <williamkempf_at_[hidden]>
>> >For the Boost.Threads-based implementation, yes, adopting threads would
>>be
>> >problematic. A possible issue that I can see is "double adoption."
>>
>>The issue isn't double adoption (in fact, that's trivial to prevent). The
>>issue is that you don't know if an adopted thread is joinable or not. You
>>can't implement "join" semantics because you don't know if you can join
>>the
>>thread or not (in fact, you are very unlikely to be able to). This is an
>>issue for both thread_ref as well as thread designs.

>Could you please explain why?

Ignoring Boost.Threads and focusing on POSIX threads. The following
produces undefined behavior.

pthread_t thrdcrtd;
pthread_t thrdadopted;
// Code to create a thread assigned to thrdcrtd.
// The created thread "adopts" itself via a call to pthread_self()
// using thrdadopted, and we synchronize to insure no race.
pthread_join(thrdcrtd);
pthread_join(thrdadopted);

As soon as either pthread_join() or pthread_detach() are called on any
pthread_t instance all other instances "referencing" the thread of execution
become invalid targets for further calls to pthread_join() and
pthread_detach(). This is the same as with pointers and memory management,
with the exception that the object referenced must also "complete" for the
thread ids to be reclaimed.

void* p1 = malloc(1000);
void* p2 = p1;
free(p1);
*p2; // undefined behavior

In most designs the thread that calls pthread_create() is also going to call
pthread_join() or pthread_detach() (or even create the thread in a detached
state to begin with). Because of this, in a boost::thread_ref (or
boost::thread) implementation based on POSIX threads you must assume that
adopted threads can not be "joined" or "detached" since the creating thread
(outside of the library, and probably outside of the application) will call
one of these. So the best you can do is implement "wait" semantics instead
by using a monitor.

The precise difference between "wait" semantics and "join" semantics is that
with "wait" you only know that the thread routine has returned. You do not
know if cleanup stacks have been called, if thread specific storage has been
reclaimed, or if the thread id itself has been recycled. If "wait"
semantics are good enough for you then you don't have an issue with the
proposed Boost.Thread implementation since you can create a
boost::thread_ref with this sort of semantics easily enough. If you need
actual "join" semantics then I frankly don't know how to address thread
adoption at all, either on top of Boost.Threads or on top of POSIX.

One problem with "wait" semantics is that if you hold onto a thread_ref long
enough it's theoretically possible for an implementation to have reclaimed
the thread id and reused it, so calls using this thread_ref now operate on a
different thread of execution. With "join" semantics the creating thread
will always either "join" or "detach" the thread insuring that it doesn't
ever reference a reused thread id. The thread objects created with the
default constructor should also not be prone to this problem as long as we
keep the restriction that they are valid only within the current thread.
POSIX doesn't have this restriction, but in practice this is how the thread
ids are used. Any other usage requires careful management of the thread id
copies.

Another problem is that it's possible for some cleanup to not be done
properly on threads which are not "joined" when the process ends. This is a
race condition.

Have I made the issue clear enough now?

Bill Kempf

_________________________________________________________________
Get your FREE download of MSN Explorer at http://explorer.msn.com/intl.asp


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk