From: John Maddock (John_Maddock_at_[hidden])
Date: 2000-06-06 07:03:09
>Levels 1.5 & 2.5 (?):
Unrelated or related instances of an object can be used in separate
That was an error, level 2 should allow related objects to be used
independently in separate threads, its the "related" issue that separates
levels 1 and 2. By related I mean that they share some common non-global
data (eg reference counting).
>No. The compiler may not remove expressions with side effects, and both
>constructor and destructor have side effects. [3.7.2/3]
You're assuming that they have side effects, if they are declared as
externals then I think that I agree with you.
>No. The compiler may not re-order instructions unless it *really knows*
>what it is doing! As mentioned above "guard"s constructor and destructor
>have side effects. Furthermore, there is a sequence point at the end of
>each full-expression [1.9/16], and at each sequence point, all side
>from previous code are complete, and no future side effects are complete
I think you're getting ahead of yourself here - imagine a simple test and
set lock, the constructor may be out of line (in assembler), but the
destructor would typically just set the "lock" (usually an int) to zero.
The obvious implementation would place the destructor inline, but in this
case there are no side effects - the compiler just sees an inline function
that does "lock.data = 0", since the lock appears to the compiler to be
completely unrelated to the surrounding code (it doesn't know that its
supposed to be a lock after all), it can reorder the instructions as it
>No. We are guaranteed an abstract machine by the Standard, which follows
>"execution sequence" [1.9/3], which implies a strictly sequential
>The compiler *is* free to parallel its instructions, but only under
>rules as seen above -- guaranteeing no extra side effects, removing no
>expected side effects, honoring sequence points, etc.
I'm talking about things beyond the scope of the compiler here - consider
that we protect some floating point code, depending upon the processor this
may be executed in parellel to subsequent integer instructions, behaviour
on a modern PIII will be very different to behaviour on an old 386 with fp
emulation. As modern cpu's become more complex and do more "out of order
execution", then this becomes more of an issue - but only if you are
writing your own locks in assembler.
>Agreed. But I would recommend two methods, "acquire" and "release",
>than just "acquire" with a boolean parameter.
Yep, I'd go with that.
>There's another dimension of considerations. Can the lock be used as a
>"signal"? For example, if thread A owns a mutex, can it wait on that
>until thread B "releases" it? It's been a few years since I've worked on
.UNIX, but I don't think that was an issue. But under Win32, if you do
>a few times, you BSOD. That's why I made a distinction above between a
>"Mutex", which I define as only releasable if you own it, and a "Binary
.Semaphore", which can be used as a "signal".
Yes signals (what win32 calls events and POSIX condition variables) would
be useful as well, maybe I drew the line too tightly.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk