Boost logo

Boost :

From: William Kempf (williamkempf_at_[hidden])
Date: 2001-08-30 14:16:31


From: "Peter Dimov" <pdimov_at_[hidden]>
>From: "William Kempf" <williamkempf_at_[hidden]>
>>From: "Peter Dimov" <pdimov_at_[hidden]>
>> >Why the stricter requirement of undefined behavior for subsequent joins?
>> >Why
>> >not simply 'no effect'?
>>
>>From a very low level point of view it's for the same reason that calling
>>"delete" on the same pointer multiple times results in undefined behavior.
>
>You need to remember whether 'join' was called anyway (for the destructor.)
>So you can easily avoid the undefined behavior.

Only if you retain the strict rules about adoption. I realize that you want
to make adoption fully undefined behavior, but users (myself included) will
rightfully expect (want, desire, pick your term) adoption to work. With the
strict semantics adoption *DOES* work, with the only consequences in
behavior having already been documented for the behavior of non-adopted
threads.

>> >Is boost::thread thread safe? IOW are calls to boost::thread::join
>> >serialized?
>>
>>Since it's a programmatic error (undefined behavior) to call join()
multiple
>>times there's no need for serialization or concerns over thread safety.
>
>This is a general question, it's not limited to 'join.' In the future there
>will be many things that you can do to a thread object - cancel,
>set_cancel_type, set_priority, etc. Are these operations thread-safe?

It's not really a general question. Obviously all operations on the thread
object must be thread safe (and they will be). The only one that can be
questionable is "join", but this operation is thread safe simply because
it's undefined behavior to call it more than once.

>> >case 3: access serialized, subsequent joins have no effect. Well defined
>> >behavior. Both threads join x.]
>>
>>Actually, only one thread "joins x", by very definition of "join" ;).
>
>The definition of join is:
>
>Effects: The current thread of execution blocks until the initial function
>of the thread of execution represented by *this finishes and all resources
>are reclaimed.
>
>Postcondition: *this is non-joinable.
>
>Both threads join x, by this definition. You can't detect from a conforming
>program how many threads joined x simultaneously. (Ignoring the
>non-joinable
>vs adopted-non-joinable issue for now.)

OK, we need to make the Boost.Threads documentation more clear here. The
term "join" differs from "wait" in that a "join" actually reclaims the
thread resources mentioned in the above definition. Only some of the thread
resources are reclaimed when the thread ends (implementation defined), the
rest are reclaimed only when "join" is called or the thread is "detached" by
the destructor (and the thread has ended). You can't "join" twice because
this act reclaims the thread resources and you can reclaim the resources
only once.

As for the assertion that "you can't detect from a conforming program how
many threads joined x simultaneously", this is simply not true, since a
conforming program CAN'T call "join" more than once, no matter what
thread(s) it's called from.

>>That
>>aside, this is an alternative design, but one that complicates thread
>>adoption, and so was not the design I went with. Beyond the issue of
thread
>>adoption, this alternate design also adds overhead (possibly significant
>>overhead), so the principle of only paying for what you use also applies.
>
>I don't see why this complicates thread adoption; it's a separate issue.
>Joining an adopted thread will invoke undefined behavior, as usual.

In a conforming program you can not tell if a thread is "adopted" or not
when you call "current" (or in our case the default constructor). It will
never make sense for someone to call "join" twice on the *same* thread
object, so being able to call "join" multiple times is only useful if you
can call "join" on other thread objects that refer to the same thread, but
since you can't distinguish an adopted thread from a non-adopted thread you
can't do this (safely) any way.

>As for the overhead... what overhead? Join blocks for an indeterminate
>amount of time anyway; and the second join is undefined behavior by the
>current specification so there's no added overhead here either.

If a second "join" is undefined behavior, like it is in the submission, then
there is no overhead. You were arguing for a second "join" to be, in
effect, a no-op instead of undefined behavior, and this would add overhead
since you'll have to synchronize access to some state that indicates whether
the thread has been joined already or not. This means additional state data
(memory/resource overhead) as well as making the (indeterminate) blocking
time even longer for the initial call to "join".

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