/*** Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: LSLocalEvent.cpp Date: 2023-10-21 Author: Reece ***/ #include #include "LSLocalEvent.hpp" #include namespace Aurora::IO::Loop { LSLocalEvent::LSLocalEvent() { } LSLocalEvent::~LSLocalEvent() { } bool LSLocalEvent::TryInit(bool bTriggered, bool bAtomicRelease, bool bPermitMultipleTriggers) { if (!LSSemaphore::TryInit(1)) { return false; } this->bTriggered_ = bTriggered; this->bAtomicRelease_ = bAtomicRelease; this->bPermitMultipleTriggers_= bPermitMultipleTriggers; return true; } bool LSLocalEvent::OnTrigger(AuUInt handle) { while (true) { EventBits bits; bits.state = AuAtomicLoad(&this->state_); if (bits.bAtomicRelease) { if (bits.bTriggered) { auto next = bits; next.bTriggered = 0; if (AuAtomicCompareExchange(&this->state_, next.state, bits.state) != bits.state) { continue; } return true; } else { (void)LSSemaphore::OnTrigger(0); AuAtomicAdd(&this->uLaterAlwaysOn, 1u); return false; } } else { if (bits.bTriggered) { LSSemaphore::AddOne(); return true; } else { (void)LSSemaphore::OnTrigger(0); AuAtomicAdd(&this->uLaterAlwaysOn, 1u); return false; } } } } bool LSLocalEvent::IsSignaled() { return this->TryTake(); } ELoopSource LSLocalEvent::GetType() { return ELoopSource::eSourceFastEvent; } bool LSLocalEvent::Set() { EventBits bits, next; while (true) { bits.state = AuAtomicLoad(&this->state_); if (!bits.bPermitMultipleTriggers) { if (bits.bTriggered) { return false; } } next.state = bits.state; next.bTriggered = 1; if (AuAtomicCompareExchange(&this->state_, next.state, bits.state) == bits.state) { break; } } if (auto count = AuAtomicLoad(&this->uApproxSleepCount)) { if (bits.bAtomicRelease) { LSSemaphore::AddOne(); } else { for (AU_ITERATE_N(n, count)) { LSSemaphore::AddOne(); } } } return true; } void LSLocalEvent::OnPresleep() { AuAtomicAdd(&this->uApproxSleepCount, 1u); EventBits bits; bits.state = AuAtomicLoad(&this->state_); if (!bits.bTriggered) { return; } while (true) { auto uState = AuAtomicLoad(&this->uLaterAlwaysOn); if (!uState) { return; } if (AuAtomicCompareExchange(&this->uLaterAlwaysOn, uState - 1, uState) == uState) { break; } } LSSemaphore::AddOne(); } void LSLocalEvent::OnFinishSleep() { AuAtomicSub(&this->uApproxSleepCount, 1u); } bool LSLocalEvent::Reset() { while (true) { EventBits bits, next; bits.state = AuAtomicLoad(&this->state_); if (!bits.bTriggered) { return false; } next.state = bits.state; next.bTriggered = 0; if (AuAtomicCompareExchange(&this->state_, next.state, bits.state) == bits.state) { break; } } return true; } bool LSLocalEvent::TryTakeNoSpin() { EventBits bits; bits.state = AuAtomicLoad(&this->state_); if (bits.bAtomicRelease) { if (bits.bTriggered) { auto next = bits; next.bTriggered = 0; return AuAtomicCompareExchange(&this->state_, next.state, bits.state) == bits.state; } else { return false; } } else { return bits.bTriggered; } } bool LSLocalEvent::TryTakeSpin() { return Threading::Primitives::DoTryIfAlderLake([&] { return this->TryTakeNoSpin(); }, &this->state_); } bool LSLocalEvent::IsSignaledNoSpinIfUserland() { return this->TryTakeNoSpin(); } bool LSLocalEvent::TryTake() { return this->TryTakeSpin(); } bool LSLocalEvent::TryTakeWaitNS(AuUInt64 uEndTime) { if (this->TryTakeSpin()) { return true; } while (!this->TryTakeNoSpin()) { if (!uEndTime) { 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; } AUKN_SYM AuSPtr NewLSEvent(bool bTriggered, bool bAtomicRelease, bool bPermitMultipleTriggers) { auto pMutex = AuMakeShared(); if (!pMutex) { SysPushErrorGeneric(); return {}; } if (!pMutex->TryInit(bTriggered, bAtomicRelease, bPermitMultipleTriggers)) { SysPushErrorNested(); return {}; } return pMutex; } }