[*] 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 kPrimitiveSize64LinuxSemaphore = 16;
static const auto kPrimitiveSize64LinuxCS = 40;
static const auto kPrimitiveSize64LinuxEvent = 64;
static const auto kPrimitiveSize64LinuxRWLock = 88;
static const auto kPrimitiveSize64LinuxCS = 32;
static const auto kPrimitiveSize64LinuxEvent = 32;
static const auto kPrimitiveSize64LinuxRWLock = 64;
static const auto kPrimitiveSize64LinuxCond = 32;
static const auto kPrimitiveSize64LinuxCondMutex = 16;
// TODO: These values aren't quite correct yet:
static const auto kPrimitiveSize32LinuxMutex = 12;
static const auto kPrimitiveSize32LinuxSemaphore = 8;
static const auto kPrimitiveSize32LinuxCS = 40;
static const auto kPrimitiveSize32LinuxEvent = 40;
static const auto kPrimitiveSize32LinuxSemaphore = 12;
static const auto kPrimitiveSize32LinuxCS = 32;
static const auto kPrimitiveSize32LinuxEvent = 32;
static const auto kPrimitiveSize32LinuxRWLock = 88;
static const auto kPrimitiveSize32LinuxCond = 32;
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()
{
if (gRuntimeConfig.threadingConfig.bPreferLinuxCondMutexSpinTryLock)
@ -111,15 +203,15 @@ namespace Aurora::Threading::Primitives
AUKN_SYM IConditionMutex *ConditionMutexNew()
{
return _new LinuxConditionMutex();
return _new ConditionMutexImpl();
}
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

View File

@ -11,22 +11,56 @@
namespace Aurora::Threading::Primitives
{
struct LinuxConditionMutex final : IConditionMutexEx
#pragma pack(push)
#pragma pack(4)
struct LinuxConditionMutex final
{
LinuxConditionMutex();
~LinuxConditionMutex();
bool TryLock() override;
void Lock() override;
void Unlock() override;
AuUInt GetOSHandle() override;
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();
private:
AuUInt32 uState_ {};
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_;
};
using ConditionVariableInternal = UnixConditionMutex;
using ConditionMutexImpl = UnixConditionMutex;
}

View File

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

View File

@ -12,32 +12,69 @@
namespace Aurora::Threading::Primitives
{
struct ConditionVariableImpl final : IConditionVariable
{
ConditionVariableImpl(const AuSPtr<IConditionMutex> &mutex);
~ConditionVariableImpl();
#pragma pack(push)
#pragma pack(4)
AuSPtr<IConditionMutex> GetMutex() override;
bool WaitForSignal(AuUInt32 timeout) override;
bool WaitForSignalNsEx(const std::shared_ptr<LinuxConditionMutex> &pMutex, AuUInt64 timeout);
bool WaitForSignalNS(AuUInt64 qwTimeout) override;
void Signal() override;
void Broadcast() override;
struct ConditionVariableLinux final
{
ConditionVariableLinux();
~ConditionVariableLinux();
bool WaitForSignalNsEx(LinuxConditionMutex *pMutex, AuUInt64 timeout);
void Signal();
void Broadcast();
bool WaitOne(AuUInt64 qwTimeout);
private:
AuUInt32 uState_ {};
AuUInt32 uSleeping_ {};
std::shared_ptr<LinuxConditionMutex> mutex_;
};
#pragma pack(pop)
struct CondVarDummy : IConditionVariable
using ConditionVariableInternal = ConditionVariableLinux;
struct ConditionVariableImpl final : IConditionVariable
{
AuUInt32 uState_ {};
AuUInt32 uSleeping_ {};
inline ConditionVariableImpl(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<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

View File

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

View File

@ -95,6 +95,48 @@ namespace Aurora::Threading::Primitives
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()
{
if (--this->count_ == 0)

View File

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

View File

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

View File

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