Re: About as-if rule and "observable behaviors"
{ reformatted to wrap long lines. quoted server banner removed. -mod }
On Tuesday, August 26, 2014 1:40:06 PM UTC+8, Chris Vine wrote:
On Mon, 25 Aug 2014 10:15:23 CST
Eric Z <yingmu1983@googlemail.com> wrote:
On Thursday, August 14, 2014 11:50:02 PM UTC+8, Joshua Cranmer
[snip]
There are two questions related.
1. Data race is where two or more threads have conflicting access
to a shared variable. And the standard says that using std::atomic
type (or other synchronization primitives) would solve the data race.
I wonder if bool variables are intrisically immune to any data race.
AFAIK, the load and the store to a bool type variable is
atomic/indivisble on all platforms. And a bool variable has its own
memory location guaranteed by the language.
So is it possible that a bool variable has any data race?
E.g.,
// global shared
bool value = false;
bool flag = false;
// thread 1
value = 6;
flag = true;
// thread 2
if (flag)
cout << value;
Is this code data race free? If there is no data race, there isn't
any undefined behavior. As a result, as-if rule should be respected.
And a compiler should not reorder the stores to "value" and "flag"
in thread 1 because that may lead to a change of observable behavior
in other threads like thread 2. Is that right?
There is a data race, whether or not the physical representation of
bool in memory happens to be free of shearing on any particular
hardware, which I think is your point.
You seem to labouring under the misapprehension that there is some
single view of memory shared amongst all threads of execution running
within any one given program. This is completely wrong. With caching,
speculative fetching and parallel pipelining of instructions, every
core on a machine can, and in the absence of synchronization
instructions will, have a different view of the state of (that is, the
value represented by) any particular memory location which is written
to or read from in the program.
In a previous post in this thread, which I will not repeat here, I
explained how threads synchronize amongst themselves to ensure that
they have can have a consistent view of memory, by establishing the
necessary "happens before" relationships. This requires both
synchronization instructions for the processor and reordering
restrictions on the compiler. By concentrating on the last of those,
you are missing the main point.
Your code example gives rise to undefined behaviour. Just because
thread 2 happens to see that 'flag' is true has no bearing on whether
it will see 'value' as true. This is true at the hardware level
quite apart from what the compiler may do by way of optimization. (By
the way, why are you passing 6 to a bool?).
2. If there is still any data race in 1, we can solve it by making
both variables atomic:
// global shared
atomic<bool> value = false;
atomic<bool> flag = false;
// thread 1
value.store(6, memory_order_relaxed); // (1)
flag(true, memory_order_relaxed); // (2)
// thread 2
if (flag.load(memory_order_relaxed))
cout << value.load(memory_order_relaxed);
This time it should be guaranteed that there is no data race in
this code. But we know that a compiler is still free to reorder
(1) and (2). Why can it do that?
The as-if rule applies here and the reorder may change some
observable behaviors of other threads (like thread 2). When
undefined behavior is not a problem, how can a compiler still
reorder these instructions? Doesn't it break the "as-if" rule?
This solves nothing. All atomic stores and loads with relaxed memory
ordering will do is prevent shearing. They do nothing to ensure
visibility. In short, relaxed atomics do not synchronize. So you still
have a data race and undefined behaviour, just as you did with your
earlier code.
Thanks.
But I guess I have a fundamental disagreement with you;) I think you
misunderstand data race vs race condition. A data race is NOT the same
to a race condition. What I'm asking is data race and what you respond
is with respect to race condition.
A data race has something to do with memory tearing where a torn write
can be seen by a read, e.g. By the language, it has UB.
A race condition can happen even if there is no data race. E.g., if you
add an entry to a linked list w/o any synchronization protection, it will
raise race condition such that the list may be malformed.
BUT, the language doesn't say anything about race condition, let alone any
possible UB of race condition. Specifically, it says that,
""Relaxed" atomic operations are not synchronization operations even though,
like synchronization operations, they cannot contribute to data races."
which definitely conflicts with your point:
"In short, relaxed atomics do not synchronize. So you still have a data race
and undefined behaviour."
Best regards,
Eric
Your concurrent posts here and on stack overflow about this same issue
shows that you have a fundamental misunderstanding about it. Can I
suggest that you reread all the earlier posts in this thread, because
the point has been explained a number of times, and it doesn't seem to
be going anywhere.
Chris
--
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]