diff --git a/Source/Threading/Primitives/AuConditionMutex.Generic.hpp b/Source/Threading/Primitives/AuConditionMutex.Generic.hpp index 39f087d3..20426fad 100644 --- a/Source/Threading/Primitives/AuConditionMutex.Generic.hpp +++ b/Source/Threading/Primitives/AuConditionMutex.Generic.hpp @@ -11,6 +11,8 @@ #if defined(AURORA_IS_MODERNNT_DERIVED) #include "AuConditionMutex.NT.hpp" +#elif defined(AURORA_IS_LINUX_DERIVED) + #include "AuConditionMutex.Linux.hpp" #elif defined(AURORA_HAS_PTHREADS) #include "AuConditionMutex.Unix.hpp" #else diff --git a/Source/Threading/Primitives/AuConditionMutex.Linux.cpp b/Source/Threading/Primitives/AuConditionMutex.Linux.cpp new file mode 100644 index 00000000..345b4ee2 --- /dev/null +++ b/Source/Threading/Primitives/AuConditionMutex.Linux.cpp @@ -0,0 +1,108 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuConditionMutex.Linux.cpp + Date: 2023-8-11 + Author: Reece +***/ +#include +#include "AuConditionMutex.Generic.hpp" +#include "SMTYield.hpp" + +#if !defined(_AURUNTIME_GENERICCV) +#include + +namespace Aurora::Threading::Primitives +{ + #define barrier() __asm__ __volatile__("sfence": : :"memory") + #define compilerReorderBarrier() __asm__ __volatile__("": : :"memory") + + LinuxConditionMutex::LinuxConditionMutex() + { + + } + + LinuxConditionMutex::~LinuxConditionMutex() + { + + } + + bool LinuxConditionMutex::TryLock() + { + return DoTryIf([=]() + { + return AuAtomicTestAndSet(&this->uState_, 0) == 0; + }); + } + + void LinuxConditionMutex::Lock() + { + if (TryLock()) + { + return; + } + + AuAtomicAdd(&this->uSleeping_, 1u); + + auto state = this->uState_; + while (!(state == 0 && + AuAtomicCompareExchange(&this->uState_, 1, state) == state)) + { + + int ret {}; + bool bStatus {}; + + do + { + if ((ret = futex_wait(&this->uState_, state)) == 0) + { + bStatus = true; + break; + } + + if (ret == EAGAIN || errno == EAGAIN) + { + bStatus = true; + break; + } + + } + while (ret == EINTR); + + RUNTIME_ASSERT_SHUTDOWN_SAFE(bStatus, "Mutex wait failed: {}", ret) + + state = this->uState_; + } + + AuAtomicSub(&this->uSleeping_, 1u); + } + + void LinuxConditionMutex::Unlock() + { + __sync_lock_release(&this->uState_); + compilerReorderBarrier(); + if (this->uSleeping_) + { + futex_wake(&this->uState_, 1); + } + } + + AuUInt LinuxConditionMutex::GetOSHandle() + { + return AuMach(&this->uState_); + } + + AUKN_SYM IConditionMutex *ConditionMutexNew() + { + return _new LinuxConditionMutex(); + } + + AUKN_SYM void ConditionMutexRelease(IConditionMutex *mutex) + { + AuSafeDelete(mutex); + } + + AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, ConditionMutex, LinuxConditionMutex) +} + +#endif \ No newline at end of file diff --git a/Source/Threading/Primitives/AuConditionMutex.Linux.hpp b/Source/Threading/Primitives/AuConditionMutex.Linux.hpp new file mode 100644 index 00000000..b6388255 --- /dev/null +++ b/Source/Threading/Primitives/AuConditionMutex.Linux.hpp @@ -0,0 +1,30 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuConditionMutex.Linux.hpp + Date: 2023-8-11 + Author: Reece +***/ +#pragma once + +#include "AuIConditionMutexEx.hpp" + +namespace Aurora::Threading::Primitives +{ + struct LinuxConditionMutex : IConditionMutexEx + { + LinuxConditionMutex(); + ~LinuxConditionMutex(); + + bool TryLock() override; + void Lock() override; + void Unlock() override; + AuUInt GetOSHandle() override; + + private: + AuUInt32 uState_ {}; + AuUInt32 uSleeping_ {}; + }; + + using ConditionMutexImpl = LinuxConditionMutex; +} \ No newline at end of file diff --git a/Source/Threading/Primitives/AuConditionMutex.Unix.cpp b/Source/Threading/Primitives/AuConditionMutex.Unix.cpp index c3bfe372..a92107d0 100644 --- a/Source/Threading/Primitives/AuConditionMutex.Unix.cpp +++ b/Source/Threading/Primitives/AuConditionMutex.Unix.cpp @@ -8,7 +8,7 @@ #include #include "AuConditionMutex.Generic.hpp" -#if !defined(_AURUNTIME_GENERICCM) +#if !defined(_AURUNTIME_GENERICCV) && !defined(AURORA_IS_LINUX_DERIVED) #include namespace Aurora::Threading::Primitives diff --git a/Source/Threading/Primitives/AuConditionVariable.Generic.hpp b/Source/Threading/Primitives/AuConditionVariable.Generic.hpp index eb656dfd..8ab25d03 100644 --- a/Source/Threading/Primitives/AuConditionVariable.Generic.hpp +++ b/Source/Threading/Primitives/AuConditionVariable.Generic.hpp @@ -11,6 +11,8 @@ #if defined(AURORA_IS_MODERNNT_DERIVED) #include "AuConditionVariable.NT.hpp" +#elif defined(AURORA_IS_LINUX_DERIVED) + #include "AuConditionVariable.Linux.hpp" #elif defined(AURORA_HAS_PTHREADS) #include "AuConditionVariable.Unix.hpp" #else diff --git a/Source/Threading/Primitives/AuConditionVariable.Linux.cpp b/Source/Threading/Primitives/AuConditionVariable.Linux.cpp new file mode 100644 index 00000000..069acb32 --- /dev/null +++ b/Source/Threading/Primitives/AuConditionVariable.Linux.cpp @@ -0,0 +1,212 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuConditionVariable.Linux.cpp + Date: 2023-8-11 + Author: Reece +***/ +#include +#include "AuConditionVariable.Generic.hpp" +#include +#include "SMTYield.hpp" + +#if !defined(_AURUNTIME_GENERICCV) + +namespace Aurora::Threading::Primitives +{ + ConditionVariableImpl::ConditionVariableImpl(const AuSPtr &pMutex) : + mutex_(AuStaticCast(pMutex)) + { + + } + + ConditionVariableImpl::~ConditionVariableImpl() + { + + } + + AuSPtr ConditionVariableImpl::GetMutex() + { + return this->mutex_; + } + + bool ConditionVariableImpl::WaitForSignal(AuUInt32 qwTimeoutRelative) + { + return WaitForSignalNS(AuMSToNS(qwTimeoutRelative)); + } + + bool ConditionVariableImpl::WaitForSignalNS(AuUInt64 qwTimeoutRelative) + { + return WaitForSignalNsEx(this->mutex_, qwTimeoutRelative); + } + + bool ConditionVariableImpl::WaitOne(AuUInt64 qwTimeoutRelative) + { + AuUInt64 uStart {}; + AuUInt64 uEnd {}; + struct timespec tspec; + + if (qwTimeoutRelative != 0) + { + uStart = AuTime::SteadyClockNS(); + uEnd = uStart + qwTimeoutRelative; + + Time::monoabsns2ts(&tspec, uEnd); + } + + if (DoTryIf([=]() + { + auto old = this->uState_; + return (old != 0 && AuAtomicCompareExchange(&this->uState_, old - 1, old) == old); + })) + { + return true; + } + + auto old = this->uState_; + while (!((old != 0) && + (AuAtomicCompareExchange(&this->uState_, old - 1, old) == old))) + { + if (qwTimeoutRelative != 0) + { + if (Time::SteadyClockNS() >= uEnd) + { + return false; + } + + int ret {}; + do + { + ret = futex_wait(&this->uState_, 0, &tspec); + } + while (ret == EINTR); + } + else + { + int ret {}; + bool bStatus {}; + + do + { + if ((ret = futex_wait(&this->uState_, 0)) == 0) + { + bStatus = true; + continue; + } + + if (ret == EAGAIN || errno == EAGAIN) + { + bStatus = true; + continue; + } + + } + while (ret == EINTR); + + RUNTIME_ASSERT_SHUTDOWN_SAFE(bStatus, "semaphore wait failed: {}", ret) + } + + old = this->uState_; + } + + return true; + } + + bool ConditionVariableImpl::WaitForSignalNsEx(const std::shared_ptr &pMutex, + AuUInt64 qwTimeoutRelative) + { + + AuAtomicAdd(&this->uSleeping_, 1u); + + if (pMutex) + { + pMutex->Unlock(); + } + + auto bSuccess = this->WaitOne(qwTimeoutRelative); + if (!bSuccess) + { + auto uWaiters = this->uSleeping_; + auto uWaitCount = 1; + + while (AuAtomicCompareExchange(&this->uSleeping_, uWaiters - uWaitCount, uWaiters) != uWaiters) + { + uWaiters = this->uSleeping_; + + if (uWaiters == 0) + { + break; + } + } + } + + if (pMutex) + { + pMutex->Lock(); + } + + return bSuccess; + } + + void ConditionVariableImpl::Signal() + { + AuUInt32 uWaitCount {}; + AuUInt32 uWaiters {}; + + uWaiters = this->uSleeping_; + if (uWaiters > 0) + { + AuAtomicAdd(&this->uState_, 1u); + futex_wake(&this->uState_, 1u); + uWaitCount = 1; + } + + while (AuAtomicCompareExchange(&this->uSleeping_, uWaiters - uWaitCount, uWaiters) != uWaiters) + { + uWaiters = this->uSleeping_; + + if (uWaiters == 0) + { + return; + } + } + } + + void ConditionVariableImpl::Broadcast() + { + AuUInt32 uWaitCount {}; + AuUInt32 uWaiters {}; + + uWaiters = this->uSleeping_; + if (uWaiters > 0) + { + AuAtomicAdd(&this->uState_, uWaiters); + futex_wake(&this->uState_, uWaiters); + uWaitCount = uWaiters; + } + + while (AuAtomicCompareExchange(&this->uSleeping_, uWaiters - uWaitCount, uWaiters) != uWaiters) + { + uWaiters = this->uSleeping_; + + if (uWaiters <= uWaitCount) + { + uWaitCount = uWaiters; + } + } + } + + AUKN_SYM IConditionVariable *ConditionVariableNew(const AuSPtr &mutex) + { + return _new ConditionVariableImpl(mutex); + } + + AUKN_SYM void ConditionVariableRelease(IConditionVariable *mutex) + { + AuSafeDelete(mutex); + } + + AUROXTL_INTERFACE_SOO_SRC(ConditionVariable, ConditionVariableImpl, (const AuSPtr &, pMutex)) +} + +#endif \ No newline at end of file diff --git a/Source/Threading/Primitives/AuConditionVariable.Linux.hpp b/Source/Threading/Primitives/AuConditionVariable.Linux.hpp new file mode 100644 index 00000000..1782bc60 --- /dev/null +++ b/Source/Threading/Primitives/AuConditionVariable.Linux.hpp @@ -0,0 +1,43 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuConditionVariable.Linux.hpp + Date: 2023-8-11 + Author: Reece +***/ +#pragma once + +#if !defined(_AURUNTIME_GENERICCV) +#include "AuConditionMutex.Linux.hpp" + +namespace Aurora::Threading::Primitives +{ + struct ConditionVariableImpl final : IConditionVariable + { + ConditionVariableImpl(const AuSPtr &mutex); + ~ConditionVariableImpl(); + + AuSPtr GetMutex() override; + bool WaitForSignal(AuUInt32 timeout) override; + bool WaitForSignalNsEx(const std::shared_ptr &pMutex, AuUInt64 timeout); + bool WaitForSignalNS(AuUInt64 qwTimeout) override; + void Signal() override; + void Broadcast() override; + bool WaitOne(AuUInt64 qwTimeout); + + private: + AuUInt32 uState_ {}; + AuUInt32 uSleeping_ {}; + AuSPtr mutex_; + }; + + struct CondVarDummy : IConditionVariable + { + AuUInt32 uState_ {}; + AuUInt32 uSleeping_ {}; + }; + + static const auto kSizeOfDummyCondVar = sizeof(CondVarDummy); + +} +#endif \ No newline at end of file diff --git a/Source/Threading/Primitives/AuConditionVariable.Unix.cpp b/Source/Threading/Primitives/AuConditionVariable.Unix.cpp index bb415669..47b63ad8 100644 --- a/Source/Threading/Primitives/AuConditionVariable.Unix.cpp +++ b/Source/Threading/Primitives/AuConditionVariable.Unix.cpp @@ -9,7 +9,7 @@ #include "AuConditionVariable.Generic.hpp" #include -#if !defined(_AURUNTIME_GENERICCV) +#if !defined(_AURUNTIME_GENERICCV) && !defined(AURORA_IS_LINUX_DERIVED) namespace Aurora::Threading::Primitives {