AuroraRuntime/Source/Threading/Primitives/AuEvent.cpp
Jamie Reece Wilson 62b6fa20f8 [*] Update the copyright header of most of the primitives
[*] Fix generic mutex abs yield always returning true
2024-01-29 14:48:04 +00:00

348 lines
8.2 KiB
C++

/***
Copyright (C) 2021-2024 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Event.cpp
Date: 2021-6-12
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuEvent.hpp"
#include "SMTYield.hpp"
#include "../AuWakeInternal.hpp"
namespace Aurora::Threading::Primitives
{
EventImpl::EventImpl(bool bTriggered, bool bAtomicRelease, bool bPermitMultipleTriggers) :
bTriggered_(bTriggered),
bAtomicRelease_(bAtomicRelease),
bPermitMultipleTriggers_(bPermitMultipleTriggers)
{}
EventImpl::~EventImpl() {}
bool EventImpl::Init()
{
return true;
}
bool EventImpl::LockMS(AuUInt64 uTimeout /*=0*/)
{
return this->LockNS(AuMSToNS<AuUInt64>(uTimeout));
}
bool EventImpl::LockNS(AuUInt64 uTimeout /*=0*/)
{
AuInt64 uStartTime {};
AuInt64 uEndTime {};
if (this->AtomicIsEventSetLogicNoSpinNoLock())
{
return true;
}
if (uTimeout)
{
uStartTime = Time::SteadyClockNS();
uEndTime = uStartTime + uTimeout;
}
if (gPreferFutexEvent)
{
auto pSleepCounter = this->GetSleepCounter();
while (!this->AtomicIsEventSetLogicNoSpinNoLock())
{
bool bStatus {};
EventBits bits;
bits.state = AuAtomicLoad(&this->state_);
if (bits.bTriggered)
{
continue;
}
AuAtomicAdd(pSleepCounter, 1u);
bStatus = InternalLTSWaitOnAddressHighRes(&this->state_, &bits.state, sizeof(bits.state), uEndTime);
AuAtomicSub(pSleepCounter, 1u);
if (!bStatus)
{
return false;
}
}
}
else
{
AU_LOCK_GUARD(this->mutex_);
while (!AtomicIsEventSetLogicNoSpinNoLock())
{
AuUInt32 uTimeoutNS {};
if (uTimeout)
{
uStartTime = Time::SteadyClockNS();
if (uStartTime >= uEndTime)
{
return false;
}
uTimeoutNS = uEndTime - uStartTime;
}
if (!this->condition_.WaitForSignalNsEx(&this->mutex_, uTimeoutNS))
{
continue;
}
}
}
return true;
}
bool EventImpl::LockAbsNS(AuUInt64 uEndTime)
{
if (this->AtomicIsEventSetLogicNoSpinNoLock())
{
return true;
}
if (gPreferFutexEvent)
{
auto pSleepCounter = this->GetSleepCounter();
while (!this->AtomicIsEventSetLogicNoSpinNoLock())
{
bool bStatus {};
EventBits bits;
bits.state = AuAtomicLoad(&this->state_);
if (bits.bTriggered)
{
continue;
}
AuAtomicAdd(pSleepCounter, 1u);
bStatus = InternalLTSWaitOnAddressHighRes(&this->state_, &bits.state, sizeof(bits.state), uEndTime);
AuAtomicSub(pSleepCounter, 1u);
if (!bStatus)
{
return false;
}
}
}
else
{
AU_LOCK_GUARD(this->mutex_);
while (!AtomicIsEventSetLogicNoSpinNoLock())
{
AuUInt32 uTimeoutNS {};
if (uEndTime)
{
auto uStartTime = Time::SteadyClockNS();
if (uStartTime >= uEndTime)
{
return false;
}
uTimeoutNS = uEndTime - uStartTime;
}
if (!this->condition_.WaitForSignalNsEx(&this->mutex_, uTimeoutNS))
{
continue;
}
}
}
return true;
}
bool EventImpl::TryLock()
{
return AtomicIsEventSetLogicNoSpinNoLock();
}
bool EventImpl::AtomicIsEventSetLogicNoSpinNoLock()
{
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;
}
}
void EventImpl::Reset()
{
while (true)
{
EventBits bits, next;
bits.state = AuAtomicLoad(&this->state_);
if (!bits.bTriggered)
{
break;
}
next.state = bits.state;
next.bTriggered = 0;
if (AuAtomicCompareExchange(&this->state_, next.state, bits.state) == bits.state)
{
break;
}
}
}
void EventImpl::Set()
{
EventBits bits, next;
while (true)
{
bits.state = AuAtomicLoad(&this->state_);
// TODO: runtime config option to turn this into a handable exception
SysAssertExp((bits.bPermitMultipleTriggers) || (!bits.bTriggered), "Can not trigger an awake event object");
next.state = bits.state;
next.bTriggered = 1;
if (AuAtomicCompareExchange(&this->state_, next.state, bits.state) == bits.state)
{
break;
}
}
this->DoSignal(bits);
}
bool EventImpl::TrySet()
{
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;
}
}
this->DoSignal(bits);
return true;
}
void EventImpl::DoSignal(const EventBits &bits)
{
if (gPreferFutexEvent)
{
if (!AuAtomicLoad(GetSleepCounter()))
{
return;
}
if (bits.bAtomicRelease)
{
InternalLTSWakeOne(&this->state_);
}
else
{
InternalLTSWakeAll(&this->state_);
}
}
else
{
this->mutex_.Lock();
this->mutex_.Unlock();
if (bits.bAtomicRelease)
{
this->condition_.Signal();
}
else
{
this->condition_.Broadcast();
}
}
}
bool EventImpl::HasOSHandle(AuMach &mach)
{
mach = 0;
return false;
}
bool EventImpl::HasLockImplementation()
{
return true;
}
void EventImpl::Lock()
{
auto ok = LockNS(0);
SysAssert(ok);
}
void EventImpl::Unlock()
{
// Unlock is always a NOP; inverse of a lock/wait is nothing
}
AuUInt32 *EventImpl::GetSleepCounter()
{
return (AuUInt32 *)&this->condition_;
}
AUKN_SYM IEvent *EventNew(bool bTriggered, bool bAtomicRelease, bool bPermitMultipleTriggers)
{
auto event = _new EventImpl(bTriggered, bAtomicRelease, bPermitMultipleTriggers);
if (!event)
{
return nullptr;
}
if (!event->Init())
{
EventRelease(event);
return nullptr;
}
return event;
}
AUKN_SYM void EventRelease(IEvent *pEvent)
{
AuSafeDelete<EventImpl *>(pEvent);
}
AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, Event, EventImpl, (bool, bTriggered), (bool, bAtomicRelease), (bool, bPermitMultipleTriggers))
}