2023-10-21 03:57:25 +00:00
/***
Copyright ( C ) 2023 J Reece Wilson ( a / k / a " Reece " ) . All rights reserved .
File : LSLocalSemaphore . cpp
Date : 2023 - 10 - 21
Author : Reece
* * */
# include <RuntimeInternal.hpp>
# include "LSLocalSemaphore.hpp"
# include <Source/Threading/Primitives/SMTYield.hpp>
namespace Aurora : : IO : : Loop
{
LSLocalSemaphore : : LSLocalSemaphore ( )
{
}
LSLocalSemaphore : : ~ LSLocalSemaphore ( )
{
}
2024-08-05 14:57:13 +00:00
bool LSLocalSemaphore : : TryInit ( AuUInt32 initialCount , AuUInt32 uMaxCount )
2023-10-21 03:57:25 +00:00
{
2023-10-22 05:11:39 +00:00
if ( ! LSSemaphore : : TryInit ( initialCount ) )
2023-10-21 03:57:25 +00:00
{
return false ;
}
this - > uAtomicSemaphore = initialCount ;
2023-10-22 05:11:39 +00:00
this - > uAtomicKernelSemaphore = initialCount ;
2024-08-05 14:57:13 +00:00
this - > uMaxCount = uMaxCount ;
2023-10-21 03:57:25 +00:00
return true ;
}
2024-07-30 16:30:19 +00:00
void LSLocalSemaphore : : DoParanoia ( )
{
if ( auto uCount = AuAtomicLoad ( & this - > uAtomicSemaphore ) )
{
AuAtomicAdd ( & this - > uAtomicKernelSemaphore , uCount ) ;
LSSemaphore : : AddMany ( uCount ) ;
}
}
2023-10-21 03:57:25 +00:00
bool LSLocalSemaphore : : OnTrigger ( AuUInt handle )
{
2023-10-21 17:12:23 +00:00
auto bRet = this - > TryTakeNoSpin ( ) ;
while ( true )
2023-10-21 03:57:25 +00:00
{
2024-07-30 16:30:19 +00:00
auto uOld = AuAtomicLoad ( & this - > uAtomicKernelSemaphore ) ;
2023-10-21 17:12:23 +00:00
if ( uOld = = 0 )
{
2024-07-30 16:30:19 +00:00
DoParanoia ( ) ;
2023-10-21 17:12:23 +00:00
break ;
}
2023-10-22 05:11:39 +00:00
if ( AuAtomicCompareExchange ( & this - > uAtomicKernelSemaphore , uOld - 1 , uOld ) = = uOld )
2023-10-21 17:12:23 +00:00
{
auto uCount = AuAtomicLoad ( & this - > uAtomicSemaphore ) ;
2023-12-01 10:55:24 +00:00
if ( uOld - 1 = = 0 )
2023-10-21 17:12:23 +00:00
{
2024-07-30 16:30:19 +00:00
# if defined(AURORA_PLATFORM_LINUX)
if ( uCount = = 1 )
{
// Don't acknowledge?
// Don't write into?
// saves two syscalls for nothang
2024-07-31 11:01:31 +00:00
AuAtomicAdd ( & this - > uAtomicKernelSemaphore , 1u ) ;
2024-07-30 16:30:19 +00:00
}
else if ( uCount )
{
2024-07-31 11:01:31 +00:00
AuAtomicAdd ( & this - > uAtomicKernelSemaphore , uCount ) ;
2024-07-30 16:30:19 +00:00
LSSemaphore : : AddMany ( uCount - 1 ) ;
}
# else
2023-12-01 10:55:24 +00:00
if ( uCount )
{
AuAtomicAdd ( & this - > uAtomicKernelSemaphore , uCount ) ;
LSSemaphore : : AddMany ( uCount ) ;
2024-08-05 14:57:13 +00:00
# if !defined(AURORA_IS_MODERNNT_DERIVED)
( void ) LSSemaphore : : OnTrigger ( 0 ) ;
# endif
2023-12-01 10:55:24 +00:00
}
2024-07-30 16:30:19 +00:00
# endif
2023-12-01 10:55:24 +00:00
else
{
( void ) LSSemaphore : : OnTrigger ( 0 ) ;
}
}
else if ( uOld | | ! bRet )
{
( void ) LSSemaphore : : OnTrigger ( 0 ) ;
2023-10-21 17:12:23 +00:00
}
break ;
}
2023-10-21 03:57:25 +00:00
}
2023-10-21 17:12:23 +00:00
return bRet ;
2023-10-21 03:57:25 +00:00
}
bool LSLocalSemaphore : : AddOne ( )
{
2024-08-05 14:57:13 +00:00
AuUInt32 uNext { } ;
2023-10-21 03:57:25 +00:00
2024-08-05 14:57:13 +00:00
if ( auto uMaxValue = this - > uMaxCount )
2023-10-21 03:57:25 +00:00
{
2024-08-05 14:57:13 +00:00
while ( true )
{
auto uCurrentValue = AuAtomicLoad ( & this - > uAtomicSemaphore ) ;
uNext = uCurrentValue + 1 ;
if ( uNext > uMaxValue )
{
return false ;
}
if ( AuAtomicCompareExchange ( & this - > uAtomicSemaphore , uNext , uCurrentValue ) = = uCurrentValue )
{
break ;
}
}
}
else
{
uNext = AuAtomicAdd ( & this - > uAtomicSemaphore , 1u ) ;
2023-10-21 03:57:25 +00:00
}
2023-10-21 17:12:23 +00:00
2024-07-30 16:30:19 +00:00
while ( true )
{
auto uCurrentValue = AuAtomicLoad ( & this - > uAtomicKernelSemaphore ) ;
auto uNextValue = uCurrentValue ;
bool bCanReturn = false ;
if ( uCurrentValue < uNext )
{
uNextValue = uNext ;
}
else
{
bCanReturn = true ;
}
if ( AuAtomicCompareExchange ( & this - > uAtomicKernelSemaphore , uNextValue , uCurrentValue ) = = uCurrentValue )
{
if ( bCanReturn )
{
return true ;
}
else
{
break ;
}
}
}
2024-08-05 14:57:13 +00:00
return LSSemaphore : : AddOne ( ) ;
2023-10-21 03:57:25 +00:00
}
2023-10-22 05:11:39 +00:00
bool LSLocalSemaphore : : AddMany ( AuUInt32 uCount )
{
2024-08-05 14:57:13 +00:00
AuUInt32 uNext { } ;
if ( auto uMaxValue = this - > uMaxCount )
{
while ( true )
{
auto uCurrentValue = AuAtomicLoad ( & this - > uAtomicSemaphore ) ;
uNext = uCurrentValue + uCount ;
if ( uNext > uMaxValue )
{
return false ;
}
if ( AuAtomicCompareExchange ( & this - > uAtomicSemaphore , uNext , uCurrentValue ) = = uCurrentValue )
{
break ;
}
}
}
else
{
uNext = AuAtomicAdd ( & this - > uAtomicSemaphore , uCount ) ;
}
2023-10-22 05:11:39 +00:00
2024-07-30 16:30:19 +00:00
#if 0
2023-10-22 05:11:39 +00:00
if ( AuAtomicLoad ( & this - > uAtomicKernelSemaphore ) > = uNext )
{
return true ;
}
2024-07-30 16:30:19 +00:00
else
{
/* if AddMany add/load/kernel-wake race condition, it's the next AddMany persons problem. */
/* uAtomicKernelSemaphore cannot be lower than uAtomicSemaphore, at the epilogue of the last unlock/adds tick. */
2023-10-22 05:11:39 +00:00
2024-07-30 16:30:19 +00:00
/* If it somehow is, ::OnTrigger will check that the final kernel negative increment does not occur just before (linux) after (win32) (bool(this->uAtomicSemaphore)). */
/* Remember: this->uAtomicKernelSemaphore should only be decremented after uAtomicSemaphore and uAtomicKernelSemaphore have already been incremented together... */
/* ...therefore, the last kernel waker should always see bool(this->uAtomicSemaphore), unless stolen by another thread. */
/* if stolen, it's a race condition we dont care about; we avoided the kernel object and state entirely. We have to wait for another AddMany to wake us up. */
/* if not stolen, uAtomicSemaphore is read as non-zero, and is readded to the kernel semaphore */
/* Most users just use the IO event objects LSAsync and LSLocalEvent. These are known good. */
}
2023-10-22 05:11:39 +00:00
AuAtomicAdd ( & this - > uAtomicKernelSemaphore , uCount ) ;
2024-07-30 16:30:19 +00:00
# else
while ( true )
{
auto uCurrentValue = AuAtomicLoad ( & this - > uAtomicKernelSemaphore ) ;
auto uNextValue = uCurrentValue ;
bool bCanReturn = false ;
if ( uCurrentValue < uNext )
{
uNextValue = uNext ;
}
else
{
bCanReturn = true ;
}
if ( AuAtomicCompareExchange ( & this - > uAtomicKernelSemaphore , uNextValue , uCurrentValue ) = = uCurrentValue )
{
if ( bCanReturn )
{
return true ;
}
else
{
uCount = uNext - uCurrentValue ;
break ;
}
}
}
# endif
2024-08-05 14:57:13 +00:00
return LSSemaphore : : AddMany ( uCount ) ;
2023-10-22 05:11:39 +00:00
}
2023-10-21 03:57:25 +00:00
bool LSLocalSemaphore : : IsSignaled ( )
{
return this - > TryTake ( ) ;
}
2023-12-01 10:33:55 +00:00
bool LSLocalSemaphore : : IsSignaledNoSpinIfUserland ( )
{
return this - > TryTakeNoSpin ( ) ;
}
2023-10-21 03:57:25 +00:00
ELoopSource LSLocalSemaphore : : GetType ( )
{
2023-12-18 07:37:48 +00:00
return ELoopSource : : eSourceFastSemaphore ;
2023-10-21 03:57:25 +00:00
}
bool LSLocalSemaphore : : TryTakeNoSpin ( )
{
AuUInt32 uOld { } ;
while ( ( uOld = this - > uAtomicSemaphore ) )
{
if ( AuAtomicCompareExchange ( & this - > uAtomicSemaphore , uOld - 1 , uOld ) = = uOld )
{
return true ;
}
}
return false ;
}
bool LSLocalSemaphore : : TryTakeSpin ( )
{
2024-05-03 11:14:52 +00:00
return Threading : : Primitives : : DoTryIfAlderLake ( [ & ]
2023-10-21 03:57:25 +00:00
{
return this - > TryTakeNoSpin ( ) ;
2024-05-03 11:14:52 +00:00
} , & this - > uAtomicSemaphore ) ;
2023-10-21 03:57:25 +00:00
}
bool LSLocalSemaphore : : TryTake ( )
{
2023-12-01 10:33:55 +00:00
return this - > TryTakeSpin ( ) ;
2023-10-21 03:57:25 +00:00
}
2024-09-07 21:45:34 +00:00
bool LSLocalSemaphore : : TryTakeWaitNS ( AuUInt64 uEndTime )
2023-10-21 03:57:25 +00:00
{
if ( this - > TryTakeSpin ( ) )
{
return true ;
}
while ( ! this - > TryTakeNoSpin ( ) )
{
2024-09-07 21:45:34 +00:00
if ( ! uEndTime )
2023-10-21 03:57:25 +00:00
{
if ( LSSemaphore : : WaitOn ( 0 ) )
{
return true ;
}
}
else
{
auto uStartTime = Time : : SteadyClockNS ( ) ;
if ( uStartTime > = uEndTime )
{
return false ;
}
2023-10-21 04:34:35 +00:00
auto uDeltaMs = AuNSToMS < AuInt64 > ( uEndTime - uStartTime ) ;
if ( uDeltaMs & &
LSSemaphore : : WaitOn ( uDeltaMs ) )
2023-10-21 03:57:25 +00:00
{
return true ;
}
2023-10-26 16:25:40 +00:00
else if ( ! uDeltaMs )
{
if ( this - > TryTakeSpin ( ) )
{
return true ;
}
}
2023-10-21 03:57:25 +00:00
}
}
return true ;
}
2023-10-21 17:12:23 +00:00
void LSLocalSemaphore : : OnPresleep ( )
{
}
void LSLocalSemaphore : : OnFinishSleep ( )
{
2023-10-22 05:11:39 +00:00
2023-10-21 17:12:23 +00:00
}
2024-08-05 14:57:13 +00:00
AUKN_SYM AuSPtr < ILSSemaphore > NewLSSemaphore ( AuUInt32 uInitialCount )
{
auto pMutex = AuMakeShared < LSLocalSemaphore > ( ) ;
if ( ! pMutex )
{
SysPushErrorGeneric ( ) ;
return { } ;
}
if ( ! pMutex - > TryInit ( uInitialCount ) )
{
SysPushErrorNested ( ) ;
return { } ;
}
return pMutex ;
}
AUKN_SYM AuSPtr < ILSSemaphore > NewLSSemaphoreEx ( AuUInt32 uInitialCount , AuUInt32 uMaxCount )
2023-10-21 03:57:25 +00:00
{
auto pMutex = AuMakeShared < LSLocalSemaphore > ( ) ;
if ( ! pMutex )
{
SysPushErrorGeneric ( ) ;
return { } ;
}
2024-08-05 14:57:13 +00:00
if ( ! pMutex - > TryInit ( uInitialCount , uMaxCount ) )
2023-10-21 03:57:25 +00:00
{
SysPushErrorNested ( ) ;
return { } ;
}
return pMutex ;
}
}