[+] 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:
parent
f404e8960f
commit
5862dbeacc
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -7,6 +7,7 @@
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
#include "AuConditionVariable.Generic.hpp"
|
||||
#if !defined(_AURUNTIME_GENERICCV)
|
||||
#include "AuConditionMutex.NT.hpp"
|
||||
|
||||
|
@ -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
|
@ -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
|
@ -16,7 +16,7 @@ namespace Aurora::Threading::Primitives
|
||||
{
|
||||
MutexImpl::MutexImpl()
|
||||
{
|
||||
|
||||
this->state_ = 0;
|
||||
}
|
||||
|
||||
MutexImpl::~MutexImpl()
|
||||
|
@ -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>
|
||||
|
@ -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
|
@ -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
|
@ -7,7 +7,6 @@
|
||||
***/
|
||||
#include <Source/RuntimeInternal.hpp>
|
||||
#include "AuSemaphore.Generic.hpp"
|
||||
#include "AuSemaphore.NT.hpp"
|
||||
#include "SMTYield.hpp"
|
||||
#include "../AuWakeInternal.hpp"
|
||||
|
||||
|
@ -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
|
@ -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_;
|
||||
};
|
||||
}
|
233
Source/Threading/Primitives/AuWoASemaphore.Unix.cpp
Normal file
233
Source/Threading/Primitives/AuWoASemaphore.Unix.cpp
Normal 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
|
67
Source/Threading/Primitives/AuWoASemaphore.Unix.hpp
Normal file
67
Source/Threading/Primitives/AuWoASemaphore.Unix.hpp
Normal 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_;
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user