[cpp-threads] [Javamemorymodel-discussion] there's a happens-before orderhere. right?
Anthony Williams
anthony at justsoftwaresolutions.co.uk
Mon Dec 15 13:58:25 GMT 2008
At Mon 15 Dec 2008 13:32:56 UTC, Alexander Terekhov
<alexander.terekhov at gmail.com> wrote:
> On Mon, Dec 15, 2008 at 12:47 PM, Anthony Williams
> <anthony at justsoftwaresolutions.co.uk> wrote:
>> At Sat 13 Dec 2008 14:42:43 UTC, Alexander Terekhov
>> <alexander.terekhov at gmail.com> wrote:
>>> Now let's introduce a store to std::atomic<> x in thread 2.
>>>
>>> int data; //= 0
>>> atomic<int> x; //= 0
>>>
>>> thread 1:
>>> ------------
>>> if (x.load(relaxed) > 0)
>>> data = 1;
>>>
>>> thread 2:
>>> ------------
>>> data = 2;
>>> x.store(-1, release);
>>>
>>> I just can't conceive how that could possibly introduce a race on
>>> "data"...
>>
>> Agreed. The load may read 0 (initial value) or -1 (stored value), but in
>> neither case is the branch followed, so there is no race.
>
> IOW you agree that it shall not be allowed to "speculate" conditional
> store (e.g. by (mis)predicting positive value for load) such that
> conditional store could happen before thread 1 has actually observed
> positive value in x. But that means that conditional store "happens
> after" load, which is to say load "happens before" conditional
> store!!!
Yes, the load happens-before the store. That's required by the C++0x
memory model, since the load is sequenced-before the store in the same
thread.
>>
>>> The next step is to change x.store(-1, release) to x.store(+1,
>>> release) :
>>>
>>> int data; //= 0
>>> atomic<int> x; //= 0
>>>
>>> thread 1:
>>> ------------
>>> if (x.load(relaxed) > 0)
>>> data = 1;
>>>
>>> thread 2:
>>> ------------
>>> data = 2;
>>> x.store(+1, release);
>>>
>>> This just ought to be data-race-free as well!!!
>>
>> I don't see why. The load is relaxed, so it is unordered. If it reads "+1",
>> there is still no happens-before relationship between the two stores to
>> data, so we have a race.
>
> See above.
These cases are different. In the first case, the load could read "0"
or "-1", neither of which would cause "data" to be written to. In the
second, the load could read "0" or "+1", and a read of "+1" *would*
cause "data" to be written to.
The load is "relaxed", so even though the load from x happens-before
the store to data in thread 1, and the store to data in thread 2
happens-before the store to x, we *don't* have the necessary
transitive relationships to order the two stores to data. In
particular, thread 1 might see the effects of the store to x (i.e.
x.load returns "+1") *before* it sees the effects of the store to data
from thread 2.
Under the C++0x memory model, release operations are not global. They
are one half of a pairwise ordering between threads - the other half
is an acquire operation that sees the value written (modulo any
release sequences). If you don't have both halves of the pairing you
haven't got any synchronization. On any particular architecture this
may require the use of a global synchronization instruction for either
the store or the load (or both), but this is not required by the
memory model, and cannot be relied on.
Anthony
--
Anthony Williams
Custom Software Development | http://www.justsoftwaresolutions.co.uk
Just Software Solutions Ltd, Registered in England, Company Number 5478976.
Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK
More information about the cpp-threads
mailing list