> Afaict, it should work. I have not tested this in Relacy yet.
It works. And it's not necessary to test such simple code
with Relacy or whatever.
I yesterday wrote a portable semaphore-class:
// fsemaphore.h
#pragma once
#if defined(_MSC_VER)
#include <Windows.h>
#elif defined(__gnu_linux__)
#include <unistd.h>
#include <linux/futex.h>
#include <stdint.h>
#include <sys/time.h>
#include <sys/syscall.h>
#endif
#include <atomic>
#include <cstdint>
#include <system_error>
struct fsemaphore
{
fsemaphore();
~fsemaphore();
fsemaphore( fsemaphore const & ) = delete;
void operator =( fsemaphore const & ) = delete;
bool wait();
bool release();
void forced_wait();
void forced_release();
private:
#if defined(_MSC_VER)
bool m_isCounter;
std::atomic<std::uint32_t> m_semCounter;
HANDLE m_hSemaphore;
using WOA_ADDR = BOOL (WINAPI *)( volatile VOID *Address, PVOID
CompareAddress, SIZE_T AddressSize, DWORD dwMilliseconds );
using WBAS_ADDR = void (WINAPI *)( PVOID Address );
static
WOA_ADDR waitOnAddress;
static
WBAS_ADDR wakeByAddressSingle;
#elif defined(__unix__)
static int futex( uint32_t *uaddr, int futex_op, uint32_t val, timespec
const *timeout, uint32_t *uaddr2, uint32_t val3 );
std::atomic<std::uint32_t> m_semCounter;
#endif
};
inline
void fsemaphore::forced_wait()
{
while( !wait() );
}
inline
void fsemaphore::forced_release()
{
while( !release() );
}
// fsemaphore.cpp
#include "fsemaphore.h"
#if defined(_MSC_VER)
fsemaphore::WOA_ADDR fsemaphore::waitOnAddress = nullptr;
fsemaphore::WBAS_ADDR fsemaphore::wakeByAddressSingle = nullptr;
fsemaphore::fsemaphore()
{
using namespace std;
if( waitOnAddress == (WOA_ADDR)-1 )
goto useSem;
if( !waitOnAddress )
{
HMODULE hmodSynch = LoadLibraryA( "API-MS-Win-Core-Synch-l1-2-0.dll" );
if( hmodSynch == NULL )
goto useSemErr;
waitOnAddress = (WOA_ADDR) GetProcAddress( hmodSynch,
"WaitOnAddress" );
wakeByAddressSingle = (WBAS_ADDR)GetProcAddress( hmodSynch,
"WakeByAddressSingle" );
if( !waitOnAddress || !wakeByAddressSingle )
goto useSemErr;
}
m_isCounter = true;
m_semCounter.store( 0, memory_order_relaxed );
return;
useSemErr:
waitOnAddress = (WOA_ADDR)-1;
useSem:
m_isCounter = false;
if( !(m_hSemaphore = CreateSemaphore( nullptr, 0, 0x7FFFFFFF, nullptr )) )
throw system_error( error_code( (int)GetLastError(), system_category()
), "can't create Win32 semaphore for fsemaphore-object" );
}
fsemaphore::~fsemaphore()
{
if( !m_isCounter )
CloseHandle( m_hSemaphore );
}
bool fsemaphore::wait()
{
using namespace std;
if( m_isCounter )
for( ; ; )
{
uint32_t cmp = m_semCounter;
if( cmp && m_semCounter.compare_exchange_weak( cmp, cmp - 1,
memory_order_acquire, memory_order_relaxed ) )
return true;
cmp = 0;
waitOnAddress( &m_semCounter, &cmp, sizeof m_semCounter, INFINITE );
}
else
return WaitForSingleObject( m_hSemaphore, INFINITE ) == WAIT_OBJECT_0;
}
bool fsemaphore::release()
{
using namespace std;
if( m_isCounter )
{
m_semCounter.fetch_add( 1, memory_order_relaxed );
wakeByAddressSingle( &m_semCounter );
return true;
}
else
return ReleaseSemaphore( m_hSemaphore, 1, nullptr );
}
#elif defined(__gnu_linux__)
fsemaphore::fsemaphore()
{
m_semCounter.store( 0, std::memory_order_relaxed );
}
fsemaphore::~fsemaphore()
{
}
bool fsemaphore::wait()
{
using namespace std;
for( ; ; )
{
uint32_t cmp = m_semCounter;
if( cmp && m_semCounter.compare_exchange_weak( cmp, cmp - 1,
memory_order_acquire, memory_order_relaxed ) )
return true;
cmp = 0;
if( futex( (uint32_t *)&m_semCounter, FUTEX_WAIT, 0, nullptr, 0, 0 )
!= 0 )
return false;
}
}
bool fsemaphore::release()
{
m_semCounter.fetch_add( 1, std::memory_order_relaxed );
return futex( (uint32_t *)&m_semCounter, FUTEX_WAKE, 1, nullptr, 0, 0 )
== 1;
}
int fsemaphore::futex( uint32_t *uaddr, int futex_op, uint32_t val,
timespec const *timeout, uint32_t *uaddr2, uint32_t val3 )
{
return syscall( SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3 );
}
#endif