AuroraRuntime/Source/IO/Loop/LSLocalEvent.cpp

273 lines
6.1 KiB
C++

/***
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: LSLocalEvent.cpp
Date: 2023-10-21
Author: Reece
***/
#include <RuntimeInternal.hpp>
#include "LSLocalEvent.hpp"
#include <Source/Threading/Primitives/SMTYield.hpp>
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);
return false;
}
}
else
{
if (bits.bTriggered)
{
LSSemaphore::AddOne();
return true;
}
else
{
(void)LSSemaphore::OnTrigger(0);
return false;
}
}
}
}
bool LSLocalEvent::IsSignaled()
{
return this->TryTake();
}
bool LSLocalEvent::WaitOn(AuUInt32 timeout)
{
return this->TryTakeWaitMS(timeout);
}
ELoopSource LSLocalEvent::GetType()
{
return ELoopSource::eSourceEvent;
}
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);
}
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::DoTryIf([&]
{
return this->TryTakeNoSpin();
});
}
bool LSLocalEvent::IsSignaledNoSpinIfUserland()
{
return this->TryTakeNoSpin();
}
bool LSLocalEvent::TryTake()
{
return this->TryTakeSpin();
}
bool LSLocalEvent::TryTakeWaitMS(AuUInt32 timeout)
{
auto uEndTime = AuTime::SteadyClockNS() + AuMSToNS<AuUInt64>(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<AuInt64>(uEndTime - uStartTime);
if (uDeltaMs &&
LSSemaphore::WaitOn(uDeltaMs))
{
return true;
}
else if (!uDeltaMs)
{
if (this->TryTakeSpin())
{
return true;
}
}
}
}
return true;
}
AUKN_SYM AuSPtr<ILSEvent> NewLSEvent(bool bTriggered, bool bAtomicRelease, bool bPermitMultipleTriggers)
{
auto pMutex = AuMakeShared<LSLocalEvent>();
if (!pMutex)
{
SysPushErrorGeneric();
return {};
}
if (!pMutex->TryInit(bTriggered, bAtomicRelease, bPermitMultipleTriggers))
{
SysPushErrorNested();
return {};
}
return pMutex;
}
}