Reputation: 410
I have a couple of questions regarding relaxed atomics in x86 architecture:
#include <iostream>
#include <thread>
uint32_t shared_var = 0;
void reader() {
while (shared_var != 42) {
std::cout << "Reader thread: " << shared_var << std::endl;
}
}
void writer() {
shared_var = 42;
std::cout << "Writer thread: " << shared_var << std::endl;
}
int main() {
std::thread t1(reader);
std::thread t2(writer);
t1.join();
t2.join();
return 0;
}
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<uint32_t> shared_var = 0;
void reader() {
while (shared_var.load(std::memory_order_relaxed) != 42) {
std::cout << "Reader thread: " << shared_var << std::endl;
}
}
void writer() {
shared_var.store(42, std::memory_order_relaxed);
std::cout << "Writer thread: " << shared_var << std::endl;
}
int main() {
std::thread t1(reader);
std::thread t2(writer);
t1.join();
t2.join();
return 0;
}
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<uint32_t> shared_var = 0;
void reader() {
std::cout << "Reader thread: " << shared_var.load(std::memory_order_acquire) << std::endl;
}
void writer() {
shared_var.store(42, std::memory_order_relaxed);
std::cout << "Writer thread: " << shared_var << std::endl;
}
int main() {
std::thread t1(writer);
std::thread t2(reader);
t1.join();
t2.join();
return 0;
}
Upvotes: 1
Views: 153
Reputation: 473212
Thus, will there be any data race in the following code?
Yes.
Meaning, is there any possibility of the reader seeing a "half write"?
That's not what "data race" means. A "data race" is a term defined by the C++ standard; the results of such a situation are undefined behavior. A "half write" is a possible outcome of such UB, but undefined behavior is undefined.
The problem here is not necessarily x86, but your compiler. Because you just used a regular type, the C++ standard says that the compiler is free to assume that the value of that object can only be changed by code the compiler can see. In the reader
loop, there are no apparent changes in shared_var
, nor are there any apparent synchronization events that would create visibility of such changes from other threads.
Now, because your infinite loop happens to call a bunch of operator<<
overloads in the iostream library, it is entirely possible that one or more of these calls invokes synchronization or is opaque to the compiler. As such, the compiler cannot simply turn your while
statement into a single if
check; it must execute it as written. And thus, your UB is now dependent upon the vagaries of x86 visibility operations.
However, if you had done something that the compiler could see within that loop, and it doesn't see anything that could perform synchronization or visibility operations, then it is well within its rights to optimize it to:
void reader() {
if(shared_var == 42)
return;
while(true)
{
//Stuff the compiler can see doesn't do synchronization.
}
}
Upvotes: 0