I'm asking specifically in the memory-model sense. http://en.cppreference.com/w/cpp/atomic/memory_order
I'm asking because I want to know if I can use a std::memory_order_consume
in the below:
mLocalMemPtr1 and 2 and mAtomicMemPtr are pointers into a shared buffer.
In a producer thread I'm doing:
for (int x = 0; x < 10; ++x)
{
++mLocalMemPtr1
*mLocalMemPtr1 = x; // <========= A
mAtomicMemPtr.store(mLocalMemPtr1, std::memory_order_release);
}
And in the consumer:
tempMemPtr = mAtomicMemPtr.load(std::memory_order_consume);
while (tempMemPtr != mLocalMemPtr2)
{
++mLocalMemPtr2;
int test = *mLocalMemPtr2; // <======== B
doSomeLongRunningThing(test);
tempMemPtr = mAtomicMemPtr.load(std::memory_order_consume);
}
So does the dependency chain go tempMemPtr -> mLocalMemPtr2 -> test -> doSomeLongRunningThing?
I'm specifically worried that B
may be executed before A
. I know I can use a std::memory_order_acquire
, but I can use consume (which is more lightweight) if the conditional statement causes a memory order dependency.
C has the following conditional statements: Use if to specify a block of code to be executed, if a specified condition is true. Use else to specify a block of code to be executed, if the same condition is false. Use else if to specify a new condition to test, if the first condition is false.
The syntax of a conditional expression consists of a conditional statement in parentheses. This can be just like the conditionals used in an if-else statement. This is followed by a question mark, a value to be set if the condition is true, a colon, and finally a value to be set if the condition is false.
Such statements are called conditional, and are a form of composite statement. In Java, there are two forms of conditional statements: • the if-else statement, to choose between two alternatives; • the switch statement, to choose between multiple alternatives.
Conditional states work on measures and aggregations only and return numeric values.
CppReference:
Release-Consume ordering
If an atomic store in thread A is tagged std::memory_order_release and an atomic load in thread B from the same variable is tagged std::memory_order_consume, all memory writes (non-atomic and relaxed atomic) that are dependency-ordered-before the atomic store from the point of view of thread A, become visible side-effects in thread B, that is, once the atomic load is completed, thread B is guaranteed to see everything that thread A wrote to memory if it carries a data dependency into the atomic load.
1.10.10:
An evaluation A is dependency-ordered before an evaluation B if
— A performs a release operation on an atomic object M, and, in another thread, B performs a consume operation on M and reads a value written by any side effect in the release sequence headed by A (...)
1.10.9:
An evaluation A carries a dependency to an evaluation B if - the value of A is used as an operand of B, unless:
— B is an invocation of any specialization of std::kill_dependency (29.3), or
— A is the left operand of a built-in logical AND (&&, see 5.14) or logical OR (||, see 5.15) operator, or
— A is the left operand of a conditional (?:, see 5.16) operator, or
— A is the left operand of the built-in comma (,) operator (5.18); (...)
Basing on these facts I say that mLocalMemPtr2
should be synchronized. However there is still a question of the order of evaluation.
if (atomic.load(std::consume) < x)
Which one is evaluated first is unspecified. There is no guarantee (as I couldn't find it in the standard) that the compiler will first perform consume operation, refresh shared buffer and then load atomic
and then x
.
Having not found a proof that operands are evaluated in the "wished" way, I say that without explicit decomposition of the atomic load mLocalMemPtr2
it won't work and CPU might read stale value of memory pointed by mLocalMemPtr2
. memory_order_acquire
would not change much here, as mLocalMemPtr2
carries a data dependency.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With