Re: Threading in new C++ standard
On May 1, 2:26 pm, Szabolcs Ferenczi <szabolcs.feren...@gmail.com>
wrote:
On Apr 28, 8:42 pm, "Boehm, Hans" <hans.bo...@hp.com> wrote:
On Apr 21, 10:07 am, Szabolcs Ferenczi <szabolcs.feren...@gmail.com>
wrote:
[...]
If you need low-level atomics, things are very tricky anyway,
I do not need low level atomics because I can write correct concurrent
programs with Conditional Critical Regions. An optimising compiler may
implement some Critical Regions with atomics. However, there are
hackers around who are just crying for atomics and low level means.
And unfortunately for good reason. If your optimizer can generate
something like the Linux RCU implementation, or even double-checked
locking, from code based on critical regions, I'm impressed. And
unfortunately, if we're writing parallel code, it's often because we
need the performance.
We have had a lot of arguments about the need for "low-level"
atomics. But I don't think there's been much question about the need
for some form of atomics to supplement critical regions / locks.
...
Ada95 also has atomic operations, but if I read the spec correctly,
they seem to have largely overlooked the memory ordering issues.
From your point of view it may seem as an overlook but the truth is
that there is no such problem as memory ordering in a well designed
concurrent language. Just look at the comments to your following code
fragments.
In
particular, it I write the equivalent of
Thread 1:
x = 42;
x_init = true;
Thread 2:
while (!x_init);
assert(x == 42);
this can fail (and in fact is incorrect) even if x_init is atomic.
Of course it is incorrect. Here both `x' and `x_init' are shared
variables but you miss to declare them as such. Furthermore, you are
trying to access them `in sequential manner' i.e. as if they were
variables in a sequential program. However, then, why are you
wondering that an incorrect concurrent program can fail?
This is correct in Java and the C++0x working paper if x_init is
volatile(Java)/atomic(C++). The variable x cannot be simultaneously
accessed, hence there is no data race on x. And x_init is effectively
declared shared.
In a C-like environment, it doesn't work to declare x shared in cases
like this. Often x = 42 is really an initialization of some library
that has no idea whether it wil be called from a single thread,
possibly in a single threaded application, or from multiple threads
which serialize access to the library. The library doesn't know
whether its variables are shared; and it doesn't matter.
I guess the meaning of your fragment that you would like Thread 2 to
proceed only when shared variable becomes 42:
shared int x = 0;
Thread 1:
with (x) {x = 42;}
Thread 2:
with (x) when (x == 42) {
// do whatever you want to do when x==42
}
Let me note that if you want Thread 2 to do anything when x==42, you
should specify that action inside the Critical Region. It is because
in a concurrent programming environment the change of the shared
variable must be regarded as a non-deterministic event.
But now you've effectively been forced to move code that cannot
participate in a race into a critical section. As a result you're
holding locks much longer than you need to, reducing concurrency, at
least in a lock-based implementation.
Hans