AuroraRuntime/Source/Threading/Primitives/AuConditionVariable.NT.cpp

158 lines
4.4 KiB
C++

/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuConditionVariable.Win32.cpp
Date: 2021-6-12
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuConditionVariable.Generic.hpp"
#include <Time/Time.hpp>
#include "SMPYield.hpp"
#if !defined(_AURUNTIME_GENERICCV)
#if !defined(NTSTATUS_TIMEOUT)
#define NTSTATUS_TIMEOUT 0x102
#endif
namespace Aurora::Threading::Primitives
{
ConditionVariableImpl::ConditionVariableImpl(const AuSPtr<IConditionMutex> &pMutex) :
mutex_(AuStaticCast<Win32ConditionMutex>(pMutex))
{
}
AuSPtr<IConditionMutex> ConditionVariableImpl::GetMutex()
{
return mutex_;
}
bool ConditionVariableImpl::WaitForSignal(AuUInt32 uTimeout)
{
return WaitForSignalNS(AuMSToNS<AuUInt64>(uTimeout));
}
bool ConditionVariableImpl::WaitForSignalNS(AuUInt64 qwTimeout)
{
bool bRet { true };
auto pThatMutex = reinterpret_cast<NT4Mutex *>(&this->mutex_->lock_);
this->mutex_->Unlock();
if (qwTimeout)
{
auto uEndTimeSteady = AuTime::SteadyClockNS() + qwTimeout;
auto uEndTimeWall = AuTime::CurrentClockNS() + qwTimeout;
auto uTargetTimeNt = AuTime::ConvertTimestampNs(uEndTimeWall);
LARGE_INTEGER word;
word.QuadPart = uTargetTimeNt;
AuAtomicAdd(&this->wlist, 1u);
bRet = pNtWaitForKeyedEvent(gKeyedEventHandle, &this->wlist, 0, &word) != NTSTATUS_TIMEOUT;
if (!bRet)
{
AuAtomicAdd(&this->expander, 1u);
}
}
else
{
AuAtomicAdd(&this->wlist, 1u);
pNtWaitForKeyedEvent(gKeyedEventHandle, &this->wlist, 0, nullptr);
}
this->mutex_->Lock();
return bRet;
}
void ConditionVariableImpl::Signal()
{
auto expected = this->wlist;
AuUInt32 uExpander { this->expander };
if (expected || uExpander)
{
AuUInt32 uOffset { 0 };
while (uExpander && AuAtomicCompareExchange(&this->expander, 0u, uExpander) != uExpander)
{
SMPPause();
uExpander = this->expander;
}
if (uExpander)
{
expected = this->wlist;
while (expected && AuAtomicCompareExchange(&this->wlist, expected - uExpander, expected) != expected)
{
SMPPause();
expected = this->wlist;
}
}
expected = this->wlist;
while (expected)
{
if (AuAtomicCompareExchange(&this->wlist, expected - 1, expected) == expected)
{
pNtReleaseKeyedEvent(gKeyedEventHandle, &this->wlist, FALSE, nullptr);
}
expected = this->wlist;
}
}
}
void ConditionVariableImpl::Broadcast()
{
auto expected = this->wlist;
AuUInt32 uExpander { this->expander };
while (expected || uExpander)
{
AuUInt32 uOffset { 0 };
if (uExpander && AuAtomicCompareExchange(&this->expander, 0u, uExpander) != uExpander)
{
SMPPause();
uExpander = this->expander;
expected = this->wlist;
continue;
}
if (uExpander)
{
expected = this->wlist;
while (AuAtomicCompareExchange(&this->wlist, expected - uExpander, expected) != expected)
{
SMPPause();
expected = this->wlist;
}
}
expected = this->wlist;
while (expected)
{
if (AuAtomicCompareExchange(&this->wlist, expected - 1, expected) == expected)
{
pNtReleaseKeyedEvent(gKeyedEventHandle, &this->wlist, FALSE, nullptr);
}
expected = this->wlist;
}
}
}
AUKN_SYM IConditionVariable *ConditionVariableNew(const AuSPtr<IConditionMutex> &pMutex)
{
return _new ConditionVariableImpl(pMutex);
}
AUKN_SYM void ConditionVariableRelease(IConditionVariable *pCV)
{
AuSafeDelete<ConditionVariableImpl *>(pCV);
}
}
#endif