348 lines
8.2 KiB
C++
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))
|
|
} |