This is my code: Godbolt.
#include <atomic>
#include <iostream>
#include <thread>
#include <vector>
int main(int, char **) {
volatile bool resource = true;
std::atomic_bool stop{false};
std::atomic_int working{0};
std::thread controller([&]() {
// Inform all workers resource is going to be destroyed.
stop.store(true, std::memory_order_relaxed); // #1
// Wait for all existing workers to finish.
while (working.load(std::memory_order_relaxed)) { // #2
std::this_thread::yield();
}
// Destroy resource.
resource = false; // #3
});
std::vector<std::thread> workers;
for (int i = 0; i < 64; ++i) {
workers.emplace_back([&]() {
working.fetch_add(1, std::memory_order_relaxed); // #4
if (stop.load(std::memory_order_relaxed)) { // #5
working.fetch_sub(1, std::memory_order_relaxed); // #6
return;
}
// Access resource
if (!resource) { // #7
std::cerr << "Data race detected: resource is not available."
<< std::endl;
abort();
}
working.fetch_sub(1, std::memory_order_relaxed); // #8
});
}
for (auto &worker : workers) worker.join();
controller.join();
std::cout << "no data race detected." << std::endl;
return 0;
}
The case is like this:
- Multiple worker threads accessing a common resource(read-only).
- One controller thread will destroy the common resource after informing the workers and wait for the existing workers to finish.
This case describes a common scene: Some workers born at any time, but a controller(resource manager) might intend to destory the resource at any time. To avoid data race, the controller need to wait a bit while for current workers to finish and prevent any new workers.
I used several c++ atomics to get it work. But i have no confidence about the memory ordering although it seems to work well on my x86 server.
- In the above code, i just use the relaxed ordering which is apparently not enough, but i don't know which ordering is right here.
- By the way, are there any websites or tools to test the memory reordering issues among different platforms?
volatileis NOT related to threadsafety! It is used for memory locations that can be modified by things other then the processor (e.g. interaction with memory mapped hardware)while (working.load(...))loop