The code below shows two ways of acquiring shared state via an atomic flag. The reader thread calls poll1()
or poll2()
to check for whether the writer has signaled the flag.
Poll Option #1:
bool poll1() {
return (flag.load(std::memory_order_acquire) == 1);
}
Poll Option #2:
bool poll2() {
int snapshot = flag.load(std::memory_order_relaxed);
if (snapshot == 1) {
std::atomic_thread_fence(std::memory_order_acquire);
return true;
}
return false;
}
Note that option #1 was presented in an earlier question, and option #2 is similar to example code at cppreference.com.
Assuming that the reader agrees to only examine the shared state if the poll
function returns true
, are the two poll
functions both correct and equivalent?
Does option #2 have a standard name?
What are the benefits and drawbacks of each option?
Is option #2 likely to be more efficient in practice? Is it possible for it to be less efficient?
Here is a full working example:
#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>
int x; // regular variable, could be a complex data structure
std::atomic<int> flag { 0 };
void writer_thread() {
x = 42;
// release value x to reader thread
flag.store(1, std::memory_order_release);
}
bool poll1() {
return (flag.load(std::memory_order_acquire) == 1);
}
bool poll2() {
int snapshot = flag.load(std::memory_order_relaxed);
if (snapshot == 1) {
std::atomic_thread_fence(std::memory_order_acquire);
return true;
}
return false;
}
int main() {
x = 0;
std::thread t(writer_thread);
// "reader thread" ...
// sleep-wait is just for the test.
// production code calls poll() at specific points
while (!poll2()) // poll1() or poll2() here
std::this_thread::sleep_for(std::chrono::milliseconds(50));
std::cout << x << std::endl;
t.join();
}