Topic: proposal for memory_order_seq_cst constraints
Author: Anthony Williams <anthony.ajw@gmail.com>
Date: Wed, 9 Mar 2011 10:22:53 CST Raw View
Masakuni Oishi <yamasa@bsdhouse.org> writes:
> Hello,
>
> Anthony Williams <anthony....@gmail.com> wrote:
>> Masakuni Oishi <yam...@bsdhouse.org> writes:
>> > /*** Case 1 ***/
>> > // Initially
>> > atomic<int> x(0), y(0);
>>
>> > // Thread 1:
>> > x.store(2, memory_order_release); // (1)
>> > x.store(1, memory_order_seq_cst); // (2)
>> > r1 = y.load(memory_order_seq_cst); // (3)
>>
>> > // Thread 2:
>> > y.store(1, memory_order_seq_cst); // (4)
>> > r2 = x.load(memory_order_seq_cst); // (5)
>>
>> > r1 == 0 && r2 == 2 is possible,
>> > in case of (2) -> (3) -> (4) -> (5) in S.
>>
>> This is already disallowed.
>>
>> (1) is sequenced-before (2), so the value written by (2) must be later
>> in the modification order of x than that written by (1). If (3) reads
>> zero then (3) must be before (4) in S, so (2) precedes (5), and (5) must
>> read the value written by (2) or a later value in the modification order
>> of x.
>
> Hmm... N3242 29.3.3 doesn't restrict to "(2) or a later".
> It only says that:
> either the last preceding modification according to this order S,
> or the result of an operation that is not memory_order_seq_cst.
29.3.3 is only one of the constraints. The bit you quote allows
memory_order_seq_cst operations to return values written by
non-memory_order_seq_cst operations (which is important), but that
doesn't mean that *any* value written by a non-memory_order_seq_cst
operation can be read: the other constraints on memory ordering still
apply.
>> > /*** Case 2 ***/
>> > // Initially
>> > atomic<int> x(0), y(0);
>>
>> > // Thread 1:
>> > r1 = x.load(memory_order_seq_cst); // (1)
>> > r2 = y.load(memory_order_seq_cst); // (2)
>>
>> > // Thread 2:
>> > y.store(1, memory_order_seq_cst); // (3)
>> > x.store(1, memory_order_seq_cst); // (4)
>> > r3 = x.load(memory_order_seq_cst); // (5)
>>
>> > // Thread 3:
>> > x.store(2, memory_order_release); // (6)
>>
>> > r1 == 2 && r2 == 0 && r3 == 2 is possible,
>> > in case of (1) -> (2) -> (3) -> (4) -> (5) in S,
>> > and (4) -> (6) in modification order of x.
>>
>> This is already disallowed.
>>
>> if (5) reads 2, then (6) must follow (4) in the modification order of
>> x. If (1) reads 2, then (1) must follow (4) in S, otherwise the reads
>> are inconsistent with the modification order of x.
>
> Why is it "inconsistent"?
> Certainly, if (1) reads 2, then (6) happens before (1).
> But, in N3242, "(4) precedes (6) in the modification order" and
> "(6) happens before (1)" don't mean "(4) should precede (1) in S",
> AFAIK.
It certainly does! You missed the first part of the first sentence of 29.3.3:
"There shall be a single total order S on all memory_order_seq_cst
operations, consistent with the happens before order and modification
orders for all affected locations,"
(6) happens-before (1), so (1) can only read the value written by (6), or
a value that is **later** in the MO of x. If (4) precedes (6) in the MO
of x, then (1) cannot return the value written by (4).
This comes from 1.10p18:
"If a side effect X on an atomic object M happens before a value
computation B of M , then the evaluation B shall take its value from X
or from a side effect Y that follows X in the modification order of M
. [ Note: This requirement is known as write-read coherence. end note
]"
In this case, X is the write (6), and B is the read (1).
>> If (1) follows (4),
>> then S must have (3)->(4)->(1)->(2) => (2) must read 1.
>>
>> If S is (1) -> (2) -> (3) -> (4) -> (5) then (1) precedes (4), so (1)
>> can read 0 (the initial value) or 2 (from (6)), and (2) must read 0 (the
>> initial value of y).
>>
>> If (1) reads 2 then the write at (6) must precede the write at (4) in
>> the modification order of x, since (1) is before (4) in S.
>
> Same as above.
> Do "(6) happens before (1)" and "(1) precedes (4) in S" mean
> "(6) should precede (4) in the modification order"?
Yes!
>> > /*** Case 3 ***/
>> > // Initially
>> > atomic<int> x(0), y(0);
>>
>> > // Thread 1:
>> > x.store(1, memory_order_seq_cst); // (1)
>> > x.store(2, memory_order_release); // (2)
>>
>> > // Thread 2:
>> > r1 = x.load(memory_order_seq_cst); // (3)
>> > r2 = y.load(memory_order_seq_cst); // (4)
>>
>> > // Thread 3:
>> > y.store(1, memory_order_seq_cst); // (5)
>> > r3 = x.load(memory_order_seq_cst); // (6)
>>
>> > r1 == 2 && r2 == 0 && r3 == 1 is possible,
>> > in case of (1) -> (3) -> (4) -> (5) -> (6) in S.
>>
>> This is already disallowed.
>>
>> If S has (1) -> (3) -> (4) -> (5) -> (6), then (6) must read 2, since
>> (2) follows (1) in the modification order of x, and (3) precedes (6) in
>> S.
>
> Why?
(2) follows (1) in the MO of x, since (1) is sequenced-before (2).
If (3) reads 2, then (3) has seen the value written by (2).
If (4) reads 0, then it has NOT seen the value written by (5), so must
precede (5) in S.
(2) is a memory_order_release, so if (3) reads 2 the (2)
synchronizes-with (3), and happens-before (4).
Since (6) follows (4) in S, if (6) reads the result of (1) then this is
inconsistent with the happens-before ordering.
Anthony
--
Author of C++ Concurrency in Action http://www.stdthread.co.uk/book/
just::thread C++0x thread library http://www.stdthread.co.uk
Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk
15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Masakuni Oishi <yamasa@bsdhouse.org>
Date: Thu, 10 Mar 2011 10:23:01 CST Raw View
[ Moderator's note: Over-quoted. Please quote only the parts of a
message that provide the necessary context. -sdc ]
Anthony Williams <anthony....@gmail.com> wrote:
> Masakuni Oishi <yam...@bsdhouse.org> writes:
> > Hello,
>
> > Anthony Williams <anthony....@gmail.com> wrote:
> >> Masakuni Oishi <yam...@bsdhouse.org> writes:
> >> > /*** Case 1 ***/
> >> > // Initially
> >> > atomic<int> x(0), y(0);
>
> >> > // Thread 1:
> >> > x.store(2, memory_order_release); // (1)
> >> > x.store(1, memory_order_seq_cst); // (2)
> >> > r1 = y.load(memory_order_seq_cst); // (3)
>
> >> > // Thread 2:
> >> > y.store(1, memory_order_seq_cst); // (4)
> >> > r2 = x.load(memory_order_seq_cst); // (5)
>
> >> > r1 == 0 && r2 == 2 is possible,
> >> > in case of (2) -> (3) -> (4) -> (5) in S.
>
> >> This is already disallowed.
>
> >> (1) is sequenced-before (2), so the value written by (2) must be later
> >> in the modification order of x than that written by (1). If (3) reads
> >> zero then (3) must be before (4) in S, so (2) precedes (5), and (5) must
> >> read the value written by (2) or a later value in the modification order
> >> of x.
>
> > Hmm... N3242 29.3.3 doesn't restrict to "(2) or a later".
> > It only says that:
> > either the last preceding modification according to this order S,
> > or the result of an operation that is not memory_order_seq_cst.
>
> 29.3.3 is only one of the constraints. The bit you quote allows
> memory_order_seq_cst operations to return values written by
> non-memory_order_seq_cst operations (which is important), but that
> doesn't mean that *any* value written by a non-memory_order_seq_cst
> operation can be read: the other constraints on memory ordering still
> apply.
>
>
>
>
>
>
>
>
>
> >> > /*** Case 2 ***/
> >> > // Initially
> >> > atomic<int> x(0), y(0);
>
> >> > // Thread 1:
> >> > r1 = x.load(memory_order_seq_cst); // (1)
> >> > r2 = y.load(memory_order_seq_cst); // (2)
>
> >> > // Thread 2:
> >> > y.store(1, memory_order_seq_cst); // (3)
> >> > x.store(1, memory_order_seq_cst); // (4)
> >> > r3 = x.load(memory_order_seq_cst); // (5)
>
> >> > // Thread 3:
> >> > x.store(2, memory_order_release); // (6)
>
> >> > r1 == 2 && r2 == 0 && r3 == 2 is possible,
> >> > in case of (1) -> (2) -> (3) -> (4) -> (5) in S,
> >> > and (4) -> (6) in modification order of x.
>
> >> This is already disallowed.
>
> >> if (5) reads 2, then (6) must follow (4) in the modification order of
> >> x. If (1) reads 2, then (1) must follow (4) in S, otherwise the reads
> >> are inconsistent with the modification order of x.
>
> > Why is it "inconsistent"?
> > Certainly, if (1) reads 2, then (6) happens before (1).
> > But, in N3242, "(4) precedes (6) in the modification order" and
> > "(6) happens before (1)" don't mean "(4) should precede (1) in S",
> > AFAIK.
>
> It certainly does! You missed the first part of the first sentence of 29.3.3:
>
> "There shall be a single total order S on all memory_order_seq_cst
> operations, consistent with the happens before order and modification
> orders for all affected locations,"
Well, then I think it should be
"consistent with arbitrary concatenations of the happens-before
order and modification orders for all affected locations".
Anyway, in this sentence, it is quite difficult to understand
what is allowed or not, I think.
For the happens-before order itself, there are 4 coherence
requirements in 1.10 15-18, N3242. Similar to them, I'd like
to describe consistency according to S and happens-before order.
>
> (6) happens-before (1), so (1) can only read the value written by (6), or
> a value that is **later** in the MO of x. If (4) precedes (6) in the MO
> of x, then (1) cannot return the value written by (4).
>
> This comes from 1.10p18:
>
> "If a side effect X on an atomic object M happens before a value
> computation B of M , then the evaluation B shall take its value from X
> or from a side effect Y that follows X in the modification order of M
> . [ Note: This requirement is known as write-read coherence. end note
> ]"
>
> In this case, X is the write (6), and B is the read (1).
>
> >> If (1) follows (4),
> >> then S must have (3)->(4)->(1)->(2) => (2) must read 1.
>
> >> If S is (1) -> (2) -> (3) -> (4) -> (5) then (1) precedes (4), so (1)
> >> can read 0 (the initial value) or 2 (from (6)), and (2) must read 0 (the
> >> initial value of y).
>
> >> If (1) reads 2 then the write at (6) must precede the write at (4) in
> >> the modification order of x, since (1) is before (4) in S.
>
> > Same as above.
> > Do "(6) happens before (1)" and "(1) precedes (4) in S" mean
> > "(6) should precede (4) in the modification order"?
>
> Yes!
>
>
>
>
>
>
>
>
>
> >> > /*** Case 3 ***/
> >> > // Initially
> >> > atomic<int> x(0), y(0);
>
> >> > // Thread 1:
> >> > x.store(1, memory_order_seq_cst); // (1)
> >> > x.store(2, memory_order_release); // (2)
>
> >> > // Thread 2:
> >> > r1 = x.load(memory_order_seq_cst); // (3)
> >> > r2 = y.load(memory_order_seq_cst); // (4)
>
> >> > // Thread 3:
> >> > y.store(1, memory_order_seq_cst); // (5)
> >> > r3 = x.load(memory_order_seq_cst); // (6)
>
> >> > r1 == 2 && r2 == 0 && r3 == 1 is possible,
> >> > in case of (1) -> (3) -> (4) -> (5) -> (6) in S.
>
> >> This is already disallowed.
>
> >> If S has (1) -> (3) -> (4) -> (5) -> (6), then (6) must read 2, since
> >> (2) follows (1) in the modification order of x, and (3) precedes (6) in
> >> S.
>
> > Why?
>
> (2) follows (1) in the MO of x, since (1) is sequenced-before (2).
> If (3) reads 2, then (3) has seen the value written by (2).
> If (4) reads 0, then it has NOT seen the value written by (5), so must
> precede (5) in S.
>
> (2) is a memory_order_release, so if (3) reads 2 the (2)
> synchronizes-with (3), and happens-before (4).
>
> Since (6) follows (4) in S, if (6) reads the result of (1) then this is
> inconsistent with the happens-before ordering.
>
--
masakuni
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Masakuni Oishi <yamasa@bsdhouse.org>
Date: Wed, 2 Mar 2011 16:55:27 CST Raw View
Hello,
I'd like to propose some changes about constraints between
memory_order_seq_cst and other memory orders.
In N3242 29.3.3, constraints of visible values with respect to
a memory_order_seq_cst load is described as below:
> There shall be a single total order S on all memory_order_seq_cst
> operations, consistent with the "happens before" order and
> modification orders for all affected locations, such that each
> memory_order_seq_cst operation that loads a value observes either the
> last preceding modification according to this order S, or the result
> of an operation that is not memory_order_seq_cst.
Under these constraints, the following cases can happen.
/*** Case 1 ***/
// Initially
atomic<int> x(0), y(0);
// Thread 1:
x.store(2, memory_order_release); // (1)
x.store(1, memory_order_seq_cst); // (2)
r1 = y.load(memory_order_seq_cst); // (3)
// Thread 2:
y.store(1, memory_order_seq_cst); // (4)
r2 = x.load(memory_order_seq_cst); // (5)
r1 == 0 && r2 == 2 is possible,
in case of (2) -> (3) -> (4) -> (5) in S.
/*** Case 2 ***/
// Initially
atomic<int> x(0), y(0);
// Thread 1:
r1 = x.load(memory_order_seq_cst); // (1)
r2 = y.load(memory_order_seq_cst); // (2)
// Thread 2:
y.store(1, memory_order_seq_cst); // (3)
x.store(1, memory_order_seq_cst); // (4)
r3 = x.load(memory_order_seq_cst); // (5)
// Thread 3:
x.store(2, memory_order_release); // (6)
r1 == 2 && r2 == 0 && r3 == 2 is possible,
in case of (1) -> (2) -> (3) -> (4) -> (5) in S,
and (4) -> (6) in modification order of x.
/*** Case 3 ***/
// Initially
atomic<int> x(0), y(0);
// Thread 1:
x.store(1, memory_order_seq_cst); // (1)
x.store(2, memory_order_release); // (2)
// Thread 2:
r1 = x.load(memory_order_seq_cst); // (3)
r2 = y.load(memory_order_seq_cst); // (4)
// Thread 3:
y.store(1, memory_order_seq_cst); // (5)
r3 = x.load(memory_order_seq_cst); // (6)
r1 == 2 && r2 == 0 && r3 == 1 is possible,
in case of (1) -> (3) -> (4) -> (5) -> (6) in S.
The above results are allowed in the current specification,
but I think this should not be allowed.
So, I propose new requirements for memory_order_seq_cst,
similar to the four coherence requirements in 1.10.
1. For atomic operations A and B on an atomic object M, where A
modifies M and B takes its value, if A precedes B in S, then the
evaluation B shall take its value from A or from a side effect
that
follows A in the modification order of M. [ write-read coherence
according to S ]
2. For atomic operations A and B on an atomic object M, where A is a
value computation of M and B modifies it, if A precedes B in S,
then A shall take its value from a side effect X on M, where X
precedes B in the modification order of M. [ read-write coherence
according to S ]
3. For value computations A and B on an atomic object M, where A
takes
its value from a side effect X on M, if A precedes B in S, then
the
evaluation B shall take its value from X or from a side effect
that
follows X in the modification order of M. [ read-read coherence
according to S ]
4. For atomic modifications A and B on an atomic object M, if A
precedes B in S, then A shall precede B in the modification order
of M. [ write-write coherence according to S ]
Additionally, I'll define a new relationship S' to treat
memory_order_seq_cst fences in the same way.
There is a relationship S' derived from the total order S and
"happens before" relation. An atomic operation A precedes an atomic
operation B in S' if:
- A precedes B in S, or
- there is a memory_order_seq_cst fence X such that A happens
before
X and X precedes B in S, or
- there is a memory_order_seq_cst fence X such that X happens
before
B and A precedes X in S, or
- there are memory_order_seq_cst fences X and Y such that A
happens
before X, Y happens before B, and X precedes Y in S.
And the above four coherence requirements should be applied to S'.
In the definition of S', I used "happens before" instead of
"is sequenced before". That is because even if S' was derived from
the "sequenced before" relation, it is easy to extend to the
"happens before" relation. For example,
// Thread 1:
x.store(1, memory_order_seq_cst); // (1)
// Thread 2:
atomic_thread_fence(memory_order_seq_cst); // (2)
r1 = x.load(memory_order_relaxed); // (3)
y.store(1, memory_order_release);
// Thread 3:
r2 = y.load(memory_order_acquire);
x.store(2, memory_order_relaxed); // (4)
It assumes (1) precedes (2) in S, and r2 == 1.
Then, because of write-read coherence according to S' (or N3242
29.3.4),
(3) shall take a value from (1) or from a side effect that follows (1)
in the modification order of x.
And also, since (3) happens before (4), (3) shall take a value from a
side effect which precedes (4) in the modification order of x.
Consequently, (1) shall precedes (4) in the modification order of x.
In a same way, the "sequenced before" relation in S' can be extended
to the "happens before" relation with considering in-between
load(memory_order_relaxed) operation.
--
masakuni
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Alexander Terekhov <terekhov@web.de>
Date: Fri, 4 Mar 2011 03:14:31 CST Raw View
The rule of thumb is that you should never combine memory_order_seq_cst
with more relaxed modes of access. If you do not like
memory_order_seq_cst then get rid of memory_order_seq_cst entirely.
regards,
alexander.
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Anthony Williams <anthony.ajw@gmail.com>
Date: Fri, 4 Mar 2011 03:14:47 CST Raw View
Masakuni Oishi <yamasa@bsdhouse.org> writes:
> /*** Case 1 ***/
> // Initially
> atomic<int> x(0), y(0);
>
> // Thread 1:
> x.store(2, memory_order_release); // (1)
> x.store(1, memory_order_seq_cst); // (2)
> r1 = y.load(memory_order_seq_cst); // (3)
>
> // Thread 2:
> y.store(1, memory_order_seq_cst); // (4)
> r2 = x.load(memory_order_seq_cst); // (5)
>
> r1 == 0 && r2 == 2 is possible,
> in case of (2) -> (3) -> (4) -> (5) in S.
This is already disallowed.
(1) is sequenced-before (2), so the value written by (2) must be later
in the modification order of x than that written by (1). If (3) reads
zero then (3) must be before (4) in S, so (2) precedes (5), and (5) must
read the value written by (2) or a later value in the modification order
of x. (5) thus cannot read the value written by (1).
>
> /*** Case 2 ***/
> // Initially
> atomic<int> x(0), y(0);
>
> // Thread 1:
> r1 = x.load(memory_order_seq_cst); // (1)
> r2 = y.load(memory_order_seq_cst); // (2)
>
> // Thread 2:
> y.store(1, memory_order_seq_cst); // (3)
> x.store(1, memory_order_seq_cst); // (4)
> r3 = x.load(memory_order_seq_cst); // (5)
>
> // Thread 3:
> x.store(2, memory_order_release); // (6)
>
> r1 == 2 && r2 == 0 && r3 == 2 is possible,
> in case of (1) -> (2) -> (3) -> (4) -> (5) in S,
> and (4) -> (6) in modification order of x.
This is already disallowed.
if (5) reads 2, then (6) must follow (4) in the modification order of
x. If (1) reads 2, then (1) must follow (4) in S, otherwise the reads
are inconsistent with the modification order of x. If (1) follows (4),
then S must have (3)->(4)->(1)->(2) => (2) must read 1.
If S is (1) -> (2) -> (3) -> (4) -> (5) then (1) precedes (4), so (1)
can read 0 (the initial value) or 2 (from (6)), and (2) must read 0 (the
initial value of y).
If (1) reads 2 then the write at (6) must precede the write at (4) in
the modification order of x, since (1) is before (4) in S. The read at
(5) must therefore read 1.
> /*** Case 3 ***/
> // Initially
> atomic<int> x(0), y(0);
>
> // Thread 1:
> x.store(1, memory_order_seq_cst); // (1)
> x.store(2, memory_order_release); // (2)
>
> // Thread 2:
> r1 = x.load(memory_order_seq_cst); // (3)
> r2 = y.load(memory_order_seq_cst); // (4)
>
> // Thread 3:
> y.store(1, memory_order_seq_cst); // (5)
> r3 = x.load(memory_order_seq_cst); // (6)
>
> r1 == 2 && r2 == 0 && r3 == 1 is possible,
> in case of (1) -> (3) -> (4) -> (5) -> (6) in S.
This is already disallowed.
If S has (1) -> (3) -> (4) -> (5) -> (6), then (6) must read 2, since
(2) follows (1) in the modification order of x, and (3) precedes (6) in
S.
Even though non-memory_order_seq_cst operations are not included in S,
they must still respect the modification-order and happens-before
constraints.
Anthony
--
Author of C++ Concurrency in Action http://www.stdthread.co.uk/book/
just::thread C++0x thread library http://www.stdthread.co.uk
Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk
15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Masakuni Oishi <yamasa@bsdhouse.org>
Date: Wed, 9 Mar 2011 08:02:16 CST Raw View
Hello,
Anthony Williams <anthony....@gmail.com> wrote:
> Masakuni Oishi <yam...@bsdhouse.org> writes:
> > /*** Case 1 ***/
> > // Initially
> > atomic<int> x(0), y(0);
>
> > // Thread 1:
> > x.store(2, memory_order_release); // (1)
> > x.store(1, memory_order_seq_cst); // (2)
> > r1 = y.load(memory_order_seq_cst); // (3)
>
> > // Thread 2:
> > y.store(1, memory_order_seq_cst); // (4)
> > r2 = x.load(memory_order_seq_cst); // (5)
>
> > r1 == 0 && r2 == 2 is possible,
> > in case of (2) -> (3) -> (4) -> (5) in S.
>
> This is already disallowed.
>
> (1) is sequenced-before (2), so the value written by (2) must be later
> in the modification order of x than that written by (1). If (3) reads
> zero then (3) must be before (4) in S, so (2) precedes (5), and (5) must
> read the value written by (2) or a later value in the modification order
> of x.
Hmm... N3242 29.3.3 doesn't restrict to "(2) or a later".
It only says that:
either the last preceding modification according to this order S,
or the result of an operation that is not memory_order_seq_cst.
> > /*** Case 2 ***/
> > // Initially
> > atomic<int> x(0), y(0);
>
> > // Thread 1:
> > r1 = x.load(memory_order_seq_cst); // (1)
> > r2 = y.load(memory_order_seq_cst); // (2)
>
> > // Thread 2:
> > y.store(1, memory_order_seq_cst); // (3)
> > x.store(1, memory_order_seq_cst); // (4)
> > r3 = x.load(memory_order_seq_cst); // (5)
>
> > // Thread 3:
> > x.store(2, memory_order_release); // (6)
>
> > r1 == 2 && r2 == 0 && r3 == 2 is possible,
> > in case of (1) -> (2) -> (3) -> (4) -> (5) in S,
> > and (4) -> (6) in modification order of x.
>
> This is already disallowed.
>
> if (5) reads 2, then (6) must follow (4) in the modification order of
> x. If (1) reads 2, then (1) must follow (4) in S, otherwise the reads
> are inconsistent with the modification order of x.
Why is it "inconsistent"?
Certainly, if (1) reads 2, then (6) happens before (1).
But, in N3242, "(4) precedes (6) in the modification order" and
"(6) happens before (1)" don't mean "(4) should precede (1) in S",
AFAIK.
> If (1) follows (4),
> then S must have (3)->(4)->(1)->(2) => (2) must read 1.
>
> If S is (1) -> (2) -> (3) -> (4) -> (5) then (1) precedes (4), so (1)
> can read 0 (the initial value) or 2 (from (6)), and (2) must read 0 (the
> initial value of y).
>
> If (1) reads 2 then the write at (6) must precede the write at (4) in
> the modification order of x, since (1) is before (4) in S.
Same as above.
Do "(6) happens before (1)" and "(1) precedes (4) in S" mean
"(6) should precede (4) in the modification order"?
> > /*** Case 3 ***/
> > // Initially
> > atomic<int> x(0), y(0);
>
> > // Thread 1:
> > x.store(1, memory_order_seq_cst); // (1)
> > x.store(2, memory_order_release); // (2)
>
> > // Thread 2:
> > r1 = x.load(memory_order_seq_cst); // (3)
> > r2 = y.load(memory_order_seq_cst); // (4)
>
> > // Thread 3:
> > y.store(1, memory_order_seq_cst); // (5)
> > r3 = x.load(memory_order_seq_cst); // (6)
>
> > r1 == 2 && r2 == 0 && r3 == 1 is possible,
> > in case of (1) -> (3) -> (4) -> (5) -> (6) in S.
>
> This is already disallowed.
>
> If S has (1) -> (3) -> (4) -> (5) -> (6), then (6) must read 2, since
> (2) follows (1) in the modification order of x, and (3) precedes (6) in
> S.
Why?
> Even though non-memory_order_seq_cst operations are not included in S,
> they must still respect the modification-order and happens-before
> constraints.
Yes, I agree.
My proposal aims to clarify what is "S is consistent with
the happens-before order and the modification orders".
--
Masakuni
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]