Re: C++ Threads, what's the status quo?
Le Chaud Lapin wrote:
[...]
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 fully understand what YOU do. What I'm asking is how you know
what the compiler and the library does? How do you know that
the compiler doesn't use something like this in its
implementation of exceptions, for example?
I have been saying all along. We should stop doing X and hoping that
things will work out magically.
And I keep repeating myself: X, in this case, is using the
compiler. I'm not willing to stop using the compiler, and I'm
not willing to write all my code in assembler. But this means
that I need some assurance concerning what the compiler does.
To do mutual exclusion, you need
low-level support. This is perhaps the 20th time I have said this in
the past 5 days.
Yes. You seem to prefer repeating it, rather that addressing
the problems. You seem to recognize that synchronization is
necessary, but accept the fact that you don't know whether the
compiler and the library do it or not, or whether they define
exactly when it is necessary, and when not. And this, despite
repeated examples of cases where compilers have defined their
guarantees differently.
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.
You mean, that the programmer is using a compiler, and doesn't
have any real guarantees with regards to what it does.
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.
Except if one of the writes migrates across a spin-lock.
(Spin-locks are only guaranteed if special hardware instructions
are used on a Sparc---or a PC. Hardware instructions that are
never generated by Sun CC or by g++---or by the current version
of VC++.)
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.
First you have to define what that part is. Suppose that the
library writer says that you need to manually acquire a lock
before calling malloc (or operator new)?
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.
What does "meant to be used in a multi-threaded application"
mean? For, say, tmpnam, or localtime? Or operator new, or
std::allocator?
If the library implementation doesn't fulfil the contract, then
it is the library implementors fault. But for that to be the
case, you first need the contract.
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.
Agreed. But the issues are a lot more complicated than you seem
to think. I know, from experience.
--
James Kanze (Gabi Software) email: james.kanze@gmail.com
Conseils en informatique orient?e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S?mard, 78210 St.-Cyr-l'?cole, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]