Boost logo

Boost :

From: William Kempf (williamkempf_at_[hidden])
Date: 2001-08-31 09:42:15


>From: "William Kempf" <williamkempf_at_[hidden]>
>>The "runtime" is a part of the abstract machine.
>
>I think that you use a definition of the term "abstract machine" that I'm
>not familiar with. The runtime library is not part of the abstract machine.
>The abstract machine is an abstract model of a CPU; it executes the runtime
>library (and any other code.)

Well, correct me if I'm wrong, because I'm no expert on this subject, but
bootstrapping the program is a part of the abstract machine, no? In any
event, these details are outside of the scope of user or library code,
dependent on details not standardized.

>> >Any dynamically initialized object with static storage duration will
>>have
>> >its initialization expression/constructor executed by the initial
>>thread,
>> >AFAICS. So the initial thread is very much detectable and 'special',
>> >without
>> >any modifications to the abstract machine.
>>
>>Ahh... but here's the rub. You can't statically initialize a thread
>>identifier. In pthreads you have to call pthread_self() and in Win32 you
>>have to call GetCurrentThread() in order to get the "current" thread
>>identifier, and the minute you do this you're no longer in static storage
>>duration. When such data is initialized follows specific rules (which I
>>know you're familiar with) and these rules do not gaurantee that they'll
>>be
>>initialized by the "initial thread", and in fact under both Win32 and
POSIX
>>you have no gaurantee what thread will initialize such data.
>
>Static storage duration is not the same as static initialization, and I
>specifically said "dynamically initialized object with static storage
>duration" above.

Yes, but the quote continued to say "will have its initialization
expression/constructor executed by the initial thread", which isn't
gauranteed, and so I wasn't sure you were using the terminology correctly.

>You are correct that there is no guarantee that the dynamic intialization
>will occur in the context of the initial thread, but the initialization
>rules do imply a lot. The only scenario where dynamic initialization will
>occur in the context of another thread (that is relevant to our problem) is
>where
>
>* dynamically initialized static object A creates a thread;
>* the thread accesses an object B in a dynamically loaded library;
>* the translation unit that contains B contains a static, dynamically
initialized object C.
>
>C is now initialized -- most probably (nothing is guaranteed) -- within the
>context of the other thread.
>
>This issue will be fun to standardize. ;-)

I don't think that "dynamically loaded libraries" have anything to do with
this. AFAIK, the standard doesn't even mention such things, nor am I aware
of the Win32 documentation or POSIX standard mentioning this. The relevant
part of the standard seems to be 3.6.2/3, which when logically extended to
include multiple threads of execution means that any dynamically initialized
object with static storage duration may be initialized in any thread. This
may be a corner case, but it's a very real one that causes very serious
consequences in this context.

>Give me an implementation that ignores this issue and I'll be happy with
>it.
>It'll work in 98% of the cases.

I don't follow you here. The issue only exists if you're attempting to
treat the "initial thread" as something special, which the current design
does not.

>>But, again, I don't see this as much of a loss. I strongly feel that it's
>>a
>>design error to do this, and if you absolutely must do this you can
simulate
>>it in user code any way.
>
>I'm writing a thread_ref library. I don't have access to user code. ;-)

You don't need access. You simply make "joining" an adopted thread (the
"initial thread" is an adopted thread) undefined behavior. Now user code
can't do this, but user code that relies on a design where the "main thread"
is "joined" can easily employ the "thread_main" simulation.

In fact, for your thread_ref case you *can* go a bit further. As I've
suggested, for "adopted" threads you can employ "wait" semantics instead of
"join" semantics. I don't think there will be any real issues with
differences in semantics here.

>>Why does a thread_ref wrapper need to "join" main?
>
>Why not? Orthogonality. All threads are equal (adopted threads don't
> >count.)

They don't? Then give me a fully equal adopted thread ;). I managed to do
this but only by restricting what one can do with any thread object created
through the default constructor. We've danced around all of this for quite
some time now, but no alternative solution has been given here, and an
alternative is needed for the "initial thread" because it *IS* an adopted
thread.

>> >Given the quality of your current implementation, I very much believe
>>that
>> >you can overcome the technical problems. ;-)
>>
>>How. Seriously, show me the error of my ways and I'll gladly change the
>>design here in many ways. If you can safely and completely adopt main,
>>including the ability to "join" it, then I should be able to do the same
>>with EVERY adopted thread and we can open up the design constraints a LOT.
>
>No, I don't think that you can do the same with every adopted thread.
>That's
>why I don't ask for it.

I don't believe that this is true. If you can do it for the "initial
thread" you should be able to do it for any "adopted" thread. But let's
start with just a solution for the "initial thread". Show me how you can do
this.

>> >Besides, I don't ask for an implementation that works all the time. I
>>ask
>> >for a specification that calls for an implementation that works.
>>
>>I prefer an implementation that leaves as few holes as possible. I guess
>>you and I have different design goals in this regard.
>
>I don't think so. Our main differences are that
>
>(1) I think that the current thread design is neither low-level nor
>high-level, meaning that it doesn't let me say
>
>thread t(adopt_main_thread_please_I_take_the_responsibility); // post: t is
>joinable

Well, I can't recognize the main thread to do this ;). However, I guess an
explicit request to "fully adopt" a thread would work and could be employed
on the "initial thread". It would be, however, highly dangerous and
wouldn't serve much actual utility. I don't see thread_ref as being able to
use this internally, because it won't be able to distinguish the "initial
thread" from any other "adopted" thread either. Only the user code is going
to be able to do this.

>while at the same time it has more undefined behavior than I'd like.
>
>(2) While I understand your position on the issues where technical reasons
>with current thread libraries call for an interface that could have been
>different were you designing a C++ standard library from the scratch, I
>think that you can afford to move the line a bit. An 'approximation' to the
>ideal implementation that works in 95% of the cases is good enough, as long
>as it's possible to have a 100% implementation with the appropriate support
>from the runtime.

I definately don't agree with this. It makes usage of the library
impossible, since you can't determine when "undefined behavior" occurs (even
knowing that the 5% occurs when threads are adopted doesn't help the user
since he doesn't know when a thread is adopted). With the stricter
definitions we get "undefined behavior" with Boost.Threads, but a
theoretical library based on Boost.Threads that's added to the language
standard can define the behavior and not break any existing code. Undefined
behavior is only bad when it prevents efficient/effective use, and I don't
think the current design does this.

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