/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuSemaphore.Win32.cpp Date: 2021-6-12 Author: Reece ***/ #include #include "AuSemaphore.Generic.hpp" #include "AuSemaphore.NT.hpp" #if !defined(_AURUNTIME_GENERIC_SEMAPHORE) namespace Aurora::Threading::Primitives { static BOOL(_stdcall *WaitOnAddress_f)( volatile VOID *Address, PVOID CompareAddress, SIZE_T AddressSize, DWORD dwMilliseconds ) = #if defined(AURORA_PLATFORM_WIN32) decltype(WaitOnAddress_f)(GetProcAddress(LoadLibraryA("API-MS-Win-Core-Synch-l1-2-0.dll"), "WaitOnAddress")); #else WaitOnAddress; #endif static void(_stdcall *WakeByAddressSingle_f)( PVOID Address ) = #if defined(AURORA_PLATFORM_WIN32) decltype(WakeByAddressSingle_f)(GetProcAddress(LoadLibraryA("API-MS-Win-Core-Synch-l1-2-0.dll"), "WakeByAddressSingle")); #else WakeByAddressSingle; #endif static void(_stdcall *WakeByAddressAll_f)( PVOID Address ) = #if defined(AURORA_PLATFORM_WIN32) decltype(WakeByAddressAll_f)(GetProcAddress(LoadLibraryA("API-MS-Win-Core-Synch-l1-2-0.dll"), "WakeByAddressAll")); #else WakeByAddressAll; #endif Semaphore::Semaphore(long iIntialValue) { this->value_ = iIntialValue; if (!WaitOnAddress_f) { ::InitializeSRWLock(&this->lock_); ::InitializeConditionVariable(&this->winCond_); } } Semaphore::~Semaphore() { } bool Semaphore::HasOSHandle(AuMach &mach) { return false; } bool Semaphore::HasLockImplementation() { return true; } bool Semaphore::TryLock() { auto old = this->value_; return (old != 0 && AuAtomicCompareExchange(&this->value_, old - 1, old) == old); } bool Semaphore::Lock(AuUInt64 uTimeout) { if (this->TryLock()) { return true; } AuUInt64 uStart = AuTime::SteadyClockMS(); AuUInt64 uEnd = uStart + uTimeout; if (WaitOnAddress_f) { auto old = this->value_; //!tryLock (with old in a scope we can access) while (!((old != 0) && (AuAtomicCompareExchange(&this->value_, old - 1, old) == old))) { AuUInt32 timeoutMs = INFINITE; if (uTimeout != 0) { uStart = Time::SteadyClockMS(); if (uStart >= uEnd) { return false; } timeoutMs = uEnd - uStart; } if (!WaitOnAddress_f(&this->value_, &old, sizeof(this->value_), timeoutMs)) { SysAssertExp(GetLastError() == ERROR_TIMEOUT); return false; } old = this->value_; } return true; } else { ::AcquireSRWLockShared(&this->lock_); // we use atomics. using shared is fine, let's not get congested early while (!TryLock()) { AuUInt32 dwTimeoutMs = INFINITE; if (uTimeout != 0) { uStart = Time::SteadyClockMS(); if (uStart >= uEnd) { ::ReleaseSRWLockShared(&this->lock_); return false; } dwTimeoutMs = uEnd - uStart; } if (!::SleepConditionVariableSRW(&this->winCond_, &this->lock_, AuUInt32(dwTimeoutMs), CONDITION_VARIABLE_LOCKMODE_SHARED)) { ::ReleaseSRWLockShared(&this->lock_); return false; } } ::ReleaseSRWLockShared(&this->lock_); } return true; } void Semaphore::Lock() { auto status = Lock(0); SysAssert(status, "Couldn't lock semaphore"); } void Semaphore::Unlock(long count) { if (!WaitOnAddress_f) { ::AcquireSRWLockShared(&this->lock_); AuAtomicAdd(&this->value_, count); ::WakeAllConditionVariable(&this->winCond_); ::ReleaseSRWLockShared(&this->lock_); } else { AuAtomicAdd(&this->value_, count); if (count == 1) { WakeByAddressSingle_f(&this->value_); } else { WakeByAddressAll_f(&this->value_); } } } void Semaphore::Unlock() { return Unlock(1); } AUKN_SYM ISemaphore *SemaphoreNew(int iInitialCount) { return _new Semaphore(iInitialCount); } AUKN_SYM void SemaphoreRelease(ISemaphore *pSemaphore) { AuSafeDelete(pSemaphore); } } #endif