[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