Reputation: 35
Is there any case where we'd want to load an atomic with memory_order_acquire
without a corresponding store to the same atomic with memory_order_release
?
For example, if I have this piece of code:
std::atomic<uint64_t> state;
std::atomic<uint64_t> count;
// Thread 1
while (true)
{
auto expected = state.load(memory_order_relaxed);
auto desired = get_desired(expected);
if (state.compare_exchange_weak(expected, desired, memory_order_relaxed, memory_order_relaxed) { <----- (A)
count.fetch_add(1, memory_order_relaxed); <---- (B)
break;
}
}
// Thread 2
auto state = state.load(memory_order_acquire); <----- (C)
auto count = count.load(memory_order_relaxed); <----- (D)
The memory_order_acquire
in Thread 2 should prevent the load of count
from being moved before the load of state
, but since we use memory_order_relaxed
everywhere else, am I correct in saying that Thread 2 may see an updated value of count
that's possibly newer than state
i.e. see the effects of (B) before it sees the effects of the compare_exchange
from (A)?
If I wanted the effect of (A) to always be visible to Thread 2 when it loads it in (C), then would I change (B) to memory_order_release
, use memory_order_acquire
in (D) and flip the order of (C) and (D)?
Upvotes: 1
Views: 317
Reputation: 119044
The
memory_order_acquire
in Thread 2 should prevent the load ofcount
from being moved before the load ofstate
No. Atomic memory ordering in C++ is not defined in terms of preventing certain operations from being reordered with respect to each other. Instead, it is defined in terms of requiring certain side effects to be visible at certain points. In particular,
If you only have acquire-loads but no release-stores, then there are no such ordering guarantees.
am I correct in saying that Thread 2 may [...] see the effects of (B) before it sees the effects of the
compare_exchange
from (A)?
Yes.
If I wanted the effect of (A) to always be visible to Thread 2 when it loads it in (C), then would I change (B) to
memory_order_release
, usememory_order_acquire
in (D) and flip the order of (C) and (D)?
Yes. Assuming I've understood you correctly, after you've performed the sequence of transformations described, we will have
state
through compare_exchange_weak
followed by a release store to count
through fetch_add
count
followed by a relaxed load from state
Assuming the acquire-load from count
reads the value stored by thread 1, the relaxed load from state
in thread 2 must observe the value that was stored by thread 1, or a later value.
Upvotes: 2