/*** 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(1)) { return false; } this->uAtomicSemaphore = initialCount; this->uAtomicWord = 1; return true; } bool LSLocalSemaphore::OnTrigger(AuUInt handle) { auto bRet = this->TryTakeNoSpin(); while (true) { auto uOld = this->uAtomicWord; if (uOld == 0) { break; } if (AuAtomicCompareExchange(&this->uAtomicWord, uOld - 1, uOld) == uOld) { auto uCount = AuAtomicLoad(&this->uAtomicSemaphore); if (uOld - 1 == 0 && uCount) { AuAtomicAdd(&this->uAtomicWord, uCount); for (AU_ITERATE_N(i, uCount)) { LSSemaphore::AddOne(); } } break; } } return bRet; } bool LSLocalSemaphore::AddOne() { auto uNext = AuAtomicAdd(&this->uAtomicSemaphore, 1u); if (AuAtomicLoad(&this->uAtomicWord) >= uNext) { return true; } AuAtomicAdd(&this->uAtomicWord, 1u); LSSemaphore::AddOne(); return true; } bool LSLocalSemaphore::IsSignaled() { return this->TryTake(); } bool LSLocalSemaphore::WaitOn(AuUInt32 timeout) { return this->TryTakeWaitMS(timeout); } ELoopSource LSLocalSemaphore::GetType() { return ELoopSource::eSourceSemaphore; } 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->TryTakeNoSpin(); } bool LSLocalSemaphore::TryTakeWaitMS(AuUInt32 timeout) { auto uEndTime = AuTime::SteadyClockNS() + AuMSToNS(timeout); 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; } } } return true; } void LSLocalSemaphore::OnPresleep() { #if 0 while (auto uActiveSemaphore = AuAtomicLoad(&this->uAtomicSemaphore)) { if (AuAtomicLoad(&this->uAtomicWord) >= uActiveSemaphore) { return; } AuAtomicAdd(&this->uAtomicWord, uActiveSemaphore); for (AU_ITERATE_N(i, uActiveSemaphore)) { LSSemaphore::AddOne(); } } #endif } void LSLocalSemaphore::OnFinishSleep() { } AUKN_SYM AuSPtr NewLSSemaphore(AuUInt32 initialCount) { auto pMutex = AuMakeShared(); if (!pMutex) { SysPushErrorGeneric(); return {}; } if (!pMutex->TryInit(initialCount)) { SysPushErrorNested(); return {}; } return pMutex; } }