Re: C++ Threads, what's the status quo?
James Kanze wrote:
Le Chaud Lapin wrote:
Mirek Fidler wrote:
Ahh... I guess you should seriously reconsider the issue.
Recommended reading is e.g. google: "why double checked guard does not
work".
Just for starters, if you have
Shared y;
Mutex y_lock;
void fn() {
y_lock.Lock();
y = 10;
y_lock.Unlock();
}
nothing in C++ standard prevents C++ compiler to generate machine code
equivalent to
void fn() {
y = 10;
y_lock.Lock();
y_lock.Unlock();
}
(and now I recommend you to get some C++ reeducation before your
threads start to fail with new compiler version ;)
First, I need to be really clear here. :) I did *NOT* write the code
above. You did. :D This could easily turn into a situation where people
start arguing that "my code does not work as expected." That is not my
code.
That's clear. But the question remains, what do you do to
guarantee that the compiler doesn't do something like this
behind your back?
I use real locking. The above, if it is using DCL is not "real"
locking. This is why I so emphatically disclaimed that code. I did
that precisely to keep this from turning into a "But what do you do
about situation X.." conversation.
I have been saying all along. We should stop doing X and hoping that
things will work out magically. To do mutual exclusion, you need
low-level support. This is perhaps the 20th time I have said this in
the past 5 days. The above is not real synchronization. The above is
an attempt to gain a performance boost by avoiding real synchronization
"just for a little while."
I don't think anyone was accusing you of using double checked
locking. Much discussion about what can and cannot happen,
however, has centered around this problem.
Not my foot. I am glad that the truth is coming out - that the problem
is not so much synchronization, but that programmers are attempting to
play tricks with code without respect for the necessity of true
synchronization.
Using volatile doesn't change anything. And the problem
normally isn't that the compiler won't do the second read; if
the compiler is Posix conform, it knows that it will have to
resync its images of memory after a pthread_mutex_lock. (Of
course, since Posix doesn't define a C++ binding, strictly
speaking, there's no such thing as a Posix conform C++
compiler.) The immediate problem is that the compiler might
reorder the writes in the constructor with regards to the write
of the pointer. And making the pointer volatile doesn't change
that (except with the extension that Microsoft has proposed).
Again, I do not mind discussing synchronization, but I feel like I am
being drawn into a conversation to discuss that which I do not
recognized now. When I see that code above, and I know that there is
some DCL attempt in it that is not based on hardware-support, as far as
I am concerned, the conversation is over.
Even if one adds additional synchronization to guarantee the
order of the writes, it is still possible for the hardware to
reorder the reads in the branch which doesn't do any
synchronization; i.e. to read a non-null value for the pointer,
and then read older, uninitialized values in the object itself.
(I don't think the current Intel 32 bit architecture allows
this. Other architectures, including the IA-64 architecture and
Sparc, do.)
Again, you are talking about order of writes, and I am talking about
spin-locks and mutexes. Different conversation.
But of course, you count on the guarantee that the compiler and
the standard library doesn't do anything similar. From where do
you get that guarantee.
You tell the library writes to do their part. As I stated before, if
it is true that the standard library breaks in a multi-threaded
application, and the standard library was meant to be used in a
multi-threaded application, then that is the standard library authors'
fault. If it is true that the standard library breaks in a
multi-threaded application, and the standard library was not meant to
run in a multi-threaded application, then that is the programmer's
fault.
If the standard library writer makes a library that depends on the
hope-this-works kind of DCL, that is the library writer's fault.
And if it is also true that sequence points are honored by the
C++ Standard after a semi-colon, then my multi-threading
framework still works, with the optimizer enabled, and is
portable.
Sequence points only define order between "observable behavior",
i.e. IO and accesses to volatile variables. A compiler can do
anything it wishes, as long as the observable behavior is
respected. And the observable behavior is not defined for
multi-threading.
That is why it is so important to use synchronization.
-Le Chaud Lapin-
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]