[+] New generic primitives

[-] AuSemaphore.Unix.hpp
[*] Moved the old pthread based primitives to _removed/*.bak
[+] AuWoASemaphore.Unix.cpp
[+] AuWoASemaphore.Unix.hpp
This commit is contained in:
Reece Wilson 2023-12-29 15:35:25 +00:00
parent f404e8960f
commit 5862dbeacc
23 changed files with 1066 additions and 410 deletions

View File

@ -19,21 +19,21 @@ namespace Aurora::Threading::Primitives
// Define all the sizes of the primitives on each platform by word-size, preprocessed to filter irrelevant/unknown architectures:
#if defined(AURORA_ARCH_X64) || defined(AURORA_ARCH_X86)
static const auto kPrimitiveSize64NTMutex = 16;
static const auto kPrimitiveSize64NTSemaphore = 24;
static const auto kPrimitiveSize64NTCS = 32;
static const auto kPrimitiveSize64NTEvent = 24;
static const auto kPrimitiveSize64NTRWLock = 56;
static const auto kPrimitiveSize64NTCond = 32;
static const auto kPrimitiveSize64NTCondMutex = 16;
static const auto kPrimitiveSize64NTMutex = 12;
static const auto kPrimitiveSize64NTSemaphore = 24;
static const auto kPrimitiveSize64NTCS = 28;
static const auto kPrimitiveSize64NTEvent = 24;
static const auto kPrimitiveSize64NTRWLock = 56;
static const auto kPrimitiveSize64NTCond = 32;
static const auto kPrimitiveSize64NTCondMutex = 16;
static const auto kPrimitiveSize32NTMutex = 8;
static const auto kPrimitiveSize32NTSemaphore = 20;
static const auto kPrimitiveSize32NTCS = 20;
static const auto kPrimitiveSize32NTEvent = 20;
static const auto kPrimitiveSize32NTRWLock = 44;
static const auto kPrimitiveSize32NTCond = 20;
static const auto kPrimitiveSize32NTCondMutex = 8;
static const auto kPrimitiveSize32NTMutex = 8;
static const auto kPrimitiveSize32NTSemaphore = 20;
static const auto kPrimitiveSize32NTCS = 20;
static const auto kPrimitiveSize32NTEvent = 20;
static const auto kPrimitiveSize32NTRWLock = 44;
static const auto kPrimitiveSize32NTCond = 20;
static const auto kPrimitiveSize32NTCondMutex = 8;
static const auto kPrimitiveSize64LinuxMutex = 16;
static const auto kPrimitiveSize64LinuxSemaphore = 16;
@ -48,7 +48,7 @@ namespace Aurora::Threading::Primitives
static const auto kPrimitiveSize32LinuxSemaphore = 12;
static const auto kPrimitiveSize32LinuxCS = 32;
static const auto kPrimitiveSize32LinuxEvent = 32;
static const auto kPrimitiveSize32LinuxRWLock = 88;
static const auto kPrimitiveSize32LinuxRWLock = 64;
static const auto kPrimitiveSize32LinuxCond = 32;
static const auto kPrimitiveSize32LinuxCondMutex = 12;
@ -137,6 +137,12 @@ namespace Aurora::Threading::Primitives
static const auto kPrimitiveSizeSemaphoreCV = AuPageRoundUp<AuUInt>(8 + kPrimitiveSizeSemaphore + 4, sizeof(AuUInt));
#if (defined(AURORA_ARCH_X64) || defined(AURORA_ARCH_X86))
#pragma pack(push)
#pragma pack(4)
#endif
struct AUKN_SYM HyperWaitable : IWaitable
{
auline inline void Lock() final override
@ -155,6 +161,10 @@ namespace Aurora::Threading::Primitives
AuAUInt32 state_ {};
};
#if (defined(AURORA_ARCH_X64) || defined(AURORA_ARCH_X86))
#pragma pack(pop)
#endif
#if defined(AURT_ENABLE_HYPER_MUTEX)
using IHyperWaitable = HyperWaitable;
#else

View File

@ -7,45 +7,197 @@
***/
#include <Source/RuntimeInternal.hpp>
#include "AuConditionMutex.Generic.hpp"
#include "../AuWakeInternal.hpp"
#include "SMTYield.hpp"
#if defined(_AURUNTIME_GENERICCM)
namespace Aurora::Threading::Primitives
{
ConditionMutexImpl::ConditionMutexImpl()
GenericConditionMutex::GenericConditionMutex()
{
}
ConditionMutexImpl::~ConditionMutexImpl()
GenericConditionMutex::~GenericConditionMutex()
{
}
void ConditionMutexImpl::Lock()
{
this->mutex_.Lock();
bool GenericConditionMutex::TryLock()
{
if (ThrdCfg::gPreferLinuxCondMutexSpinTryLock)
{
return this->TryLockHeavy();
}
else
{
return this->TryLockNoSpin();
}
}
void ConditionMutexImpl::Unlock()
void GenericConditionMutex::Lock()
{
this->mutex_.Unlock()
if (ThrdCfg::gPreferUnixPrimitivesNoSpin)
{
if (this->TryLockNoSpin())
{
return;
}
}
else
{
if (this->TryLockHeavy())
{
return;
}
}
AuAtomicAdd(&this->uSleeping_, 1u);
while (!this->TryLockNoSpin())
{
static const AuUInt32 kRef { 1 };
(void)InternalLTSWaitOnAddressHighRes(&this->uState_, &kRef, 4, 0);
}
AuAtomicSub(&this->uSleeping_, 1u);
}
AuUInt ConditionMutexImpl::GetOSHandle()
void GenericConditionMutex::Unlock()
{
AuMach handle = 0;
SysAssertExp(this->mutex_.HasOSHandle(handle));
return handle;
AuAtomicClearU8Lock(&this->uState_);
if (AuAtomicLoad(&this->uSleeping_))
{
InternalLTSWakeOne(&this->uState_);
}
}
bool GenericConditionMutex::TryLockNoSpin()
{
return AuAtomicTestAndSet(&this->uState_, 0) == 0;
}
bool GenericConditionMutex::TryLockHeavy()
{
return DoTryIf([=]()
{
return this->TryLockNoSpin();
});
}
bool GenericConditionMutex::LockAbsNS(AuUInt64 uEndTime)
{
bool bStatus { true };
if (this->TryLock())
{
return true;
}
AuAtomicAdd(&this->uSleeping_, 1u);
while (!this->TryLockNoSpin())
{
static const AuUInt32 kRef { 1 };
if (uEndTime != 0)
{
if (!InternalLTSWaitOnAddressHighRes(&this->uState_, &kRef, 4, uEndTime))
{
bStatus = false;
break;
}
}
else
{
(void)InternalLTSWaitOnAddressHighRes(&this->uState_, &kRef, 4, uEndTime);
}
}
AuAtomicSub(&this->uSleeping_, 1u);
return bStatus;
}
bool GenericConditionMutex::LockNS(AuUInt64 uTimeout)
{
AuUInt64 uEndTime {};
bool bStatus { true };
if (this->TryLock())
{
return true;
}
if (uTimeout != 0)
{
uEndTime = AuTime::SteadyClockNS() + uTimeout;
}
if (ThrdCfg::gPreferUnixPrimitivesNoSpin)
{
if (this->TryLockNoSpin())
{
return true;
}
}
else
{
if (this->TryLockHeavy())
{
return true;
}
}
AuAtomicAdd(&this->uSleeping_, 1u);
while (!this->TryLockNoSpin())
{
static const AuUInt32 kRef { 1 };
if (uTimeout != 0)
{
if (!InternalLTSWaitOnAddressHighRes(&this->uState_, &kRef, 4, uEndTime))
{
bStatus = false;
break;
}
}
else
{
(void)InternalLTSWaitOnAddressHighRes(&this->uState_, &kRef, 4, uEndTime);
}
}
AuAtomicSub(&this->uSleeping_, 1u);
return bStatus;
}
bool GenericConditionMutex::LockAbsMS(AuUInt64 timeout)
{
return this->LockAbsNS(AuMSToNS<AuUInt64>(timeout));
}
bool GenericConditionMutex::LockMS(AuUInt64 timeout)
{
return this->LockNS(AuMSToNS<AuUInt64>(timeout));
}
AuUInt GenericConditionMutex::GetOSHandle()
{
return 0;
}
AUKN_SYM IConditionMutex *ConditionMutexNew()
{
return _new ConditionMutexImpl();
return _new ConditionMutexGenericImpl();
}
AUKN_SYM void ConditionMutexRelease(IConditionMutex *pMutex)
{
AuSafeDelete<ConditionMutexImpl *>(pMutex);
AuSafeDelete<ConditionMutexGenericImpl *>(pMutex);
}
AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, ConditionMutex, ConditionMutexGenericImpl)
}
#endif

View File

@ -13,8 +13,6 @@
#include "AuConditionMutex.NT.hpp"
#elif defined(AURORA_IS_LINUX_DERIVED)
#include "AuConditionMutex.Linux.hpp"
#elif defined(AURORA_HAS_PTHREADS)
#include "AuConditionMutex.Unix.hpp"
#else
#define _AURUNTIME_GENERICCM
@ -22,18 +20,57 @@
namespace Aurora::Threading::Primitives
{
class ConditionMutexImpl : public IConditionMutex
{
public:
UnixConditionMutex();
~UnixConditionMutex();
#pragma pack(push)
#pragma pack(4)
struct GenericConditionMutex final
{
GenericConditionMutex();
~GenericConditionMutex();
bool TryLock();
void Lock();
void Unlock();
AuUInt GetOSHandle();
bool LockMS(AuUInt64 timeout);
bool LockNS(AuUInt64 timeout);
bool LockAbsMS(AuUInt64 timeout);
bool LockAbsNS(AuUInt64 timeout);
auline bool TryLockNoSpin();
auline bool TryLockHeavy();
void Lock() override;
void Unlock() override;
AuUInt GetOSHandle() override;
private:
Mutex mutex_;
AuUInt32 uState_ {};
AuUInt32 uSleeping_ {};
};
#pragma pack(pop)
using ConditionMutexInternal = GenericConditionMutex;
struct ConditionMutexGenericImpl final : IConditionMutexEx
{
auline bool TryLock()
{
return mutex.TryLock();
}
auline void Lock()
{
mutex.Unlock();
}
auline void Unlock()
{
mutex.Unlock();
}
inline AuUInt GetOSHandle()
{
return mutex.GetOSHandle();
}
ConditionMutexInternal mutex;
};
}
#endif

View File

@ -8,88 +8,177 @@
#include <Source/RuntimeInternal.hpp>
#include <Source/Threading/AuWaitFor.hpp>
#include "AuConditionVariable.Generic.hpp"
#include "../AuWakeInternal.hpp"
#include "SMTYield.hpp"
#if defined(_AURUNTIME_GENERICCV)
namespace Aurora::Threading::Primitives
{
ConditionVariableImpl::ConditionVariableImpl(const AuSPtr<IConditionMutex> &mutex) : mutex_(AuDynamicCast<ConditionMutexImpl>(mutex))
ConditionVariableGeneric::ConditionVariableGeneric()
{
}
AuSPtr<IConditionMutex> ConditionVariableImpl::GetMutex()
ConditionVariableGeneric::~ConditionVariableGeneric()
{
return mutex_;
}
bool ConditionVariableImpl::WaitForSignal(AuUInt32 timeout)
bool ConditionVariableGeneric::WaitForSignalNsEx(GenericConditionMutex *pMutex, AuUInt64 qwTimeoutRelative, bool bSpin)
{
// yield then wake up spuriously - this is should be illegal, but works well enough as a place holder
mutex_->Unlock();
for (int i = 0; i < 20; i++)
AuAtomicAdd(&this->uSleeping_, 1u);
if (pMutex)
{
YieldToOtherThread();
pMutex->Unlock();
}
mutex_->Lock();
/**
TODO: A more technically competent solution may be more sophisticated than anything proposed here,
but the following seems good enough.
It's same thing without the spurious wake ups and hopefully scalable timeout-relative yielding
auto bSuccess = this->WaitOne(qwTimeoutRelative, bSpin);
if (!bSuccess)
{
auto uWaiters = this->uSleeping_;
auto uWaitCount = 1;
while (uWaiters &&
AuAtomicCompareExchange(&this->uSleeping_, uWaiters - uWaitCount, uWaiters) != uWaiters)
{
uWaiters = this->uSleeping_;
if (uWaiters == 0)
{
break;
}
}
}
if (pMutex)
{
pMutex->Lock();
}
return bSuccess;
}
void ConditionVariableGeneric::Signal()
{
AuUInt32 uWaiters {};
uWaiters = AuAtomicLoad(&this->uSleeping_);
if (uWaiters == 0)
{
return;
}
AuAtomicAdd(&this->uState_, 1u);
InternalLTSWakeOne(&this->uState_);
while (uWaiters &&
AuAtomicCompareExchange(&this->uSleeping_, uWaiters - 1u, uWaiters) != uWaiters)
{
uWaiters = this->uSleeping_;
if (uWaiters == 0)
{
return;
}
}
}
void ConditionVariableGeneric::Broadcast()
{
AuUInt32 uWaitCount {};
AuUInt32 uWaiters {};
while ((uWaiters = AuAtomicLoad(&this->uSleeping_)))
{
AuAtomicAdd(&this->uState_, uWaiters);
InternalLTSWakeCount(&this->uState_, uWaiters);
uWaitCount = uWaiters;
while (uWaiters &&
AuAtomicCompareExchange(&this->uSleeping_, uWaiters - uWaitCount, uWaiters) != uWaiters)
{
uWaiters = AuAtomicLoad(&this->uSleeping_);
if (uWaiters <= uWaitCount)
{
uWaitCount = uWaiters;
}
}
}
}
bool ConditionVariableGeneric::WaitOne(AuUInt64 qwTimeoutRelative, bool bSpin)
{
AuUInt64 uStart {};
AuUInt64 uEnd {};
if (qwTimeoutRelative != 0)
{
uStart = AuTime::SteadyClockNS();
uEnd = uStart + qwTimeoutRelative;
}
if (bSpin ? this->TryTakeOneSpin() : this->TryTakeOneNoSpin())
{
return true;
}
auto old = this->uState_;
while (!((old != 0) &&
(AuAtomicCompareExchange(&this->uState_, old - 1, old) == old)))
{
static const AuUInt32 kRef { 0 };
if (qwTimeoutRelative != 0)
{
if (!InternalLTSWaitOnAddressHighRes(&this->uState_, &kRef, 4, uEnd))
{
return false;
}
}
else
{
(void)InternalLTSWaitOnAddressHighRes(&this->uState_, &kRef, 4, uEnd);
}
old = this->uState_;
}
wait ->
waiting_++
WaitFor([]() {
auto old = signals_.load(std::memory_order_relaxed);
return old != 0 && signals_.compare_exchange_strong(old, old - 1);
})
*/
return true;
}
void ConditionVariableImpl::Signal()
bool ConditionVariableGeneric::TryTakeOneNoSpin()
{
signals_++;
waiting_--;
auto old = this->uState_;
return old != 0 && AuAtomicCompareExchange(&this->uState_, old - 1, old) == old;
}
void ConditionVariableImpl::Broadcast()
bool ConditionVariableGeneric::TryTakeOneSpin()
{
auto initwaiting = waiting_.load(std::memory_order_relaxed);
if (initwaiting < 0) return;
signals_ += initwaiting;
#if 0
auto old = initwaiting;
if (!old) return;
while (!(waiting_.compare_exchange_strong(old, old - initwaiting)))
if (ThrdCfg::gPreferUnixPrimitivesNoSpin)
{
old = waiting_.load(std::memory_order_relaxed);
if (!old) return;
return this->TryTakeOneNoSpin();
}
#else
waiting_ -= initwaiting;
// is it possible to go negative on waiters? yes!
// is it a big deal? well, no actually
// the only way this can happen is if we have above 0 signals and no threads waiting
// it should sort itself out
#endif
return DoTryIf([=]()
{
return this->TryTakeOneNoSpin();
});
}
AUKN_SYM IConditionVariable *ConditionVariableNew(const AuSPtr<IConditionMutex> &mutex)
AUKN_SYM IConditionVariable *ConditionVariableNew(const AuSPtr<IConditionMutex> &pMutex)
{
return _new ConditionVariableImpl(mutex);
return _new ConditionVariableGenericImpl(pMutex);
}
AUKN_SYM void ConditionVariableRelease(IConditionVariable *mutex)
AUKN_SYM void ConditionVariableRelease(IConditionVariable *pCV)
{
AuSafeDelete<ConditionVariableImpl *>(mutex);
AuSafeDelete<ConditionVariableGenericImpl *>(pCV);
}
AUROXTL_INTERFACE_SOO_SRC(ConditionVariable, ConditionVariableGenericImpl, (const AuSPtr<IConditionMutex> &, pMutex))
}
#endif

View File

@ -13,27 +13,75 @@
#include "AuConditionVariable.NT.hpp"
#elif defined(AURORA_IS_LINUX_DERIVED)
#include "AuConditionVariable.Linux.hpp"
#elif defined(AURORA_HAS_PTHREADS)
#include "AuConditionVariable.Unix.hpp"
#else
#define _AURUNTIME_GENERICCV
#include "AuConditionMutex.Generic.hpp"
namespace Aurora::Threading::Primitives
{
struct ConditionVariableImpl : IConditionVariable
#pragma pack(push)
#pragma pack(4)
struct ConditionVariableGeneric final
{
ConditionVariableImpl(const AuSPtr<IConditionMutex> &mutex);
AuSPtr<IConditionMutex> GetMutex() override;
bool WaitForSignal(AuUInt32 timeout) override;
void Signal() override;
void Broadcast() override;
ConditionVariableGeneric();
~ConditionVariableGeneric();
bool WaitForSignalNsEx(GenericConditionMutex *pMutex, AuUInt64 timeout, bool bSpin = true);
void Signal();
void Broadcast();
bool WaitOne(AuUInt64 qwTimeout, bool bSpin);
private:
std::atomic_int signals_, waiting_;
AuSPtr<ConditionMutexImpl> mutex_;
bool TryTakeOneNoSpin();
bool TryTakeOneSpin();
AuUInt32 uState_ {};
AuUInt32 uSleeping_ {};
};
#pragma pack(pop)
using ConditionVariableInternal = ConditionVariableGeneric;
struct ConditionVariableGenericImpl final : IConditionVariable
{
inline ConditionVariableGenericImpl(const AuSPtr<IConditionMutex> &mutex) :
mutex(mutex)
{ }
auline AuSPtr<IConditionMutex> GetMutex() override
{
return mutex;
}
auline bool WaitForSignal(AuUInt32 timeout) override
{
return cond.WaitForSignalNsEx(&std::static_pointer_cast<ConditionMutexGenericImpl>(this->mutex)->mutex, AuMSToNS<AuUInt64>(timeout));
}
auline bool WaitForSignalNS(AuUInt64 qwTimeout) override
{
return cond.WaitForSignalNsEx(&std::static_pointer_cast<ConditionMutexGenericImpl>(this->mutex)->mutex, qwTimeout);
}
inline bool WaitForSignalNsEx(const std::shared_ptr<GenericConditionMutex> &pMutex, AuUInt64 timeout)
{
return cond.WaitForSignalNsEx(pMutex.get(), timeout);
}
auline void Signal() override
{
cond.Signal();
}
auline void Broadcast() override
{
cond.Broadcast();
}
ConditionVariableGeneric cond;
std::shared_ptr<IConditionMutex> mutex;
};
static const auto kSizeOfDummyCondVar = sizeof(ConditionVariableInternal);
}
#endif

View File

@ -7,6 +7,7 @@
***/
#pragma once
#include "AuConditionVariable.Generic.hpp"
#if !defined(_AURUNTIME_GENERICCV)
#include "AuConditionMutex.NT.hpp"

View File

@ -7,65 +7,178 @@
***/
#include <Source/RuntimeInternal.hpp>
#include "AuMutex.Generic.hpp"
#include "../AuWakeInternal.hpp"
#include "SMTYield.hpp"
#if defined(_AURUNTIME_GENERICMUTEX)
#if defined(_AURUNTIME_GENERIC_MUTEX)
namespace Aurora::Threading::Primitives
{
MutexImpl::MutexImpl()
MutexGenericImpl::MutexGenericImpl()
{
value_ = 0;
this->state_ = 0;
}
MutexImpl::~MutexImpl()
MutexGenericImpl::~MutexGenericImpl()
{
value_ = 0;
}
bool MutexImpl::HasOSHandle(AuMach &mach)
bool MutexGenericImpl::HasOSHandle(AuMach &mach)
{
return false;
}
bool MutexImpl::TryLock()
bool MutexGenericImpl::HasLockImplementation()
{
#if defined(_AU_HAS_ATOMIC_INTRINS)
return _interlockedbittestandset(&value_, 0) == 0;
#else
return value_.fetch_or(1);
#endif
return true;
}
bool MutexImpl::HasLockImplementation()
bool MutexGenericImpl::TryLock()
{
return false;
if (ThrdCfg::gPreferUnixPrimitivesNoSpin)
{
return this->TryLockNoSpin();
}
else
{
return this->TryLockHeavy();
}
}
void MutexImpl::Lock()
bool MutexGenericImpl::TryLockNoSpin()
{
auto status = Lock(0);
SysAssert(status, "Couldn't lock Mutex object");
return AuAtomicTestAndSet(&this->state_, 0) == 0;
}
bool MutexImpl::Lock(AuUInt64 timeout)
bool MutexGenericImpl::TryLockHeavy()
{
SysAssertExp(!HasLockImplementation());
return Aurora::Threading::WaitFor(this, timeout);
return DoTryIf([=]()
{
return this->TryLockNoSpin();
});
}
void MutexImpl::Unlock()
bool MutexGenericImpl::LockMS(AuUInt64 uTimeout)
{
value_ = 0;
return this->LockNS(AuMSToNS<AuUInt64>(uTimeout));
}
bool MutexGenericImpl::LockNS(AuUInt64 uTimeout)
{
AuUInt64 uStart {};
AuUInt64 uEnd {};
bool bStatus { true };
if (this->TryLockNoSpin())
{
return true;
}
if (uTimeout != 0)
{
uStart = AuTime::SteadyClockNS();
uEnd = uStart + uTimeout;
}
if (this->TryLock())
{
return true;
}
AuAtomicAdd(&this->dwSleeping_, 1u);
while (!this->TryLockNoSpin())
{
static const AuUInt32 kRef { 1 };
if (uTimeout != 0)
{
if (!InternalLTSWaitOnAddressHighRes((void *)&this->state_, &kRef, 4, uEnd))
{
bStatus = false;
break;
}
}
else
{
(void)InternalLTSWaitOnAddressHighRes((void *)&this->state_, &kRef, 4, uEnd);
}
}
AuAtomicSub(&this->dwSleeping_, 1u);
return bStatus;
}
bool MutexGenericImpl::LockAbsNS(AuUInt64 uEndTime)
{
bool bStatus { true };
if (ThrdCfg::gPreferLinuxPrimitivesFutexNoSpin)
{
if (this->TryLockNoSpin())
{
return true;
}
}
else
{
if (this->TryLockHeavy())
{
return true;
}
}
AuAtomicAdd(&this->dwSleeping_, 1u);
while (!this->TryLockNoSpin())
{
static const AuUInt32 kRef { 1 };
if (uEndTime != 0)
{
if (!InternalLTSWaitOnAddressHighRes((void *)&this->state_, &kRef, 4, uEndTime))
{
bStatus = false;
break;
}
}
else
{
(void)InternalLTSWaitOnAddressHighRes((void *)&this->state_, &kRef, 4, uEndTime);
}
}
AuAtomicSub(&this->dwSleeping_, 1u);
return true;
}
void MutexGenericImpl::SlowLock()
{
(void)this->LockAbsNS(0);
}
void MutexGenericImpl::Unlock()
{
AuAtomicClearU8Lock(&this->state_);
if (AuAtomicLoad(&this->dwSleeping_))
{
InternalLTSWakeOne((void *)&this->state_);
}
}
AUKN_SYM IHyperWaitable *MutexNew()
{
return _new MutexGenericImpl();
}
AUKN_SYM void MutexRelease(IHyperWaitable *pMutex)
{
AuSafeDelete<MutexGenericImpl *>(pMutex);
}
AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, Mutex, MutexGenericImpl)
AUKN_SYM IWaitable *MutexNew()
{
return _new Mutex();
}
AUKN_SYM void MutexRelease(IWaitable *pMutex)
{
AuSafeDelete<Mutex *>(pMutex);
}
}
#endif

View File

@ -11,33 +11,32 @@
#include "AuMutex.NT.hpp"
#elif defined(AURORA_IS_LINUX_DERIVED)
#include "AuMutex.Linux.hpp"
#elif defined(AURORA_HAS_PTHREADS)
#include "AuMutex.Unix.hpp"
#else
#define _AURUNTIME_GENERIC_MUTEX
namespace Aurora::Threading::Primitives
{
class Mutex : public IWaitable
struct MutexGenericImpl final : IHyperWaitable
{
public:
Mutex();
~Mutex();
MutexGenericImpl();
~MutexGenericImpl();
bool HasOSHandle(AuMach &mach) override;
bool TryLock() override;
bool HasLockImplementation() override;
void Lock() override;
bool Lock(AuUInt64 timeout) override;
void SlowLock() override;
bool LockMS(AuUInt64 timeout) override;
bool LockNS(AuUInt64 timeout) override;
bool LockAbsNS(AuUInt64 uTimeout) override;
void Unlock() override;
auline bool TryLockNoSpin();
auline bool TryLockHeavy();
private:
#if defined(_AU_HAS_ATOMIC_INTRINS)
volatile AuAtomicInt value_{};
#else
std::atomic_long value_;
#endif
AuUInt32 dwSleeping_ {};
};
using MutexImpl = MutexGenericImpl;
}
#endif

View File

@ -16,7 +16,7 @@ namespace Aurora::Threading::Primitives
{
MutexImpl::MutexImpl()
{
this->state_ = 0;
}
MutexImpl::~MutexImpl()

View File

@ -10,7 +10,7 @@
#include "SMTYield.hpp"
#include "../AuWakeInternal.hpp"
#if !defined(_AURUNTIME_GENERICMUTEX)
#if !defined(_AURUNTIME_GENERIC_MUTEX)
#include "AuMutex.NT.hpp"
#include "AuConditionMutex.NT.hpp"
#include <Time/Time.hpp>

View File

@ -7,65 +7,170 @@
***/
#include <Source/RuntimeInternal.hpp>
#include "AuSemaphore.Generic.hpp"
#include "../AuWakeInternal.hpp"
#include "SMTYield.hpp"
#if defined(_AURUNTIME_GENERIC_SEMAPHORE)
namespace Aurora::Threading::Primitives
{
SemaphoreImpl::SemaphoreImpl(long intialValue)
SemaphoreGeneric::SemaphoreGeneric(AuUInt16 uIntialValue) :
uAtomicState(uIntialValue),
uAtomicSleeping(0)
{
value_ = intialValue;
}
SemaphoreImpl::~SemaphoreImpl()
SemaphoreGeneric::~SemaphoreGeneric()
{
}
bool SemaphoreImpl::HasOSHandle(AuMach &mach)
bool SemaphoreGeneric::TryLockNoSpin()
{
mach = reinterpret_cast<AuMach>(value_);
return true;
auto uState = this->uAtomicState;
return (uState != 0 && AuAtomicCompareExchange(&this->uAtomicState, uState - 1, uState) == uState);
}
bool SemaphoreImpl::HasLockImplementation()
bool SemaphoreGeneric::TryLockHeavy()
{
return DoTryIf([=]()
{
return this->TryLockNoSpin();
});
}
bool SemaphoreGeneric::TryLock()
{
if (ThrdCfg::gPreferUnixPrimitivesNoSpin)
{
return this->TryLockNoSpin();
}
else
{
return this->TryLockHeavy();
}
}
bool SemaphoreGeneric::HasOSHandle(AuMach &mach)
{
return false;
}
bool SemaphoreImpl::TryLock()
bool SemaphoreGeneric::HasLockImplementation()
{
auto old = value_.load(std::memory_order_relaxed);
return old != 0 && value_.compare_exchange_strong(old, old - 1);
}
bool SemaphoreImpl::Lock(AuUInt64 timeout)
{
return WaitFor(this, timeout);
}
void SemaphoreImpl::Lock()
{
auto status = Lock(0);
SysAssert(status, "Couldn't lock semaphore");
}
void SemaphoreImpl::Unlock(AuUInt16 count)
{
value_.fetch_add(count);
}
void SemaphoreImpl::Unlock()
{
return Unlock(1);
return true;
}
AUKN_SYM ISemaphore *SemaphoreNew(int initialCount)
void SemaphoreGeneric::Unlock()
{
return _new Semaphore(initialCount);
this->Unlock(1);
}
void SemaphoreGeneric::Unlock(AuUInt16 uThreads)
{
AuAtomicAdd(&this->uAtomicState, AuUInt32(uThreads));
if (auto uSleeping = AuAtomicLoad(&this->uAtomicSleeping))
{
InternalLTSWakeCount((const void *)&this->uAtomicState, AuMin(uSleeping, uThreads));
}
}
void SemaphoreGeneric::Lock()
{
static const AuUInt32 kRef { 0 };
if (this->TryLock())
{
return;
}
while (!this->TryLockNoSpin())
{
AuAtomicAdd(&this->uAtomicSleeping, 1u);
InternalLTSWaitOnAddressHighRes((const void *)&this->uAtomicState, &kRef, sizeof(kRef), 0);
AuAtomicSub(&this->uAtomicSleeping, 1u);
}
}
bool SemaphoreGeneric::LockMS(AuUInt64 qwTimeout)
{
return LockNS(AuMSToNS<AuUInt64>(qwTimeout));
}
bool SemaphoreGeneric::LockAbsMS(AuUInt64 qwTimeout)
{
return LockAbsNS(AuMSToNS<AuUInt64>(qwTimeout));
}
bool SemaphoreGeneric::LockNS(AuUInt64 qwTimeout)
{
static const AuUInt32 kRef { 0 };
if (this->TryLockNoSpin())
{
return true;
}
auto qwEndTime = qwTimeout ?
Time::SteadyClockNS() + qwTimeout :
0;
if (!ThrdCfg::gPreferUnixPrimitivesNoSpin)
{
if (this->TryLockHeavy())
{
return true;
}
}
while (!this->TryLockNoSpin())
{
bool bStatus {};
AuAtomicAdd(&this->uAtomicSleeping, 1u);
bStatus = InternalLTSWaitOnAddressHighRes((const void *)&this->uAtomicState, &kRef, sizeof(kRef), qwEndTime);
AuAtomicSub(&this->uAtomicSleeping, 1u);
if (!bStatus)
{
return TryLockNoSpin();
}
}
return true;
}
bool SemaphoreGeneric::LockAbsNS(AuUInt64 qwTimeoutAbs)
{
static const AuUInt32 kRef { 0 };
while (!TryLock())
{
bool bStatus {};
AuAtomicAdd(&this->uAtomicSleeping, 1u);
bStatus = InternalLTSWaitOnAddressHighRes((const void *)&this->uAtomicState, &kRef, sizeof(kRef), qwTimeoutAbs);
AuAtomicSub(&this->uAtomicSleeping, 1u);
if (!bStatus)
{
return TryLockNoSpin();
}
}
return true;
}
AUKN_SYM ISemaphore *SemaphoreNew(AuUInt16 uIntialValue)
{
return _new SemaphoreGeneric(uIntialValue);
}
AUKN_SYM void SemaphoreRelease(ISemaphore *waitable)
{
AuSafeDelete<Semaphore *>(waitable);
AuSafeDelete<SemaphoreGeneric *>(waitable);
}
AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, Semaphore, SemaphoreGeneric)
}
#endif

View File

@ -11,30 +11,45 @@
#include "AuSemaphore.NT.hpp"
#elif defined(AURORA_IS_LINUX_DERIVED)
#include "AuSemaphore.Linux.hpp"
#elif defined(AURORA_HAS_PTHREADS)
#include "AuSemaphore.Unix.hpp"
#else
#define _AURUNTIME_GENERIC_SEMAPHORE
namespace Aurora::Threading::Primitives
{
class Semaphore : public ISemaphore
struct SemaphoreGeneric : ISemaphore
{
public:
Semaphore(long intialValue = 0);
~Semaphore();
SemaphoreGeneric(AuUInt16 uIntialValue = 0);
~SemaphoreGeneric();
auline bool TryLockNoSpin();
auline bool TryLockHeavy();
bool TryLock() override;
bool HasOSHandle(AuMach &mach) override;
bool HasLockImplementation() override;
bool TryLock() override;
bool Lock(AuUInt64 timeout) override;
void Lock() override;
void Unlock(AuUInt16 count) override;
void Unlock() override;
private:
std::atomic_long value_{};
void Unlock(AuUInt16 uThreads) override;
void Lock() override;
bool LockMS(AuUInt64 qwTimeout) override;
bool LockAbsMS(AuUInt64 qwTimeout) override;
bool LockNS(AuUInt64 qwTimeout) override;
bool LockAbsNS(AuUInt64 qwTimeoutAbs) override;
AuAUInt32 uAtomicState {};
AuAUInt32 uAtomicSleeping {};
};
using SemaphoreImpl = SemaphoreGeneric;
}
#endif

View File

@ -7,7 +7,6 @@
***/
#include <Source/RuntimeInternal.hpp>
#include "AuSemaphore.Generic.hpp"
#include "AuSemaphore.NT.hpp"
#include "SMTYield.hpp"
#include "../AuWakeInternal.hpp"

View File

@ -1,178 +0,0 @@
/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuSemaphore.Unix.cpp
Date: 2021-6-12
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuSemaphore.Generic.hpp"
#include "SMTYield.hpp"
#if !defined(_AURUNTIME_GENERIC_SEMAPHORE) && !defined(AURORA_IS_LINUX_DERIVED)
#include <Source/Time/Time.hpp>
namespace Aurora::Threading::Primitives
{
SemaphoreImpl::SemaphoreImpl(AuUInt16 uIntialValue) : value_(intialValue)
{
pthread_condattr_t attr;
::pthread_condattr_init(&attr);
::pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
SysAssert(::pthread_cond_init(&this->pthreadCv_, &attr) == 0, "couldn't initialize sema/CV");
}
SemaphoreImpl::~SemaphoreImpl()
{
::pthread_cond_destroy(&this->pthreadCv_);
}
bool SemaphoreImpl::HasOSHandle(AuMach &mach)
{
return false;
}
bool SemaphoreImpl::HasLockImplementation()
{
return true;
}
bool SemaphoreImpl::TryLockNoSpin()
{
auto old = this->value_;
return (old != 0 && AuAtomicCompareExchange(&this->value_, old - 1, old) == old);
}
bool SemaphoreImpl::TryLock()
{
if (gRuntimeConfig.threadingConfig.bPreferUnixPrimitivesNoSpin)
{
return this->TryLockNoSpin();
}
else
{
return return DoTryIf([=]()
{
return this->TryLockNoSpin();
});
}
}
bool SemaphoreImpl::LockMS(AuUInt64 uTimeout)
{
return LockNS(AuMSToNS<AuUInt64>(uTimeout));
}
bool SemaphoreImpl::LockNS(AuUInt64 uTimeout)
{
AuUInt64 uStart {};
AuUInt64 uEnd {};
if (this->TryLock())
{
return true;
}
errno = 0;
auto mutex = reinterpret_cast<pthread_mutex_t*>(this->mutex_.GetOSHandle());
struct timespec tspec;
if (uTimeout != 0)
{
uStart = AuTime::SteadyClockNS();
uEnd = uStart + uTimeout;
Time::monoabsns2ts(&tspec, uEnd);
}
while (!this->TryLock())
{
AU_LOCK_GUARD(this->mutex_);
if (this->TryLockNoSpin())
{
return true;
}
if (uTimeout != 0)
{
if (Time::SteadyClockNS() >= uEnd)
{
return false;
}
int ret {};
do
{
ret = ::pthread_cond_timedwait(&this->pthreadCv_, mutex, &tspec);
}
while (ret == EINTR);
}
else
{
int ret {};
bool bStatus {};
do
{
if ((ret = ::pthread_cond_wait(&this->pthreadCv_, mutex)) == 0)
{
bStatus = true;
break;
}
}
while (ret == EINTR);
RUNTIME_ASSERT_SHUTDOWN_SAFE(bStatus, "semaphore wait failed: {}", ret)
}
}
return true;
}
void SemaphoreImpl::Lock()
{
auto status = LockNS(0);
SysAssert(status, "Couldn't lock semaphore");
}
void SemaphoreImpl::Unlock(AuUInt16 count)
{
{
AU_LOCK_GUARD(this->mutex_);
AuAtomicAdd<AuInt32>(&this->value_, count);
}
if (count == 1)
{
auto ret = ::pthread_cond_signal(&this->pthreadCv_);
SysAssert(ret == 0, "Couldn't wake any semaphore waiter");
}
else
{
auto ret = ::pthread_cond_broadcast(&this->pthreadCv_);
SysAssert(ret == 0, "Couldn't wake any semaphore waiters");
}
}
void SemaphoreImpl::Unlock()
{
Unlock(1);
}
AUKN_SYM ISemaphore *SemaphoreNew(AuUInt16 uIntialValue)
{
return _new SemaphoreImpl(uIntialValue);
}
AUKN_SYM void SemaphoreRelease(ISemaphore *pSemaphore)
{
AuSafeDelete<SemaphoreImpl *>(pSemaphore);
}
AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, Semaphore, SemaphoreImpl)
}
#endif

View File

@ -1,34 +0,0 @@
/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuSemaphore.Unix.hpp
Date: 2021-6-12
Author: Reece
***/
#pragma once
#include "AuConditionMutex.Unix.hpp"
namespace Aurora::Threading::Primitives
{
struct SemaphoreImpl final : ISemaphore
{
SemaphoreImpl(AuUInt16 intialValue = 0);
~SemaphoreImpl();
bool HasOSHandle(AuMach &mach) override;
bool HasLockImplementation() override;
auline bool TryLockNoSpin();
bool TryLock() override;
bool LockMS(AuUInt64 timeout) override;
bool LockNS(AuUInt64 timeout) override;
void Lock() override;
void Unlock(AuUInt16 count) override;
void Unlock() override;
private:
AuInt32 value_ {};
pthread_cond_t pthreadCv_ {};
UnixConditionMutex mutex_;
};
}

View File

@ -0,0 +1,233 @@
/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuWoASemaphore.Unix.cpp
Date: 2021-6-12
Date: 2023-12-29
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuSemaphore.Generic.hpp"
#include "SMTYield.hpp"
#if !defined(AURORA_IS_LINUX_DERIVED)
#include <Source/Time/Time.hpp>
namespace Aurora::Threading::Primitives
{
WoAConditionMutex::UnixConditionMutex()
{
auto status = pthread_mutex_init(&this->value_, nullptr) == 0;
SysAssert(status, "Mutex init failed");
}
WoAConditionMutex::~UnixConditionMutex()
{
int status = pthread_mutex_destroy(&this->value_);
RUNTIME_ASSERT_SHUTDOWN_SAFE(status == 0, "Mutex destruct failed, {} {}", status, errno);
}
void WoAConditionMutex::Lock()
{
int ret {};
do
{
if ((ret = pthread_mutex_lock(&this->value_)) == 0)
{
return;
}
}
while (ret == EINTR);
RUNTIME_ASSERT_SHUTDOWN_SAFE(false, "mutex lock failed: {}", ret)
}
bool WoAConditionMutex::TryLock()
{
int ret {};
do
{
if ((ret = pthread_mutex_trylock(&this->value_)) == 0)
{
return true;
}
}
while (ret == EINTR);
return false;
}
void WoAConditionMutex::Unlock()
{
auto status = pthread_mutex_unlock(&this->value_) == 0;
SysAssert(status, "Mutex release error");
}
WoAConditionVariable::WoAConditionVariable()
{
pthread_condattr_t attr;
::pthread_condattr_init(&attr);
::pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
auto ret = ::pthread_cond_init(&this->pthreadCv_, &attr);
SysAssert(ret == 0, "Couldn't initialize CV");
}
WoAConditionVariable::~WoAConditionVariable()
{
::pthread_cond_destroy(&this->pthreadCv_);
}
bool WoAConditionVariable::WaitForSignalNsEx(WoAConditionMutex *pMutex,
AuUInt64 qwTimeout,
bool bSpin)
{
auto mutex = pMutex->value_;
if (gRuntimeRunLevel >= 5)
{
return true;
}
if (qwTimeout == 0)
{
int ret {};
do
{
if ((ret = ::pthread_cond_wait(&this->pthreadCv_, mutex)) == 0)
{
return true;
}
}
while (ret == EINTR);
RUNTIME_ASSERT_SHUTDOWN_SAFE(false, "conditional wait failed: {}", ret)
return false;
}
else
{
struct timespec tspec;
Time::monoabsns2ts(&tspec, AuTime::SteadyClockNS() + qwTimeout);
int ret {};
do
{
ret = ::pthread_cond_timedwait(&this->pthreadCv_, mutex, &tspec);
if (ret == 0)
{
return true;
}
if (ret == ETIMEDOUT)
{
return false;
}
}
while (ret == EINTR);
RUNTIME_ASSERT_SHUTDOWN_SAFE(false, "conditional timed wait failed: {}", ret)
return false;
}
}
void WoAConditionVariable::Signal()
{
(void)::pthread_cond_signal(&this->pthreadCv_);
}
void WoAConditionVariable::Broadcast()
{
(void)::pthread_cond_broadcast(&this->pthreadCv_);
}
WoASemaphoreImpl::WoASemaphoreImpl(AuUInt16 uIntialValue) :
value_(intialValue)
{
}
WoASemaphoreImpl::~WoASemaphoreImpl()
{
::pthread_cond_destroy(&this->pthreadCv_);
}
bool WoASemaphoreImpl::HasOSHandle(AuMach &mach)
{
return false;
}
bool WoASemaphoreImpl::HasLockImplementation()
{
return true;
}
bool WoASemaphoreImpl::TryLock()
{
auto old = this->value_;
return (old != 0 && AuAtomicCompareExchange(&this->value_, old - 1, old) == old);
}
bool WoASemaphoreImpl::LockMS(AuUInt64 uTimeout)
{
return LockNS(AuMSToNS<AuUInt64>(uTimeout));
}
bool WoASemaphoreImpl::LockNS(AuUInt64 uTimeout)
{
if (!uTimeout)
{
while (!this->TryLock())
{
(void)this->cond_.WaitForSignalNsEx(&this->mutex_, 0, true);
}
return true;
}
else
{
if (this->TryLock())
{
return true;
}
(void)this->cond_.WaitForSignalNsEx(&this->mutex_, uTimeout, true);
return this->TryLock();
}
}
void WoASemaphoreImpl::Lock()
{
auto status = this->LockNS(0);
SysAssert(status, "Couldn't lock semaphore");
}
void WoASemaphoreImpl::Unlock(AuUInt16 count)
{
{
AuAtomicAdd<AuInt32>(&this->value_, count);
}
{
AU_LOCK_GUARD(this->mutex_);
}
if (count > 1)
{
this->cond_.Signal();
}
else
{
this->cond_.Broadcast();
}
}
void WoASemaphoreImpl::Unlock()
{
this->Unlock(1);
}
}
#endif

View File

@ -0,0 +1,67 @@
/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuWoASemaphore.Unix.hpp
Date: 2021-6-12
Date: 2023-12-29
Author: Reece
Note: Implements the basic primitives for use when the generic WoA implementations of thread primitives are incorporated into a given target platform
(Win9x - NT 2000 + Xbox [360] kernel objects, iOS/libdispatch semaphores, Nintendo Switch (NX semaphores), PlayStation X (kqueue yielding or not-pthread ~p~sceThreads perhaps?), etc)
***/
#pragma once
namespace Aurora::Threading::Primitives
{
// Moved from ./AuConditionMutex.Unix.[c/h]pp
// now ./_removed/AuConditionMutex.Unix.[c/h]pp.bak
struct WoAConditionMutex final
{
WoAConditionMutex();
~WoAConditionMutex();
bool TryLock();
void Lock();
void Unlock();
AuUInt GetOSHandle();
private:
pthread_mutex_t value_;
};
struct WoAConditionVariable final
{
WoAConditionVariable();
~WoAConditionVariable();
bool WaitForSignalNsEx(WoAConditionMutex *pMutex,
AuUInt64 qwTimeout,
bool bSpin = true);
void Signal();
void Broadcast();
private:
friend struct WoAConditionMutex;
pthread_cond_t pthreadCv_;
};
struct WoASemaphoreImpl final
{
WoASemaphoreImpl(AuUInt16 intialValue = 0);
~WoASemaphoreImpl();
bool HasOSHandle(AuMach &mach);
bool HasLockImplementation();
auline bool TryLockNoSpin();
bool TryLock() override;
bool LockMS(AuUInt64 timeout);
bool LockNS(AuUInt64 timeout);
void Lock();
void Unlock(AuUInt16 count);
void Unlock();
private:
AuInt32 value_ {};
WoAConditionMutex mutex_;
WoAConditionVariable cond_;
};
}