winapisemaphorereadwritelock

Can SRW Lock be used as a binary semaphore?


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:


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;
}

Solution

  • @Raymond Chen is right. Application Verifier reports errors for the code in question:

    The code in question produces this error:

    =======================================
    VERIFIER STOP 0000000000000255: pid 0x1A44: The SRW lock being released was not acquired by this thread. 
    
        00007FF73979C170 : SRW Lock
        00000000000025CC : Current ThreadId.
        00000000000043F4 : ThreadId of the thread that acquired the SRW lock.
        000001C1BEA8BF40 : Address of the acquire stack trace. Use dps <address> to see where the SRW lock was acquired.
    
    
    =======================================
    This verifier stop is continuable.
    After debugging it use `go' to continue.
    
    =======================================
    
    
    
    =======================================
    VERIFIER STOP 0000000000000253: pid 0x1A44: The SRW lock is being acquired recursively by the same thread. 
    
        00007FF73979C170 : SRW Lock
        000001C1BEA8BF40 : Address of the first acquire stack trace. Use dps <address> to see where the SRW lock was acquired.
        0000000000000000 : Not used
        0000000000000000 : Not used
    
    
    =======================================
    This verifier stop is continuable.
    After debugging it use `go' to continue.
    
    =======================================
    

    As of now, the documentation also explicitly prohibits releasing in a different thread, see ReleaseSRWLockExclusive, ReleaseSRWLockShared:

    The SRW lock must be released by the same thread that acquired it. You can use Application Verifier to help verify that your program uses SRW locks correctly (enable Locks checker from Basic group).