301 lines
6.8 KiB
C++
301 lines
6.8 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);
|
|
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();
|
|
}
|
|
|
|
bool LSLocalEvent::WaitOn(AuUInt32 timeout)
|
|
{
|
|
return this->TryTakeWaitMS(timeout);
|
|
}
|
|
|
|
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::TryTakeWaitMS(AuUInt32 timeout)
|
|
{
|
|
auto uEndTime = timeout ?
|
|
AuTime::SteadyClockNS() + AuMSToNS<AuUInt64>(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<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;
|
|
}
|
|
} |