/*** Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: LSLocalMutex.cpp Date: 2023-10-21 Author: Reece ***/ #include #include "LSLocalMutex.hpp" #include namespace Aurora::IO::Loop { static auto const kFutexBitWake = 256u; LSLocalMutex::LSLocalMutex() { } LSLocalMutex::~LSLocalMutex() { } bool LSLocalMutex::TryInit() { if (!LSSemaphore::TryInit(1)) { return false; } return true; } bool LSLocalMutex::OnTrigger(AuUInt handle) { if (AuAtomicLoad(&this->uAtomicWord) & kFutexBitWake) { AuAtomicSub(&this->uAtomicWord, kFutexBitWake); } return this->TryTakeNoSpin(); } bool LSLocalMutex::Unlock() { if (!(AuAtomicLoad(&this->uAtomicWord) & 1)) { return false; } AuAtomicClearU8Lock(&this->uAtomicWord); while (true) { auto uState = AuAtomicLoad(&this->uAtomicWord); if (uState & 1) { return true; } if (uState & kFutexBitWake) { if (AuAtomicCompareExchange(&this->uAtomicWord, uState, uState) == uState) { return true; } else { continue; } } if (AuAtomicCompareExchange(&this->uAtomicWord, uState + kFutexBitWake, uState) == uState) { LSSemaphore::AddOne(); return true; } } } bool LSLocalMutex::IsSignaled() { return this->TryTake(); } bool LSLocalMutex::WaitOn(AuUInt32 timeout) { return this->TryTakeWaitMS(timeout); } ELoopSource LSLocalMutex::GetType() { return ELoopSource::eSourceMutex; } bool LSLocalMutex::TryTakeNoSpin() { return AuAtomicTestAndSet(&this->uAtomicWord, 0) == 0; } bool LSLocalMutex::TryTakeSpin() { return Threading::Primitives::DoTryIf([&] { return this->TryTakeNoSpin(); }); } bool LSLocalMutex::TryTake() { return TryTakeNoSpin(); } bool LSLocalMutex::TryTakeWaitMS(AuUInt32 timeout) { auto uEndTime = AuTime::SteadyClockMS() + 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; } if (LSSemaphore::WaitOn(AuNSToMS(uEndTime - uStartTime))) { return true; } } } return true; } AUKN_SYM AuSPtr NewLSMutex() { auto pMutex = AuMakeShared(); if (!pMutex) { SysPushErrorGeneric(); return {}; } if (!pMutex->TryInit()) { SysPushErrorNested(); return {}; } return pMutex; } }