Reputation: 1416
Supposing I have an unsynchronized atomic flag is_finished
. In the following code:
// thread A
while(!is_finished.test(memory_order_relaxed)) {
// supposing I have some observable side effects here
}
// thread B
is_finished.test_and_set(memory_order_relaxed);
If these two threads run concurrently, will thread A always end?
Upvotes: 0
Views: 131
Reputation: 1156
Because you use memory_order_relaxed
mode, you get nothing but the guaranty of the atomic read/write operations themself:
Relaxed operation: there are no synchronization or ordering constraints imposed on other reads or writes, only this operation's atomicity is guaranteed...
Source: https://cppreference.com/
That means you never know when test
and test_and_set
methods are going to be executed. Compiler and CPU are free to reorder the instructions.
In order to get the desired effect, you should either use memory_order_release
/memory_order_acquire
semantics on atomics or use std::atomic_thread_fence
Option 1:
// thread A
while(!is_finished.test(memory_order_acquire)) {
// supposing I have some observable side effects here
}
// thread B
is_finished.test_and_set(memory_order_acq_rel);
Option 2:
// thread
while(!is_finished.test(memory_order_relaxed)) {
std::atomic_thread_fence(memory_order_acquire);
// supposing I have some observable side effects here
}
// thread B
std::atomic_thread_fence(memory_order_release);
is_finished.test_and_set(memory_order_relaxed);
Please note, you are asking about the single atomic variable. I assume you have more variables that are read/modified based on the value of the atomic variable. You must take care of the ordering all together. Check the Fences are Memory Barriers article for more details.
Upvotes: 1
Reputation: 119467
The C++ standard does not guarantee that thread A will see the side effects caused by thread B within any particular amount of time. However, on all real hardware, thread A should see the side effects caused by thread B without undue delay. The standard does say that an implementation "should" ensure that thread A will see the side effect "in a finite period of time", which doesn't actually mean anything, since all time is finite in the real world.
Whether or not you use relaxed memory order makes no difference. The point is that thread A is not going to wake up right away: it will be scheduled whenever the operating system feels like scheduling it. Even if thread A is explicitly waiting on the atomic flag (using the wait
member) and thread B notifies all waiters (using the notify
member), you still don't know that the operating system is going to wake up thread A right away. A higher-priority task could intervene.
This is why all guarantees provided by the concept of synchronizes with in the standard are conditional: if thread A sees a side effect caused by thread B and the two operations synchronize, then subsequent operations in thread A are guaranteed to also see previous side effects caused by thread B.
Upvotes: 2