[+] Added Linux-specific condvars and condmutex

This commit is contained in:
Reece Wilson 2023-08-12 11:11:12 +01:00
parent 737d3bb4d6
commit 7962772c62
8 changed files with 399 additions and 2 deletions

View File

@ -11,6 +11,8 @@
#if defined(AURORA_IS_MODERNNT_DERIVED)
#include "AuConditionMutex.NT.hpp"
#elif defined(AURORA_IS_LINUX_DERIVED)
#include "AuConditionMutex.Linux.hpp"
#elif defined(AURORA_HAS_PTHREADS)
#include "AuConditionMutex.Unix.hpp"
#else

View File

@ -0,0 +1,108 @@
/***
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuConditionMutex.Linux.cpp
Date: 2023-8-11
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuConditionMutex.Generic.hpp"
#include "SMTYield.hpp"
#if !defined(_AURUNTIME_GENERICCV)
#include <Source/Time/Time.hpp>
namespace Aurora::Threading::Primitives
{
#define barrier() __asm__ __volatile__("sfence": : :"memory")
#define compilerReorderBarrier() __asm__ __volatile__("": : :"memory")
LinuxConditionMutex::LinuxConditionMutex()
{
}
LinuxConditionMutex::~LinuxConditionMutex()
{
}
bool LinuxConditionMutex::TryLock()
{
return DoTryIf([=]()
{
return AuAtomicTestAndSet(&this->uState_, 0) == 0;
});
}
void LinuxConditionMutex::Lock()
{
if (TryLock())
{
return;
}
AuAtomicAdd(&this->uSleeping_, 1u);
auto state = this->uState_;
while (!(state == 0 &&
AuAtomicCompareExchange<AuUInt32>(&this->uState_, 1, state) == state))
{
int ret {};
bool bStatus {};
do
{
if ((ret = futex_wait(&this->uState_, state)) == 0)
{
bStatus = true;
break;
}
if (ret == EAGAIN || errno == EAGAIN)
{
bStatus = true;
break;
}
}
while (ret == EINTR);
RUNTIME_ASSERT_SHUTDOWN_SAFE(bStatus, "Mutex wait failed: {}", ret)
state = this->uState_;
}
AuAtomicSub(&this->uSleeping_, 1u);
}
void LinuxConditionMutex::Unlock()
{
__sync_lock_release(&this->uState_);
compilerReorderBarrier();
if (this->uSleeping_)
{
futex_wake(&this->uState_, 1);
}
}
AuUInt LinuxConditionMutex::GetOSHandle()
{
return AuMach(&this->uState_);
}
AUKN_SYM IConditionMutex *ConditionMutexNew()
{
return _new LinuxConditionMutex();
}
AUKN_SYM void ConditionMutexRelease(IConditionMutex *mutex)
{
AuSafeDelete<LinuxConditionMutex *>(mutex);
}
AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, ConditionMutex, LinuxConditionMutex)
}
#endif

View File

@ -0,0 +1,30 @@
/***
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuConditionMutex.Linux.hpp
Date: 2023-8-11
Author: Reece
***/
#pragma once
#include "AuIConditionMutexEx.hpp"
namespace Aurora::Threading::Primitives
{
struct LinuxConditionMutex : IConditionMutexEx
{
LinuxConditionMutex();
~LinuxConditionMutex();
bool TryLock() override;
void Lock() override;
void Unlock() override;
AuUInt GetOSHandle() override;
private:
AuUInt32 uState_ {};
AuUInt32 uSleeping_ {};
};
using ConditionMutexImpl = LinuxConditionMutex;
}

View File

@ -8,7 +8,7 @@
#include <Source/RuntimeInternal.hpp>
#include "AuConditionMutex.Generic.hpp"
#if !defined(_AURUNTIME_GENERICCM)
#if !defined(_AURUNTIME_GENERICCV) && !defined(AURORA_IS_LINUX_DERIVED)
#include <Source/Time/Time.hpp>
namespace Aurora::Threading::Primitives

View File

@ -11,6 +11,8 @@
#if defined(AURORA_IS_MODERNNT_DERIVED)
#include "AuConditionVariable.NT.hpp"
#elif defined(AURORA_IS_LINUX_DERIVED)
#include "AuConditionVariable.Linux.hpp"
#elif defined(AURORA_HAS_PTHREADS)
#include "AuConditionVariable.Unix.hpp"
#else

View File

@ -0,0 +1,212 @@
/***
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuConditionVariable.Linux.cpp
Date: 2023-8-11
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuConditionVariable.Generic.hpp"
#include <Source/Time/Time.hpp>
#include "SMTYield.hpp"
#if !defined(_AURUNTIME_GENERICCV)
namespace Aurora::Threading::Primitives
{
ConditionVariableImpl::ConditionVariableImpl(const AuSPtr<IConditionMutex> &pMutex) :
mutex_(AuStaticCast<LinuxConditionMutex>(pMutex))
{
}
ConditionVariableImpl::~ConditionVariableImpl()
{
}
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)
{
AuUInt64 uStart {};
AuUInt64 uEnd {};
struct timespec tspec;
if (qwTimeoutRelative != 0)
{
uStart = AuTime::SteadyClockNS();
uEnd = uStart + qwTimeoutRelative;
Time::monoabsns2ts(&tspec, uEnd);
}
if (DoTryIf([=]()
{
auto old = this->uState_;
return (old != 0 && AuAtomicCompareExchange(&this->uState_, old - 1, old) == old);
}))
{
return true;
}
auto old = this->uState_;
while (!((old != 0) &&
(AuAtomicCompareExchange(&this->uState_, old - 1, old) == old)))
{
if (qwTimeoutRelative != 0)
{
if (Time::SteadyClockNS() >= uEnd)
{
return false;
}
int ret {};
do
{
ret = futex_wait(&this->uState_, 0, &tspec);
}
while (ret == EINTR);
}
else
{
int ret {};
bool bStatus {};
do
{
if ((ret = futex_wait(&this->uState_, 0)) == 0)
{
bStatus = true;
continue;
}
if (ret == EAGAIN || errno == EAGAIN)
{
bStatus = true;
continue;
}
}
while (ret == EINTR);
RUNTIME_ASSERT_SHUTDOWN_SAFE(bStatus, "semaphore wait failed: {}", ret)
}
old = this->uState_;
}
return true;
}
bool ConditionVariableImpl::WaitForSignalNsEx(const std::shared_ptr<LinuxConditionMutex> &pMutex,
AuUInt64 qwTimeoutRelative)
{
AuAtomicAdd(&this->uSleeping_, 1u);
if (pMutex)
{
pMutex->Unlock();
}
auto bSuccess = this->WaitOne(qwTimeoutRelative);
if (!bSuccess)
{
auto uWaiters = this->uSleeping_;
auto uWaitCount = 1;
while (AuAtomicCompareExchange(&this->uSleeping_, uWaiters - uWaitCount, uWaiters) != uWaiters)
{
uWaiters = this->uSleeping_;
if (uWaiters == 0)
{
break;
}
}
}
if (pMutex)
{
pMutex->Lock();
}
return bSuccess;
}
void ConditionVariableImpl::Signal()
{
AuUInt32 uWaitCount {};
AuUInt32 uWaiters {};
uWaiters = this->uSleeping_;
if (uWaiters > 0)
{
AuAtomicAdd(&this->uState_, 1u);
futex_wake(&this->uState_, 1u);
uWaitCount = 1;
}
while (AuAtomicCompareExchange(&this->uSleeping_, uWaiters - uWaitCount, uWaiters) != uWaiters)
{
uWaiters = this->uSleeping_;
if (uWaiters == 0)
{
return;
}
}
}
void ConditionVariableImpl::Broadcast()
{
AuUInt32 uWaitCount {};
AuUInt32 uWaiters {};
uWaiters = this->uSleeping_;
if (uWaiters > 0)
{
AuAtomicAdd(&this->uState_, uWaiters);
futex_wake(&this->uState_, uWaiters);
uWaitCount = uWaiters;
}
while (AuAtomicCompareExchange(&this->uSleeping_, uWaiters - uWaitCount, uWaiters) != uWaiters)
{
uWaiters = this->uSleeping_;
if (uWaiters <= uWaitCount)
{
uWaitCount = uWaiters;
}
}
}
AUKN_SYM IConditionVariable *ConditionVariableNew(const AuSPtr<IConditionMutex> &mutex)
{
return _new ConditionVariableImpl(mutex);
}
AUKN_SYM void ConditionVariableRelease(IConditionVariable *mutex)
{
AuSafeDelete<ConditionVariableImpl *>(mutex);
}
AUROXTL_INTERFACE_SOO_SRC(ConditionVariable, ConditionVariableImpl, (const AuSPtr<IConditionMutex> &, pMutex))
}
#endif

View File

@ -0,0 +1,43 @@
/***
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuConditionVariable.Linux.hpp
Date: 2023-8-11
Author: Reece
***/
#pragma once
#if !defined(_AURUNTIME_GENERICCV)
#include "AuConditionMutex.Linux.hpp"
namespace Aurora::Threading::Primitives
{
struct ConditionVariableImpl final : IConditionVariable
{
ConditionVariableImpl(const AuSPtr<IConditionMutex> &mutex);
~ConditionVariableImpl();
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;
bool WaitOne(AuUInt64 qwTimeout);
private:
AuUInt32 uState_ {};
AuUInt32 uSleeping_ {};
AuSPtr<LinuxConditionMutex> mutex_;
};
struct CondVarDummy : IConditionVariable
{
AuUInt32 uState_ {};
AuUInt32 uSleeping_ {};
};
static const auto kSizeOfDummyCondVar = sizeof(CondVarDummy);
}
#endif

View File

@ -9,7 +9,7 @@
#include "AuConditionVariable.Generic.hpp"
#include <Source/Time/Time.hpp>
#if !defined(_AURUNTIME_GENERICCV)
#if !defined(_AURUNTIME_GENERICCV) && !defined(AURORA_IS_LINUX_DERIVED)
namespace Aurora::Threading::Primitives
{