/*** Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: LSLocalSemaphore.cpp Date: 2023-10-21 Author: Reece ***/ #include #include "LSLocalSemaphore.hpp" #include namespace Aurora::IO::Loop { LSLocalSemaphore::LSLocalSemaphore() { } LSLocalSemaphore::~LSLocalSemaphore() { } bool LSLocalSemaphore::TryInit(AuUInt32 initialCount) { if (!LSSemaphore::TryInit(initialCount)) { return false; } this->uAtomicSemaphore = initialCount; this->uAtomicKernelSemaphore = initialCount; return true; } bool LSLocalSemaphore::OnTrigger(AuUInt handle) { auto bRet = this->TryTakeNoSpin(); bool bTriggerLater = !bRet; while (true) { auto uOld = this->uAtomicKernelSemaphore; if (uOld == 0) { break; } if (AuAtomicCompareExchange(&this->uAtomicKernelSemaphore, uOld - 1, uOld) == uOld) { auto uCount = AuAtomicLoad(&this->uAtomicSemaphore); if (uOld - 1 == 0) { if (uCount) { AuAtomicAdd(&this->uAtomicKernelSemaphore, uCount); LSSemaphore::AddMany(uCount); } else { (void)LSSemaphore::OnTrigger(0); bTriggerLater = false; } } else if (uOld || !bRet) { (void)LSSemaphore::OnTrigger(0); bTriggerLater = false; } break; } } #if 0 if (bTriggerLater) { (void)LSSemaphore::OnTrigger(0); } #endif return bRet; } bool LSLocalSemaphore::AddOne() { auto uNext = AuAtomicAdd(&this->uAtomicSemaphore, 1u); if (AuAtomicLoad(&this->uAtomicKernelSemaphore) >= uNext) { return true; } AuAtomicAdd(&this->uAtomicKernelSemaphore, 1u); LSSemaphore::AddOne(); return true; } bool LSLocalSemaphore::AddMany(AuUInt32 uCount) { auto uNext = AuAtomicAdd(&this->uAtomicSemaphore, 1u); if (AuAtomicLoad(&this->uAtomicKernelSemaphore) >= uNext) { return true; } AuAtomicAdd(&this->uAtomicKernelSemaphore, uCount); LSSemaphore::AddMany(uCount); return true; } bool LSLocalSemaphore::IsSignaled() { return this->TryTake(); } bool LSLocalSemaphore::IsSignaledNoSpinIfUserland() { return this->TryTakeNoSpin(); } bool LSLocalSemaphore::WaitOn(AuUInt32 timeout) { return this->TryTakeWaitMS(timeout); } ELoopSource LSLocalSemaphore::GetType() { return ELoopSource::eSourceFastSemaphore; } bool LSLocalSemaphore::TryTakeNoSpin() { AuUInt32 uOld {}; while ((uOld = this->uAtomicSemaphore)) { if (AuAtomicCompareExchange(&this->uAtomicSemaphore, uOld - 1, uOld) == uOld) { return true; } } return false; } bool LSLocalSemaphore::TryTakeSpin() { return Threading::Primitives::DoTryIf([&] { return this->TryTakeNoSpin(); }); } bool LSLocalSemaphore::TryTake() { return this->TryTakeSpin(); } bool LSLocalSemaphore::TryTakeWaitMS(AuUInt32 timeout) { auto uEndTime = timeout ? AuTime::SteadyClockNS() + AuMSToNS(timeout) : 0; if (this->TryTakeSpin()) { return true; } while (!this->TryTakeNoSpin()) { if (!timeout) { if (LSSemaphore::WaitOn(0)) { return true; } } else { auto uStartTime = Time::SteadyClockNS(); if (uStartTime >= uEndTime) { return false; } auto uDeltaMs = AuNSToMS(uEndTime - uStartTime); if (uDeltaMs && LSSemaphore::WaitOn(uDeltaMs)) { return true; } else if (!uDeltaMs) { if (this->TryTakeSpin()) { return true; } } } } return true; } void LSLocalSemaphore::OnPresleep() { } void LSLocalSemaphore::OnFinishSleep() { } AUKN_SYM AuSPtr NewLSSemaphore(AuUInt32 initialCount) { auto pMutex = AuMakeShared(); if (!pMutex) { SysPushErrorGeneric(); return {}; } if (!pMutex->TryInit(initialCount)) { SysPushErrorNested(); return {}; } return pMutex; } }