Is in the following code x_value guaranteed to be 42?
std::atomic<int> x;
Thread A:
x.store(42, std::memory_order_seq_cst);
wakeThreadB();
Thread B:
int x_value = x.load(std::memory_order_relaxed);
assert(x_value == 42);
I have tried to test it, and it seemed that thread B always reads correct value. But I'm not sure if it is guaranteed.
A relaxed load does not synchronize with any other load/store before or after it.
Note also that memory order semantics are about the visibility of other work done in relation to the syncronization variable.
So e.g. the following wouldn't be correct:
std::atomic<int> x;
int y;
void func_A()
{
y = 1;
x.store(42, std::memory_order_seq_cst); // "releases" y = 1
}
void func_B()
{
while (x.load(std::memory_order_relaxed) != 42) {} // does NOT "acquire" anything
assert(y == 1); // not guaranteed
}
int main()
{
std::thread a(&func_A), b(&func_B);
a.join();
b.join();
}
Mandatory note here: "it always works on my machine" doesn't make it correct; without synchronization it's a data race, a form of undefined behavior.
But in your specific case, if by wakeThreadB()
you mean construction of std::thread
instance with Thread B code as the thread function, then the code is actually correct - std::thread
creation is a syncronization event (see [thread.thread.constr]/5), so any load in Thread B is guaranteed to see everything done before Thread B was started.
That means that the atomic store of x
does not matter at all, the code would be correct even with a non-atomic int
:
void func_B();
int x;
std::thread t;
void func_A()
{
x = 42;
t = std::thread(&func_B); // "releases" x = 42
}
void func_B() // "acquires" x = 42
{
assert(x == 42); // guaranteed success
}
int main()
{
func_A();
t.join();
}
Similarly, std::condition_variable
uses a mutex internally and a mutex release/lock is a synchronization event (see [thread.mutex.requirements.mutex]/25), so notifying another thread via a condition_variable
would also work correctly without the need for any atomics.