[+] Added Linux-specific condvars and condmutex
This commit is contained in:
parent
737d3bb4d6
commit
7962772c62
@ -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
|
||||
|
108
Source/Threading/Primitives/AuConditionMutex.Linux.cpp
Normal file
108
Source/Threading/Primitives/AuConditionMutex.Linux.cpp
Normal 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
|
30
Source/Threading/Primitives/AuConditionMutex.Linux.hpp
Normal file
30
Source/Threading/Primitives/AuConditionMutex.Linux.hpp
Normal 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;
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
212
Source/Threading/Primitives/AuConditionVariable.Linux.cpp
Normal file
212
Source/Threading/Primitives/AuConditionVariable.Linux.cpp
Normal 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
|
43
Source/Threading/Primitives/AuConditionVariable.Linux.hpp
Normal file
43
Source/Threading/Primitives/AuConditionVariable.Linux.hpp
Normal 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
|
@ -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
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user