Slim Reader/Writer (SRW) Locks is a synchronization primitive in Windows, available starting from Windows Vista.
Name and interface suggests that it should be used as non-timed shared non-recursive mutex. However, it is common to use it as non-shared mutex as well, to avoid CRTICAL_SECTION
overhead (by using only Exclusive APIs).
I've noticed that it works also as a binary semaphore. This can come handy, as other semaphores available in Windows APIs - Event object and Semaphore object - are always a kernel call, so it is probably the only lightweight semaphore readily available from Windows API (and C++ has semaphores starting C++20, and boost thread also does not provide semaphores).
But is this reliable? Specifically, I have not found in the documentation explicit information that it can be used this way.
But, I have not found anything that prohibits this usage. The documentation seems to be uncertain.
What I'm expecting as an answer:
- Maybe someone can point me to documentation wording that permits or prohibits semaphore usage
- Maybe there's some practical experience with such usage
- Maybe someone directly involved with SRW lock implementation could clarify (there's some chance, I think)
Example - this does not hang
#include <Windows.h>
#include <atomic>
SRWLOCK lock = SRWLOCK_INIT;
std::atomic<bool> can_release{ false };
DWORD CALLBACK Thread1(LPVOID)
{
for (int i = 0; i < 3; i++)
{
while (!can_release)
{
// spin
}
can_release = false;
::ReleaseSRWLockExclusive(&lock);
}
return 0;
}
DWORD CALLBACK Thread2(LPVOID)
{
for (int i = 0; i < 3; i++)
{
can_release = true;
::AcquireSRWLockExclusive(&lock);
}
return 0;
}
int main() {
::AcquireSRWLockExclusive(&lock);
HANDLE h1 = ::CreateThread(nullptr, 0, Thread1, nullptr, 0, nullptr);
HANDLE h2 = ::CreateThread(nullptr, 0, Thread2, nullptr, 0, nullptr);
::WaitForSingleObject(h1, INFINITE);
::WaitForSingleObject(h2, INFINITE);
::CloseHandle(h1);
::CloseHandle(h2);
return 0;
}