2023-05-08 12:58:08 +00:00
/***
2021-06-27 21:25:29 +00:00
Copyright ( C ) 2021 J Reece Wilson ( a / k / a " Reece " ) . All rights reserved .
2023-03-12 15:27:28 +00:00
File : AuMutex . NT . cpp
2021-06-27 21:25:29 +00:00
Date : 2021 - 6 - 12
Author : Reece
* * */
2021-09-30 14:57:41 +00:00
# include <Source/RuntimeInternal.hpp>
2022-11-17 07:46:07 +00:00
# include "AuMutex.Generic.hpp"
2023-03-21 03:18:09 +00:00
# include "SMTYield.hpp"
2023-06-15 19:52:28 +00:00
# include "../AuWakeInternal.hpp"
2021-06-27 21:25:29 +00:00
# if !defined(_AURUNTIME_GENERICMUTEX)
2022-11-17 07:46:07 +00:00
# include "AuMutex.NT.hpp"
2023-03-15 00:35:29 +00:00
# include "AuConditionMutex.NT.hpp"
# include <Time/Time.hpp>
# if !defined(NTSTATUS_TIMEOUT)
# define NTSTATUS_TIMEOUT 0x102
# endif
2021-06-27 21:25:29 +00:00
namespace Aurora : : Threading : : Primitives
{
2023-04-23 18:23:10 +00:00
MutexImpl : : MutexImpl ( )
2021-06-27 21:25:29 +00:00
{
2023-02-16 16:01:21 +00:00
if ( ! pWaitOnAddress )
2022-12-28 10:22:44 +00:00
{
2023-03-16 18:25:23 +00:00
# if defined(AURORA_FORCE_SRW_LOCKS)
2022-12-28 10:22:44 +00:00
: : InitializeSRWLock ( & this - > atomicHolder_ ) ;
: : InitializeConditionVariable ( & this - > wakeup_ ) ;
2023-03-15 00:35:29 +00:00
# endif
2022-12-28 10:22:44 +00:00
}
2023-06-15 19:52:28 +00:00
2022-11-28 16:01:08 +00:00
this - > state_ = 0 ;
2021-06-27 21:25:29 +00:00
}
2023-04-23 18:23:10 +00:00
MutexImpl : : ~ MutexImpl ( )
2021-06-27 21:25:29 +00:00
{
}
2023-04-23 18:23:10 +00:00
bool MutexImpl : : HasOSHandle ( AuMach & mach )
2021-06-27 21:25:29 +00:00
{
return false ;
}
2023-08-19 17:14:28 +00:00
bool MutexImpl : : TryLockHeavy ( )
2021-06-27 21:25:29 +00:00
{
2023-03-12 15:27:28 +00:00
return DoTryIf ( [ = ] ( )
{
2023-07-10 12:12:17 +00:00
return this - > TryLockNoSpin ( ) ;
2023-03-12 15:27:28 +00:00
} ) ;
2021-06-27 21:25:29 +00:00
}
2023-08-19 17:14:28 +00:00
bool MutexImpl : : TryLock ( )
{
if ( gRuntimeConfig . threadingConfig . bPreferNtMutexSpinTryLock )
{
return TryLockHeavy ( ) ;
}
else
{
return TryLockNoSpin ( ) ;
}
}
2023-07-10 12:12:17 +00:00
bool MutexImpl : : TryLockNoSpin ( )
{
return AuAtomicTestAndSet ( & this - > state_ , 0 ) = = 0 ;
}
2023-04-23 18:23:10 +00:00
bool MutexImpl : : HasLockImplementation ( )
2021-06-27 21:25:29 +00:00
{
return true ;
}
2023-04-23 18:23:10 +00:00
void MutexImpl : : SlowLock ( )
2021-06-27 21:25:29 +00:00
{
2023-04-03 07:21:44 +00:00
auto status = LockNS ( 0 ) ;
2021-06-27 21:25:29 +00:00
SysAssert ( status , " Couldn't lock Mutex object " ) ;
}
2023-04-23 18:23:10 +00:00
bool MutexImpl : : LockMS ( AuUInt64 uTimeout )
2023-03-12 15:27:28 +00:00
{
2023-07-10 17:51:28 +00:00
if ( this - > TryLockNoSpin ( ) )
2023-04-03 07:21:44 +00:00
{
return true ;
}
2023-07-10 17:51:28 +00:00
return this - > LockNS ( AuMSToNS < AuUInt64 > ( uTimeout ) ) ;
2023-03-12 15:27:28 +00:00
}
2023-04-23 18:23:10 +00:00
bool MutexImpl : : LockNS ( AuUInt64 uTimeout )
2021-06-27 21:25:29 +00:00
{
bool returnValue = false ;
2023-08-19 17:14:28 +00:00
if ( this - > TryLockHeavy ( ) )
2022-12-28 10:22:44 +00:00
{
return true ;
}
2021-06-27 21:25:29 +00:00
2023-07-09 22:33:12 +00:00
AuUInt64 uEndTime = uTimeout ? Time : : SteadyClockNS ( ) + uTimeout : 0 ;
2023-05-30 12:12:53 +00:00
int iYieldCounter { } ;
2021-06-27 21:25:29 +00:00
2023-06-15 19:52:28 +00:00
if ( gUseNativeWaitMutex )
2021-06-27 21:25:29 +00:00
{
2023-07-10 12:12:17 +00:00
while ( ! this - > TryLockNoSpin ( ) )
2021-06-27 21:25:29 +00:00
{
2023-07-09 22:33:12 +00:00
auto & uValueRef = this - > state_ ;
auto uValue = uValueRef | 1 ;
auto uNextValue = uValue + kFutexBitWait ;
if ( AuAtomicCompareExchange ( & uValueRef , uNextValue , uValue ) = = uValue )
2021-06-27 21:25:29 +00:00
{
2023-07-09 22:33:12 +00:00
if ( ! InternalLTSWaitOnAddressHighRes ( ( void * ) & uValueRef , & uNextValue , sizeof ( uNextValue ) , uEndTime ) )
{
return false ;
}
2021-06-27 21:25:29 +00:00
}
}
2022-12-28 10:22:44 +00:00
return true ;
}
else
{
2023-03-16 18:25:23 +00:00
# if defined(AURORA_FORCE_SRW_LOCKS)
2022-12-28 10:22:44 +00:00
: : AcquireSRWLockShared ( & this - > atomicHolder_ ) ;
BOOL status = false ;
2023-07-10 12:12:17 +00:00
while ( ! this - > TryLockNoSpin ( ) )
2021-06-27 21:25:29 +00:00
{
2022-12-28 10:22:44 +00:00
AuUInt32 uTimeoutMS = INFINITE ;
2023-03-12 15:27:28 +00:00
if ( uTimeout ! = 0 )
2022-12-28 10:22:44 +00:00
{
2023-07-10 12:12:17 +00:00
auto uStartTime = Time : : SteadyClockNS ( ) ;
2022-12-28 10:22:44 +00:00
if ( uStartTime > = uEndTime )
{
goto exitWin32 ;
}
2023-03-12 15:27:28 +00:00
uTimeoutMS = AuNSToMS < AuInt64 > ( uEndTime - uStartTime ) ;
2022-12-28 10:22:44 +00:00
}
2023-03-12 15:27:28 +00:00
if ( ! uTimeoutMS )
{
: : ReleaseSRWLockShared ( & this - > atomicHolder_ ) ;
SMPPause ( ) ;
AuThreading : : ContextYield ( ) ;
: : AcquireSRWLockShared ( & this - > atomicHolder_ ) ;
}
else
2022-12-28 10:22:44 +00:00
{
2023-03-12 15:27:28 +00:00
( void ) SleepConditionVariableSRW ( & this - > wakeup_ , & this - > atomicHolder_ , uTimeoutMS , CONDITION_VARIABLE_LOCKMODE_SHARED ) ;
2022-12-28 10:22:44 +00:00
}
2021-06-27 21:25:29 +00:00
}
2022-12-28 10:22:44 +00:00
returnValue = true ;
2021-06-27 21:25:29 +00:00
2022-12-28 10:22:44 +00:00
exitWin32 :
: : ReleaseSRWLockShared ( & this - > atomicHolder_ ) ;
2023-03-15 00:35:29 +00:00
# else
if ( ! uTimeout )
{
2023-07-10 12:12:17 +00:00
while ( ! this - > TryLockNoSpin ( ) )
2023-03-15 00:35:29 +00:00
{
auto & uValueRef = this - > state_ ;
auto uValue = uValueRef | 1 ;
2023-04-01 08:53:00 +00:00
if ( AuAtomicCompareExchange ( & uValueRef , uValue + kFutexBitWait , uValue ) = = uValue )
2023-03-15 00:35:29 +00:00
{
pNtWaitForKeyedEvent ( gKeyedEventHandle , ( void * ) & uValueRef , 0 , NULL ) ;
2023-04-01 08:53:00 +00:00
AuAtomicSub ( & uValueRef , kFutexBitWake ) ;
2023-03-15 00:35:29 +00:00
}
}
return true ;
}
else
{
auto & uValueRef = this - > state_ ;
returnValue = true ;
auto uEndTimeSteady = AuTime : : SteadyClockNS ( ) + uTimeout ;
auto uEndTimeWall = AuTime : : CurrentClockNS ( ) + uTimeout ;
2023-03-16 17:31:50 +00:00
bool bFailed { } ;
2023-03-15 00:35:29 +00:00
2023-07-10 12:12:17 +00:00
while ( bFailed | | ( ! this - > TryLockNoSpin ( ) ) )
2023-03-15 00:35:29 +00:00
{
auto uValue = uValueRef | 1 ;
2023-03-16 17:31:50 +00:00
if ( ! bFailed & &
AuTime : : SteadyClockNS ( ) > = uEndTimeSteady )
2023-03-15 00:35:29 +00:00
{
2023-07-10 12:12:17 +00:00
returnValue = this - > TryLock ( ) ;
2023-03-15 00:35:29 +00:00
break ;
}
2023-04-01 08:53:00 +00:00
if ( bFailed | | AuAtomicCompareExchange ( & uValueRef , uValue + kFutexBitWait , uValue ) = = uValue )
2023-03-15 00:35:29 +00:00
{
auto uTargetTimeNt = AuTime : : ConvertTimestampNs ( uEndTimeWall ) ;
LARGE_INTEGER word ;
word . QuadPart = uTargetTimeNt ;
auto uStatus = pNtWaitForKeyedEvent ( gKeyedEventHandle , ( void * ) & this - > state_ , 0 , & word ) ;
if ( uStatus = = NTSTATUS_TIMEOUT )
{
2023-04-01 08:53:00 +00:00
auto uWWaiters = this - > state_ & ~ kFutexBitWake ;
if ( uWWaiters > = kFutexBitWait & & AuAtomicCompareExchange ( & this - > state_ , uWWaiters - kFutexBitWait , uWWaiters ) = = uWWaiters )
2023-03-16 17:31:50 +00:00
{
continue ;
}
else
2023-03-15 08:28:16 +00:00
{
2023-03-16 17:31:50 +00:00
bFailed = true ;
2023-03-15 08:28:16 +00:00
continue ;
}
2023-03-15 00:35:29 +00:00
}
else
{
2023-04-01 08:53:00 +00:00
AuAtomicSub ( & uValueRef , kFutexBitWake ) ;
2023-03-15 00:35:29 +00:00
SysAssertDbg ( uStatus = = 0 ) ;
}
}
2023-03-16 17:31:50 +00:00
bFailed = false ;
2023-03-15 00:35:29 +00:00
}
}
# endif
2022-12-28 10:22:44 +00:00
return returnValue ;
}
2021-06-27 21:25:29 +00:00
}
2023-04-23 18:23:10 +00:00
void MutexImpl : : Unlock ( )
2021-06-27 21:25:29 +00:00
{
2023-07-10 17:51:28 +00:00
# if defined(AURORA_FORCE_SRW_LOCKS)
2023-06-15 19:52:28 +00:00
if ( gUseNativeWaitMutex )
{
2023-07-09 22:33:12 +00:00
auto & uValueRef = this - > state_ ;
* ( AuUInt8 * ) & uValueRef = 0 ;
while ( true )
{
auto uValue = uValueRef ;
if ( uValue < kFutexBitWait )
{
return ;
}
if ( AuAtomicCompareExchange ( & uValueRef , uValue - kFutexBitWait , uValue ) = = uValue )
{
pWakeByAddressSingle ( ( void * ) & this - > state_ ) ;
return ;
}
SMPPause ( ) ;
}
return ;
2023-06-15 19:52:28 +00:00
}
2023-03-15 00:35:29 +00:00
2023-07-10 17:51:28 +00:00
: : AcquireSRWLockExclusive ( & this - > atomicHolder_ ) ;
this - > state_ = 0 ;
: : ReleaseSRWLockExclusive ( & this - > atomicHolder_ ) ;
: : WakeAllConditionVariable ( & this - > wakeup_ ) ;
# else
2023-03-15 00:35:29 +00:00
2023-07-10 17:51:28 +00:00
auto & uValueRef = this - > state_ ;
2023-05-08 12:58:08 +00:00
2023-07-10 17:51:28 +00:00
# if defined(AURORA_ARCH_X86) || defined(AURORA_ARCH_X64)
// Intel 64 and IA - 32 Architectures Software Developer's Manual, Volume 3A: Section: 8.2.3.1
* ( AuUInt8 * ) & uValueRef = 0 ;
// From this point onwards, our thread could be subject to StoreLoad re-ordering
// ...but it should not matter.
2023-05-08 12:58:08 +00:00
2023-07-10 17:51:28 +00:00
// Given the memory model of x86[64], we can only really expect to be out of order during an unfenced load operation, which in this class, can only be expected under this function before the CAS.
// No other place reads.
// Re-ordering race condition 1: one thread wins an atomic bit set, that we dont catch until the CAS, resulting in: a slow implicit fence under the cas, a mm_pause stall, a compare, and a return
// alt: uValueRef reads zero, resulting in a preemptive return while no threads need to be awoken
// Re-ordering race condition 2: we unlock, multiple threads enter ::Lock(), we somehow read `uValue = uValueRef` as zero, and then the first atomic bitsetandtest winner thread signals the keyed mutex
// I fail to see how:
// *byte = 0; | |
// | interlocked atomicbitset | interlocked atomicbitset fail
// | [logic] | interlocked atomic set kFutexBitWait
// | *byte = 0; | yield
// | auto uValue =[acquire]= uValueRef
// ...would result in the second thread missing the third threads atomic set kFutexBitWait (cst (?) on the account of 8.2.3.1, 8.2.3.8, etc)
// Also note: mfence is far too expensive and the _ReadWriteBarrier() intrinsics do absolutely nothing
_ReadWriteBarrier ( ) ;
# else
InterlockedAndRelease ( ( volatile LONG * ) & uValueRef , ~ 0xFF ) ;
# endif
while ( true )
{
auto uValue = uValueRef ;
2023-05-08 12:58:08 +00:00
2023-07-10 17:51:28 +00:00
if ( uValue < kFutexBitWait )
2023-03-15 00:35:29 +00:00
{
2023-07-10 17:51:28 +00:00
return ;
}
2023-03-15 16:06:58 +00:00
2023-07-10 17:51:28 +00:00
// StoreLoad race-conditions here cannot result in a return
// We should see StoreLoads of at least our *pByte = 0
// or we should at least see the CST of kFutexBitWait being applied
if ( uValue & 1 )
{
return ;
}
2023-03-15 00:35:29 +00:00
2023-07-10 17:51:28 +00:00
if ( gUseNativeWaitMutex )
{
if ( AuAtomicCompareExchange ( & uValueRef , uValue - kFutexBitWait , uValue ) = = uValue )
2023-03-15 00:35:29 +00:00
{
2023-07-10 17:51:28 +00:00
pWakeByAddressSingle ( ( void * ) & this - > state_ ) ;
2023-03-15 00:35:29 +00:00
return ;
}
2023-07-10 17:51:28 +00:00
}
else
{
2023-04-01 08:53:00 +00:00
if ( uValue & kFutexBitWake )
2023-03-15 00:35:29 +00:00
{
2023-05-08 12:58:08 +00:00
// StoreLoad paranoia
if ( AuAtomicCompareExchange ( & uValueRef , uValue , uValue ) = = uValue )
{
return ;
}
else
{
SMPPause ( ) ;
continue ;
}
2023-03-15 00:35:29 +00:00
}
2023-04-01 08:53:00 +00:00
if ( AuAtomicCompareExchange ( & uValueRef , uValue - kFutexBitWait + kFutexBitWake , uValue ) = = uValue )
2023-03-15 00:35:29 +00:00
{
pNtReleaseKeyedEvent ( gKeyedEventHandle , ( void * ) & uValueRef , 0 , NULL ) ;
return ;
}
}
2023-07-10 17:51:28 +00:00
SMPPause ( ) ;
2022-12-28 10:22:44 +00:00
}
2023-07-10 17:51:28 +00:00
# endif
2021-06-27 21:25:29 +00:00
}
2023-04-03 07:21:44 +00:00
AUKN_SYM IHyperWaitable * MutexNew ( )
2021-06-27 21:25:29 +00:00
{
2023-04-23 18:23:10 +00:00
return _new MutexImpl ( ) ;
2021-06-27 21:25:29 +00:00
}
2023-04-03 07:21:44 +00:00
AUKN_SYM void MutexRelease ( IHyperWaitable * pMutex )
2021-06-27 21:25:29 +00:00
{
2023-04-23 18:23:10 +00:00
AuSafeDelete < MutexImpl * > ( pMutex ) ;
2021-06-27 21:25:29 +00:00
}
2023-03-21 03:18:09 +00:00
2023-04-23 18:23:10 +00:00
AUROXTL_INTERFACE_SOO_SRC_EX ( AURORA_SYMBOL_EXPORT , Mutex , MutexImpl )
2021-06-27 21:25:29 +00:00
}
# endif