[*] More compact Linux primitives

This commit is contained in:
Reece Wilson 2023-08-21 19:17:05 +01:00
parent e8f445c463
commit fa170c413d
12 changed files with 256 additions and 55 deletions

View File

@ -37,17 +37,17 @@ namespace Aurora::Threading::Primitives
static const auto kPrimitiveSize64LinuxMutex = 16; static const auto kPrimitiveSize64LinuxMutex = 16;
static const auto kPrimitiveSize64LinuxSemaphore = 16; static const auto kPrimitiveSize64LinuxSemaphore = 16;
static const auto kPrimitiveSize64LinuxCS = 40; static const auto kPrimitiveSize64LinuxCS = 32;
static const auto kPrimitiveSize64LinuxEvent = 64; static const auto kPrimitiveSize64LinuxEvent = 32;
static const auto kPrimitiveSize64LinuxRWLock = 88; static const auto kPrimitiveSize64LinuxRWLock = 64;
static const auto kPrimitiveSize64LinuxCond = 32; static const auto kPrimitiveSize64LinuxCond = 32;
static const auto kPrimitiveSize64LinuxCondMutex = 16; static const auto kPrimitiveSize64LinuxCondMutex = 16;
// TODO: These values aren't quite correct yet: // TODO: These values aren't quite correct yet:
static const auto kPrimitiveSize32LinuxMutex = 12; static const auto kPrimitiveSize32LinuxMutex = 12;
static const auto kPrimitiveSize32LinuxSemaphore = 8; static const auto kPrimitiveSize32LinuxSemaphore = 12;
static const auto kPrimitiveSize32LinuxCS = 40; static const auto kPrimitiveSize32LinuxCS = 32;
static const auto kPrimitiveSize32LinuxEvent = 40; static const auto kPrimitiveSize32LinuxEvent = 32;
static const auto kPrimitiveSize32LinuxRWLock = 88; static const auto kPrimitiveSize32LinuxRWLock = 88;
static const auto kPrimitiveSize32LinuxCond = 32; static const auto kPrimitiveSize32LinuxCond = 32;
static const auto kPrimitiveSize32LinuxCondMutex = 12; static const auto kPrimitiveSize32LinuxCondMutex = 12;

View File

@ -27,6 +27,98 @@ namespace Aurora::Threading::Primitives
} }
bool LinuxConditionMutex::LockMS(AuUInt64 uTimeout)
{
return LockNS(AuMSToNS<AuUInt64>(uTimeout));
}
bool LinuxConditionMutex::LockNS(AuUInt64 uTimeout)
{
if (TryLockNoSpin())
{
return true;
}
return LockNS(AuTime::SteadyClockNS() + uTimeout);
}
bool LinuxConditionMutex::LockAbsMS(AuUInt64 uTimeout)
{
return LockAbsNS(AuMSToNS<AuUInt64>(uTimeout));
}
bool LinuxConditionMutex::LockAbsNS(AuUInt64 uTimeout)
{
struct timespec tspec;
if (TryLockNoSpin())
{
return true;
}
if (uTimeout != 0)
{
Time::monoabsns2ts(&tspec, uTimeout);
}
if (TryLockHeavy())
{
return true;
}
AuAtomicAdd(&this->uSleeping_, 1u);
auto state = this->uState_;
while (!(state == 0 &&
AuAtomicCompareExchange<AuUInt32>(&this->uState_, 1, state) == state))
{
if (uTimeout != 0)
{
if (Time::SteadyClockNS() >= uTimeout)
{
AuAtomicSub(&this->uSleeping_, 1u);
return false;
}
int ret {};
do
{
ret = futex_wait(&this->uState_, state, &tspec);
}
while (ret == EINTR);
}
else
{
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);
return true;
}
bool LinuxConditionMutex::TryLock() bool LinuxConditionMutex::TryLock()
{ {
if (gRuntimeConfig.threadingConfig.bPreferLinuxCondMutexSpinTryLock) if (gRuntimeConfig.threadingConfig.bPreferLinuxCondMutexSpinTryLock)
@ -111,15 +203,15 @@ namespace Aurora::Threading::Primitives
AUKN_SYM IConditionMutex *ConditionMutexNew() AUKN_SYM IConditionMutex *ConditionMutexNew()
{ {
return _new LinuxConditionMutex(); return _new ConditionMutexImpl();
} }
AUKN_SYM void ConditionMutexRelease(IConditionMutex *mutex) AUKN_SYM void ConditionMutexRelease(IConditionMutex *mutex)
{ {
AuSafeDelete<LinuxConditionMutex *>(mutex); AuSafeDelete<ConditionMutexImpl *>(mutex);
} }
AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, ConditionMutex, LinuxConditionMutex) AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, ConditionMutex, ConditionMutexImpl)
} }
#endif #endif

View File

@ -11,22 +11,56 @@
namespace Aurora::Threading::Primitives namespace Aurora::Threading::Primitives
{ {
struct LinuxConditionMutex final : IConditionMutexEx #pragma pack(push)
#pragma pack(4)
struct LinuxConditionMutex final
{ {
LinuxConditionMutex(); LinuxConditionMutex();
~LinuxConditionMutex(); ~LinuxConditionMutex();
bool TryLock() override; bool TryLock();
void Lock() override; void Lock();
void Unlock() override; void Unlock();
AuUInt GetOSHandle() override; AuUInt GetOSHandle();
bool LockMS(AuUInt64 timeout);
bool LockNS(AuUInt64 timeout);
bool LockAbsMS(AuUInt64 timeout);
bool LockAbsNS(AuUInt64 timeout);
auline bool TryLockNoSpin(); auline bool TryLockNoSpin();
auline bool TryLockHeavy(); auline bool TryLockHeavy();
private: private:
AuUInt32 uState_ {}; AuUInt32 uState_ {};
AuUInt32 uSleeping_ {}; AuUInt32 uSleeping_ {};
}; };
#pragma pack(pop)
using ConditionMutexImpl = LinuxConditionMutex; using ConditionMutexInternal = LinuxConditionMutex;
struct ConditionMutexImpl 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;
};
} }

View File

@ -25,5 +25,6 @@ namespace Aurora::Threading::Primitives
pthread_mutex_t value_; pthread_mutex_t value_;
}; };
using ConditionVariableInternal = UnixConditionMutex;
using ConditionMutexImpl = UnixConditionMutex; using ConditionMutexImpl = UnixConditionMutex;
} }

View File

@ -14,33 +14,17 @@
namespace Aurora::Threading::Primitives namespace Aurora::Threading::Primitives
{ {
ConditionVariableImpl::ConditionVariableImpl(const AuSPtr<IConditionMutex> &pMutex) : ConditionVariableLinux::ConditionVariableLinux()
mutex_(AuStaticCast<LinuxConditionMutex>(pMutex))
{ {
} }
ConditionVariableImpl::~ConditionVariableImpl() ConditionVariableLinux::~ConditionVariableLinux()
{ {
} }
AuSPtr<IConditionMutex> ConditionVariableImpl::GetMutex() bool ConditionVariableLinux::WaitOne(AuUInt64 qwTimeoutRelative)
{
return this->mutex_;
}
bool ConditionVariableImpl::WaitForSignal(AuUInt32 qwTimeoutRelative)
{
return WaitForSignalNS(AuMSToNS<AuUInt64>(qwTimeoutRelative));
}
bool ConditionVariableImpl::WaitForSignalNS(AuUInt64 qwTimeoutRelative)
{
return WaitForSignalNsEx(this->mutex_, qwTimeoutRelative);
}
bool ConditionVariableImpl::WaitOne(AuUInt64 qwTimeoutRelative)
{ {
AuUInt64 uStart {}; AuUInt64 uStart {};
AuUInt64 uEnd {}; AuUInt64 uEnd {};
@ -112,7 +96,7 @@ namespace Aurora::Threading::Primitives
return true; return true;
} }
bool ConditionVariableImpl::WaitForSignalNsEx(const std::shared_ptr<LinuxConditionMutex> &pMutex, bool ConditionVariableLinux::WaitForSignalNsEx(LinuxConditionMutex *pMutex,
AuUInt64 qwTimeoutRelative) AuUInt64 qwTimeoutRelative)
{ {
@ -149,7 +133,7 @@ namespace Aurora::Threading::Primitives
return bSuccess; return bSuccess;
} }
void ConditionVariableImpl::Signal() void ConditionVariableLinux::Signal()
{ {
AuUInt32 uWaitCount {}; AuUInt32 uWaitCount {};
AuUInt32 uWaiters {}; AuUInt32 uWaiters {};
@ -173,7 +157,7 @@ namespace Aurora::Threading::Primitives
} }
} }
void ConditionVariableImpl::Broadcast() void ConditionVariableLinux::Broadcast()
{ {
AuUInt32 uWaitCount {}; AuUInt32 uWaitCount {};
AuUInt32 uWaiters {}; AuUInt32 uWaiters {};

View File

@ -12,32 +12,69 @@
namespace Aurora::Threading::Primitives namespace Aurora::Threading::Primitives
{ {
struct ConditionVariableImpl final : IConditionVariable #pragma pack(push)
{ #pragma pack(4)
ConditionVariableImpl(const AuSPtr<IConditionMutex> &mutex);
~ConditionVariableImpl();
AuSPtr<IConditionMutex> GetMutex() override; struct ConditionVariableLinux final
bool WaitForSignal(AuUInt32 timeout) override; {
bool WaitForSignalNsEx(const std::shared_ptr<LinuxConditionMutex> &pMutex, AuUInt64 timeout); ConditionVariableLinux();
bool WaitForSignalNS(AuUInt64 qwTimeout) override; ~ConditionVariableLinux();
void Signal() override;
void Broadcast() override; bool WaitForSignalNsEx(LinuxConditionMutex *pMutex, AuUInt64 timeout);
void Signal();
void Broadcast();
bool WaitOne(AuUInt64 qwTimeout); bool WaitOne(AuUInt64 qwTimeout);
private: private:
AuUInt32 uState_ {}; AuUInt32 uState_ {};
AuUInt32 uSleeping_ {}; AuUInt32 uSleeping_ {};
std::shared_ptr<LinuxConditionMutex> mutex_;
}; };
#pragma pack(pop)
struct CondVarDummy : IConditionVariable using ConditionVariableInternal = ConditionVariableLinux;
struct ConditionVariableImpl final : IConditionVariable
{ {
AuUInt32 uState_ {}; inline ConditionVariableImpl(const AuSPtr<IConditionMutex> &mutex) :
AuUInt32 uSleeping_ {}; mutex(mutex)
{
}
auline AuSPtr<IConditionMutex> GetMutex() override
{
return mutex;
}
auline bool WaitForSignal(AuUInt32 timeout) override
{
return cond.WaitForSignalNsEx(&std::static_pointer_cast<ConditionMutexImpl>(this->mutex)->mutex, AuMSToNS<AuUInt64>(timeout));
}
auline bool WaitForSignalNS(AuUInt64 qwTimeout) override
{
return cond.WaitForSignalNsEx(&std::static_pointer_cast<ConditionMutexImpl>(this->mutex)->mutex, qwTimeout);
}
inline bool WaitForSignalNsEx(const std::shared_ptr<LinuxConditionMutex> &pMutex, AuUInt64 timeout)
{
return cond.WaitForSignalNsEx(pMutex.get(), timeout);
}
auline void Signal() override
{
cond.Signal();
}
auline void Broadcast() override
{
cond.Broadcast();
}
ConditionVariableInternal cond;
std::shared_ptr<IConditionMutex> mutex;
}; };
static const auto kSizeOfDummyCondVar = sizeof(CondVarDummy); static const auto kSizeOfDummyCondVar = sizeof(ConditionVariableInternal);
} }
#endif #endif

View File

@ -34,6 +34,8 @@ namespace Aurora::Threading::Primitives
pthread_cond_t pthreadCv_; pthread_cond_t pthreadCv_;
}; };
using ConditionVariableInternal = ConditionVariableImpl;
static const auto kSizeOfDummyCondVar = sizeof(CondVarDummy); static const auto kSizeOfDummyCondVar = sizeof(CondVarDummy);
} }

View File

@ -95,6 +95,48 @@ namespace Aurora::Threading::Primitives
return true; return true;
} }
bool CriticalSectionImpl::LockAbsNS(AuUInt64 timeout)
{
auto cur = GetThreadCookie();
if (this->owner_ == cur)
{
this->count_++;
return true;
}
if (!this->mutex_.LockAbsNS(timeout))
{
return false;
}
this->owner_ = cur;
this->count_ = 1;
return true;
}
bool CriticalSectionImpl::LockAbsMS(AuUInt64 timeout)
{
auto cur = GetThreadCookie();
if (this->owner_ == cur)
{
this->count_++;
return true;
}
if (!this->mutex_.LockAbsMS(timeout))
{
return false;
}
this->owner_ = cur;
this->count_ = 1;
return true;
}
void CriticalSectionImpl::Unlock() void CriticalSectionImpl::Unlock()
{ {
if (--this->count_ == 0) if (--this->count_ == 0)

View File

@ -10,6 +10,10 @@
#include "AuMutex.Generic.hpp" #include "AuMutex.Generic.hpp"
#include "ThreadCookie.hpp" #include "ThreadCookie.hpp"
#if defined(AURORA_IS_LINUX_DERIVED)
#include "AuConditionMutex.Linux.hpp"
#endif
namespace Aurora::Threading::Primitives namespace Aurora::Threading::Primitives
{ {
struct CriticalSectionImpl final : IWaitable struct CriticalSectionImpl final : IWaitable
@ -23,10 +27,16 @@ namespace Aurora::Threading::Primitives
void Lock() override; void Lock() override;
bool LockMS(AuUInt64 timeout) override; bool LockMS(AuUInt64 timeout) override;
bool LockNS(AuUInt64 timeout) override; bool LockNS(AuUInt64 timeout) override;
bool LockAbsMS(AuUInt64 timeout) override;
bool LockAbsNS(AuUInt64 timeout) override;
void Unlock() override; void Unlock() override;
private: private:
#if defined(AURORA_IS_LINUX_DERIVED)
LinuxConditionMutex mutex_; // shave a few bytes
#else
MutexImpl mutex_; MutexImpl mutex_;
#endif
ThreadCookie_t owner_; ThreadCookie_t owner_;
AuUInt32 count_; AuUInt32 count_;
}; };

View File

@ -16,7 +16,6 @@ namespace Aurora::Threading::Primitives
{ {
SemaphoreImpl::SemaphoreImpl(AuUInt16 uIntialValue) : dwState_(uIntialValue) SemaphoreImpl::SemaphoreImpl(AuUInt16 uIntialValue) : dwState_(uIntialValue)
{ {
} }
SemaphoreImpl::~SemaphoreImpl() SemaphoreImpl::~SemaphoreImpl()

View File

@ -49,7 +49,7 @@ namespace Aurora::Threading
return; return;
} }
gRuntimeConfig.threadingConfig = decltype(gRuntimeConfig.threadingConfig)(*pUpdateConfig); gRuntimeConfig.threadingConfig = decltype(gRuntimeConfig.threadingConfig)(ThreadingConfig(*pUpdateConfig));
} }
} }