I've been hearing so many conflicting answers, and now I don't know what to think. The agreed-upon knowledge is that for sharing memory in a thread safe manner in C++, it's required to use volatile together with std::mutex.
Based on that understanding, I've been writing code like this:
volatile bool ready = false;
std::condition_variable cv;
std::mutex mtx;
std::unique_lock<std::mutex> lckr{ mtx };
cv.wait(lckr, [&ready]() -> bool { return ready; });
But then I saw a lecture of Chandler Carruth in CppCon where he said (as a side note) that volatile is not required in this situation, and that I should basically never use volatile.
I then saw other answers in Stack Overflow that say that volatile should never be used, and it's not good enough and it doesn't guarantee atomicity at all.
Is Chandler Carruth correct? Are we both wrong?
Now I have 3 options:
- Must use volatile or std::atomic
- Any boolean will do
- Must be std::atomic
I want to know if I'm allowed by the C++14 ISO standard to write code like this:
#include <condition_variable>
#include <mutex>
#include <iostream>
#include <future>
#include <functional>
struct sync_t
{
std::condition_variable cv;
std::mutex mtx;
bool ready{ false };
};
static void threaded_func(sync_t& sync)
{
std::lock_guard<std::mutex> lckr{ sync.mtx };
sync.ready = true;
std::cout << "Waking up main thread" << std::endl;
sync.cv.notify_one();
}
int main()
{
sync_t sync;
{
std::unique_lock<std::mutex> lckr{ sync.mtx };
sync.ready = false;
std::future<void> thread =
std::async(std::launch::async, threaded_func, std::ref(sync));
std::cout << "Preparing to sleep" << std::endl;
sync.cv.wait(lckr, [&sync]() -> bool { return sync.ready; });
thread.get();
}
std::cout << "Done program execution" << std::endl;
return 0;
}
and what happens when I make it:
volatile bool ready{ false };
and what happens when I make it:
std::atomic<bool> ready{ false };