This is the follow-up of the question How is std::atomic<T>::notify_all ordered?
What would be the answer to that question, if I use WaitOnAddress
or futex
directly.
From the answer to that question the conclusion is that the below program does not hang, and C++ provides necessary guarantees, although wording still may raise questions:
#include <atomic>
#include <chrono>
#include <thread>
int main()
{
std::atomic<bool> go{ false };
std::thread thd([&go] {
go.wait(false, std::memory_order_relaxed); // (1)
});
std::this_thread::sleep_for(std::chrono::milliseconds(400));
go.store(true, std::memory_order_relaxed); // (2)
go.notify_all(); // (3)
thd.join();
return 0;
}
Now let's consider translation of this program to pure Windows API, without C++20 or even C++11:
#include <Windows.h>
#pragma comment(lib, "Synchronization.lib")
volatile DWORD go = 0;
DWORD CALLBACK ThreadProc(LPVOID)
{
DWORD zero = 0;
while (go == zero)
{
WaitOnAddress(&go, &zero, sizeof(DWORD), INFINITE); // (1)
}
return 0;
}
int main()
{
HANDLE thread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
if (thread == 0)
{
return 1;
}
Sleep(400);
go = 1; // (2)
WakeByAddressAll(&go); // (3)
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
return 0;
}
Due to spurious wakeups, I've added the loop.
So same question here. If (2) and (3) are observed in reverse order in (1), the may hang due to lost notification. Does WinAPI prevent that, or need to put fences explicitly.
The practical application of the answer to this questions is an implementation of std::atomic<T>::wait
by a standard library or a substitute of it on Windows platform.
I have also same question about futex
, in context of Linux platform implementation.