Reputation: 13689
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.
Upvotes: 1
Views: 466
Reputation: 13689
From Synchronization and Multiprocessor Issues page of Windows documentation:
Memory Ordering
[...]
The following synchronization functions use the appropriate barriers to ensure memory ordering:
- Functions that enter or leave critical sections
- Functions that signal synchronization objects
- Wait functions
- Interlocked functions
On Synchronization Functions page, Wait functions section lists WaitOnAddress
, WakeByAddressAll
, WakeByAddressSingle
. They are also listed in Wait Functions page as Waiting on an Address.
So all three functions count as wait functions, and thus have appropriate barriers. Though it is not defined what exactly is an appropriate barrier, it seems not possible to imagine any barrier that would not prevent the situation in questions, but otherwise would be somehow "appropriate".
So the situation in question is prevented by these barriers.
Upvotes: 1