[+] Linux: semaphores and mutexes directly over futexes. Move UNIX pthread condvar mutex into the condvar mutex class.
[*] BSD: Rewrote fundamentally flawed pthread_mutex class code to use MONOTONIC clock time [+] Linus SwInfo: Added enterprise check for RedHat
This commit is contained in:
parent
87194ac1ea
commit
2209aeb7a8
@ -41,7 +41,14 @@ static int futex_wait(uint32_t *addr, uint32_t expected)
|
||||
|
||||
static int futex_wait(uint32_t *addr, uint32_t expected, const struct timespec *timeout)
|
||||
{
|
||||
return futex(addr, FUTEX_WAIT, expected, timeout, 0, 0);
|
||||
if (timeout)
|
||||
{
|
||||
return futex(addr, FUTEX_WAIT_BITSET, expected, timeout, 0, FUTEX_BITSET_MATCH_ANY);
|
||||
}
|
||||
else
|
||||
{
|
||||
return futex(addr, FUTEX_WAIT, expected, timeout, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int futex_wake(uint32_t *addr, uint32_t nthreads)
|
||||
@ -355,22 +362,23 @@ static bool LinuxLockFutex(AuUInt32 *futex, AuUInt32 timeout)
|
||||
|
||||
do
|
||||
{
|
||||
bContended = AuAtomicCompareExchange<AuUInt32>(futex, value, kFutexValueUnlocked) != kFutexValueUnlocked;
|
||||
auto old = AuAtomicCompareExchange<AuUInt32>(futex, value, kFutexValueUnlocked);
|
||||
bContended = old != kFutexValueUnlocked;
|
||||
if (bContended)
|
||||
{
|
||||
int res = ::futex_wait(futex, kFutexValueUnlocked, timeout ? &tspec : nullptr);
|
||||
int res = ::futex_wait(futex, old, timeout ? &tspec : nullptr);
|
||||
if (res < 0)
|
||||
{
|
||||
if (res != -EAGAIN)
|
||||
{
|
||||
SysPushErrorIO("FUTEX ERROR: {}", res);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
if (res == -EAGAIN || errno == EAGAIN || errno == ETIMEDOUT)
|
||||
{
|
||||
//EAGAIN
|
||||
bContended = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SysPushErrorIO("FUTEX ERROR: {}", res);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -165,6 +165,19 @@ namespace Aurora::SWInfo
|
||||
}
|
||||
}
|
||||
|
||||
static void CheckForSupportLicenseHints(OSInformation &osInfo)
|
||||
{
|
||||
AuString a;
|
||||
|
||||
osInfo.bIsEnterprise = false;
|
||||
|
||||
// redhat
|
||||
if (AuFS::ReadString("/etc/system-release-cpe", a))
|
||||
{
|
||||
osInfo.bIsEnterprise |= a.find("nterprise") != AuString::npos; // cpe:?????:enterprise_linux:??????
|
||||
}
|
||||
}
|
||||
|
||||
void InitLinuxInfo(OSInformation &osInfo)
|
||||
{
|
||||
osInfo = AuMove(OSInformation(&gKernelString, &gUserlandBrand, &gUserlandDesktopEnv, &gBuildString, Aurora::Build::EPlatform::eEnumInvalid));
|
||||
@ -172,6 +185,7 @@ namespace Aurora::SWInfo
|
||||
SetProcVersion();
|
||||
SetVersion(osInfo);
|
||||
ParseOSRel(osInfo);
|
||||
CheckForSupportLicenseHints(osInfo);
|
||||
|
||||
SetDesktopWindowManager();
|
||||
}
|
||||
|
@ -15,27 +15,40 @@ namespace Aurora::Threading::Primitives
|
||||
{
|
||||
UnixConditionMutex::UnixConditionMutex()
|
||||
{
|
||||
auto status = pthread_mutex_init(&this->value_, nullptr) == 0;
|
||||
SysAssert(status, "Mutex init failed");
|
||||
}
|
||||
|
||||
UnixConditionMutex::~UnixConditionMutex()
|
||||
{
|
||||
int status = pthread_mutex_destroy(&this->value_);
|
||||
RUNTIME_ASSERT_SHUTDOWN_SAFE(status == 0, "Mutex destruct failed, {} {}", status, errno);
|
||||
}
|
||||
|
||||
void UnixConditionMutex::Lock()
|
||||
{
|
||||
mutex_.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)
|
||||
}
|
||||
|
||||
void UnixConditionMutex::Unlock()
|
||||
{
|
||||
mutex_.Unlock();
|
||||
auto status = pthread_mutex_unlock(&this->value_) == 0;
|
||||
SysAssert(status, "Mutex release error");
|
||||
}
|
||||
|
||||
AuUInt UnixConditionMutex::GetOSHandle()
|
||||
{
|
||||
AuMach handle = 0;
|
||||
SysAssertExp(mutex_.HasOSHandle(handle));
|
||||
return handle;
|
||||
return AuMach(&this->value_);
|
||||
}
|
||||
|
||||
AUKN_SYM IConditionMutex *ConditionMutexNew()
|
||||
|
@ -8,7 +8,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "AuIConditionMutexEx.hpp"
|
||||
#include "AuMutex.Generic.hpp"
|
||||
|
||||
namespace Aurora::Threading::Primitives
|
||||
{
|
||||
@ -23,7 +22,7 @@ namespace Aurora::Threading::Primitives
|
||||
AuUInt GetOSHandle() override;
|
||||
|
||||
private:
|
||||
Mutex mutex_;
|
||||
pthread_mutex_t value_;
|
||||
};
|
||||
|
||||
using ConditionMutexImpl = UnixConditionMutex;
|
||||
|
@ -9,10 +9,12 @@
|
||||
|
||||
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
||||
#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_GENERICMUTEX
|
||||
#define _AURUNTIME_GENERIC_MUTEX
|
||||
|
||||
namespace Aurora::Threading::Primitives
|
||||
{
|
||||
|
176
Source/Threading/Primitives/AuMutex.Linux.cpp
Executable file
176
Source/Threading/Primitives/AuMutex.Linux.cpp
Executable file
@ -0,0 +1,176 @@
|
||||
/***
|
||||
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: AuMutex.Linux.cpp
|
||||
Date: 2022-12-28
|
||||
Author: Reece
|
||||
***/
|
||||
#include <Source/RuntimeInternal.hpp>
|
||||
#include "AuMutex.Generic.hpp"
|
||||
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/futex.h>
|
||||
|
||||
#if !defined(_AURUNTIME_GENERIC_MUTEX)
|
||||
#include <Source/Time/Time.hpp>
|
||||
|
||||
namespace Aurora::Threading::Primitives
|
||||
{
|
||||
static int futex(uint32_t *uaddr, int futex_op, uint32_t val,
|
||||
const struct timespec *timeout,
|
||||
uint32_t *uaddr2, uint32_t val3)
|
||||
{
|
||||
return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3);
|
||||
}
|
||||
|
||||
static int futex_wait(uint32_t *addr, uint32_t expected)
|
||||
{
|
||||
return futex(addr, FUTEX_WAIT, expected, 0, 0, 0);
|
||||
}
|
||||
|
||||
static int futex_wait(uint32_t *addr, uint32_t expected, const struct timespec *timeout)
|
||||
{
|
||||
if (timeout)
|
||||
{
|
||||
return futex(addr, FUTEX_WAIT_BITSET, expected, timeout, 0, FUTEX_BITSET_MATCH_ANY);
|
||||
}
|
||||
else
|
||||
{
|
||||
return futex(addr, FUTEX_WAIT, expected, timeout, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int futex_wake(uint32_t *addr, uint32_t nthreads)
|
||||
{
|
||||
return futex(addr, FUTEX_WAKE, nthreads, 0, 0, 0);
|
||||
}
|
||||
|
||||
Mutex::Mutex()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Mutex::~Mutex()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool Mutex::HasOSHandle(AuMach &mach)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mutex::HasLockImplementation()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mutex::TryLock()
|
||||
{
|
||||
auto old = this->value_;
|
||||
return (old == 0 && AuAtomicCompareExchange<AuUInt32>(&this->value_, 1, old) == old);
|
||||
}
|
||||
|
||||
bool Mutex::Lock(AuUInt64 uTimeout)
|
||||
{
|
||||
if (this->TryLock())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
AuUInt64 uStart = AuTime::SteadyClockMS();
|
||||
AuUInt64 uEnd = uStart + uTimeout;
|
||||
|
||||
struct timespec tspec;
|
||||
if (uTimeout != 0)
|
||||
{
|
||||
Time::ms2tsabs(&tspec, uTimeout);
|
||||
}
|
||||
|
||||
auto state = this->value_;
|
||||
while (!(state == 0 && AuAtomicCompareExchange<AuUInt32>(&this->value_, 1, state) == state))
|
||||
{
|
||||
if (uTimeout != 0)
|
||||
{
|
||||
uStart = Time::SteadyClockMS();
|
||||
if (uStart >= uEnd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int ret {};
|
||||
do
|
||||
{
|
||||
ret = futex_wait(&this->value_, state, &tspec);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ret == EAGAIN || errno == EAGAIN)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ret == ETIMEDOUT || errno == ETIMEDOUT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while (ret == EINTR);
|
||||
|
||||
RUNTIME_ASSERT_SHUTDOWN_SAFE(false, "semaphore timed wait failed: {}", ret)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
int ret {};
|
||||
do
|
||||
{
|
||||
if ((ret = futex_wait(&this->value_, state)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ret == EAGAIN || errno == EAGAIN)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
while (ret == EINTR);
|
||||
|
||||
RUNTIME_ASSERT_SHUTDOWN_SAFE(false, "semaphore wait failed: {}", ret)
|
||||
return false;
|
||||
}
|
||||
|
||||
state = this->value_;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mutex::Lock()
|
||||
{
|
||||
auto status = Lock(0);
|
||||
SysAssert(status, "Couldn't lock mutex");
|
||||
}
|
||||
|
||||
void Mutex::Unlock()
|
||||
{
|
||||
this->value_ = 0;
|
||||
futex_wake(&this->value_, 1);
|
||||
}
|
||||
|
||||
AUKN_SYM IWaitable *MutexNew()
|
||||
{
|
||||
return _new Mutex();
|
||||
}
|
||||
|
||||
AUKN_SYM void MutexRelease(IWaitable *pMutex)
|
||||
{
|
||||
AuSafeDelete<Mutex *>(pMutex);
|
||||
}
|
||||
}
|
||||
#endif
|
27
Source/Threading/Primitives/AuMutex.Linux.hpp
Executable file
27
Source/Threading/Primitives/AuMutex.Linux.hpp
Executable file
@ -0,0 +1,27 @@
|
||||
/***
|
||||
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: AuMutex.Linux.hpp
|
||||
Date: 2022-12-28
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Threading::Primitives
|
||||
{
|
||||
struct Mutex : IWaitable
|
||||
{
|
||||
Mutex();
|
||||
~Mutex();
|
||||
|
||||
bool HasOSHandle(AuMach &mach) override;
|
||||
bool TryLock() override;
|
||||
bool HasLockImplementation() override;
|
||||
void Lock() override;
|
||||
bool Lock(AuUInt64 timeout) override;
|
||||
void Unlock() override;
|
||||
|
||||
private:
|
||||
AuUInt32 value_ {};
|
||||
};
|
||||
}
|
@ -5,98 +5,129 @@
|
||||
Date: 2021-6-12
|
||||
Author: Reece
|
||||
***/
|
||||
|
||||
#include <Source/RuntimeInternal.hpp>
|
||||
#include "AuMutex.Generic.hpp"
|
||||
|
||||
#if !defined(_AURUNTIME_GENERICMUTEX)
|
||||
#include "AuMutex.Unix.hpp"
|
||||
#if !defined(_AURUNTIME_GENERIC_MUTEX) && !defined(AURORA_IS_LINUX_DERIVED)
|
||||
#include <Source/Time/Time.hpp>
|
||||
|
||||
|
||||
namespace Aurora::Threading::Primitives
|
||||
{
|
||||
Mutex::Mutex()
|
||||
{
|
||||
auto status = pthread_mutex_init(&this->value_, nullptr) == 0;
|
||||
SysAssert(status, "Mutex init failed");
|
||||
}
|
||||
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 mutex/CV");
|
||||
}
|
||||
|
||||
Mutex::~Mutex()
|
||||
{
|
||||
int status = pthread_mutex_destroy(&this->value_);
|
||||
RUNTIME_ASSERT_SHUTDOWN_SAFE(status == 0, "Mutex destruct failed, {} {}", status, errno);
|
||||
::pthread_cond_destroy(&this->pthreadCv_);
|
||||
}
|
||||
|
||||
bool Mutex::HasOSHandle(AuMach &mach)
|
||||
{
|
||||
mach = reinterpret_cast<AuMach>(&this->value_);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mutex::TryLock()
|
||||
{
|
||||
return pthread_mutex_trylock(&this->value_) == 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Mutex::HasLockImplementation()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mutex::Lock()
|
||||
|
||||
bool Mutex::TryLock()
|
||||
{
|
||||
auto status = Lock(0);
|
||||
SysAssert(status, "Couldn't lock Mutex object");
|
||||
auto old = this->value_;
|
||||
return (old == 0 && AuAtomicCompareExchange(&this->value_, 1, old) == old);
|
||||
}
|
||||
|
||||
bool Mutex::Lock(AuUInt64 timeout)
|
||||
|
||||
bool Mutex::Lock(AuUInt64 uTimeout)
|
||||
{
|
||||
if (timeout == 0)
|
||||
if (this->TryLock())
|
||||
{
|
||||
int ret {};
|
||||
do
|
||||
{
|
||||
if ((ret = pthread_mutex_lock(&this->value_)) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} while (ret == EINTR);
|
||||
|
||||
RUNTIME_ASSERT_SHUTDOWN_SAFE(false, "mutex lock failed: {}", ret)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
||||
AuUInt64 uStart = AuTime::SteadyClockMS();
|
||||
AuUInt64 uEnd = uStart + uTimeout;
|
||||
|
||||
AU_LOCK_GUARD(this->mutex_);
|
||||
auto mutex = reinterpret_cast<pthread_mutex_t*>(this->mutex_.GetOSHandle());
|
||||
|
||||
struct timespec tspec;
|
||||
if (uTimeout != 0)
|
||||
{
|
||||
struct timespec tspec;
|
||||
Time::ms2tsabsRealtime(&tspec, timeout);
|
||||
Time::ms2tsabs(&tspec, uTimeout);
|
||||
}
|
||||
|
||||
int ret {};
|
||||
|
||||
do
|
||||
while (!this->TryLock())
|
||||
{
|
||||
if (uTimeout != 0)
|
||||
{
|
||||
ret = pthread_mutex_timedlock(&this->value_, &tspec);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ret == ETIMEDOUT)
|
||||
uStart = Time::SteadyClockMS();
|
||||
if (uStart >= uEnd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} while (ret == EINTR);
|
||||
int ret {};
|
||||
do
|
||||
{
|
||||
ret = ::pthread_cond_timedwait(&this->pthreadCv_, mutex, &tspec);
|
||||
|
||||
RUNTIME_ASSERT_SHUTDOWN_SAFE(false, "mutex timed lock failed: {}", ret)
|
||||
return false;
|
||||
if (ret == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ret == ETIMEDOUT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
while (ret == EINTR);
|
||||
|
||||
RUNTIME_ASSERT_SHUTDOWN_SAFE(false, "Mutex timed wait failed: {}", ret)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
int ret {};
|
||||
do
|
||||
{
|
||||
if ((ret = ::pthread_cond_wait(&this->pthreadCv_, mutex)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
while (ret == EINTR);
|
||||
|
||||
RUNTIME_ASSERT_SHUTDOWN_SAFE(false, "Mutex wait failed: {}", ret)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mutex::Lock()
|
||||
{
|
||||
auto status = Lock(0);
|
||||
SysAssert(status, "Couldn't lock mutex");
|
||||
}
|
||||
|
||||
void Mutex::Unlock()
|
||||
{
|
||||
auto status = pthread_mutex_unlock(&this->value_) == 0;
|
||||
SysAssert(status, "Mutex release error");
|
||||
{
|
||||
AU_LOCK_GUARD(this->mutex_);
|
||||
this->value_ = 0;
|
||||
}
|
||||
auto ret = ::pthread_cond_signal(&this->pthreadCv_);
|
||||
SysAssert(ret == 0, "Couldn't wake any mutex waiter");
|
||||
}
|
||||
|
||||
AUKN_SYM IWaitable *MutexNew()
|
||||
@ -104,10 +135,9 @@ namespace Aurora::Threading::Primitives
|
||||
return _new Mutex();
|
||||
}
|
||||
|
||||
AUKN_SYM void MutexRelease(IWaitable *waitable)
|
||||
AUKN_SYM void MutexRelease(IWaitable *pMutex)
|
||||
{
|
||||
AuSafeDelete<Mutex *>(waitable);
|
||||
AuSafeDelete<Mutex *>(pMutex);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -7,6 +7,8 @@
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
#include "AuConditionMutex.Unix.hpp"
|
||||
|
||||
namespace Aurora::Threading::Primitives
|
||||
{
|
||||
struct Mutex : IWaitable
|
||||
@ -22,6 +24,8 @@ namespace Aurora::Threading::Primitives
|
||||
void Unlock() override;
|
||||
|
||||
private:
|
||||
pthread_mutex_t value_;
|
||||
AuInt32 value_ {};
|
||||
pthread_cond_t pthreadCv_ {};
|
||||
UnixConditionMutex mutex_;
|
||||
};
|
||||
}
|
@ -9,8 +9,8 @@
|
||||
|
||||
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
||||
#include "AuSemaphore.NT.hpp"
|
||||
#elif defined(AURORA_IS_XNU_DERIVED)
|
||||
#include "AuSemaphore.Apple.hpp"
|
||||
#elif defined(AURORA_IS_LINUX_DERIVED)
|
||||
#include "AuSemaphore.Linux.hpp"
|
||||
#elif defined(AURORA_HAS_PTHREADS)
|
||||
#include "AuSemaphore.Unix.hpp"
|
||||
#else
|
||||
|
185
Source/Threading/Primitives/AuSemaphore.Linux.cpp
Normal file
185
Source/Threading/Primitives/AuSemaphore.Linux.cpp
Normal file
@ -0,0 +1,185 @@
|
||||
/***
|
||||
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: AuSemaphore.Linux.cpp
|
||||
Date: 2022-12-28
|
||||
Author: Reece
|
||||
***/
|
||||
#include <Source/RuntimeInternal.hpp>
|
||||
#include "AuSemaphore.Generic.hpp"
|
||||
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/futex.h>
|
||||
|
||||
#if !defined(_AURUNTIME_GENERIC_SEMAPHORE)
|
||||
#include <Source/Time/Time.hpp>
|
||||
|
||||
namespace Aurora::Threading::Primitives
|
||||
{
|
||||
static int futex(uint32_t *uaddr, int futex_op, uint32_t val,
|
||||
const struct timespec *timeout,
|
||||
uint32_t *uaddr2, uint32_t val3)
|
||||
{
|
||||
return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3);
|
||||
}
|
||||
|
||||
static int futex_wait(uint32_t *addr, uint32_t expected)
|
||||
{
|
||||
return futex(addr, FUTEX_WAIT, expected, 0, 0, 0);
|
||||
}
|
||||
|
||||
static int futex_wait(uint32_t *addr, uint32_t expected, const struct timespec *timeout)
|
||||
{
|
||||
if (timeout)
|
||||
{
|
||||
return futex(addr, FUTEX_WAIT_BITSET, expected, timeout, 0, FUTEX_BITSET_MATCH_ANY);
|
||||
}
|
||||
else
|
||||
{
|
||||
return futex(addr, FUTEX_WAIT, expected, timeout, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int futex_wake(uint32_t *addr, uint32_t nthreads)
|
||||
{
|
||||
return futex(addr, FUTEX_WAKE, nthreads, 0, 0, 0);
|
||||
}
|
||||
|
||||
Semaphore::Semaphore(long intialValue) : value_(intialValue)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore()
|
||||
{
|
||||
}
|
||||
|
||||
bool Semaphore::HasOSHandle(AuMach &mach)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Semaphore::HasLockImplementation()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Semaphore::TryLock()
|
||||
{
|
||||
auto old = this->value_;
|
||||
return (old != 0 && AuAtomicCompareExchange(&this->value_, old - 1, old) == old);
|
||||
}
|
||||
|
||||
bool Semaphore::Lock(AuUInt64 uTimeout)
|
||||
{
|
||||
if (this->TryLock())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
AuUInt64 uStart = AuTime::SteadyClockMS();
|
||||
AuUInt64 uEnd = uStart + uTimeout;
|
||||
|
||||
struct timespec tspec;
|
||||
if (uTimeout != 0)
|
||||
{
|
||||
Time::ms2tsabs(&tspec, uTimeout);
|
||||
}
|
||||
|
||||
auto old = this->value_;
|
||||
//!tryLock (with old in a scope we can access)
|
||||
while (!((old != 0) &&
|
||||
(AuAtomicCompareExchange(&this->value_, old - 1, old) == old)))
|
||||
{
|
||||
|
||||
if (uTimeout != 0)
|
||||
{
|
||||
uStart = Time::SteadyClockMS();
|
||||
if (uStart >= uEnd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int ret {};
|
||||
do
|
||||
{
|
||||
ret = futex_wait(&this->value_, 0, &tspec);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ret == EAGAIN || errno == EAGAIN)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ret == ETIMEDOUT || errno == ETIMEDOUT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while (ret == EINTR);
|
||||
|
||||
RUNTIME_ASSERT_SHUTDOWN_SAFE(false, "semaphore timed wait failed: {}", ret)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
int ret {};
|
||||
do
|
||||
{
|
||||
if ((ret = futex_wait(&this->value_, 0)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ret == EAGAIN || errno == EAGAIN)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
while (ret == EINTR);
|
||||
|
||||
RUNTIME_ASSERT_SHUTDOWN_SAFE(false, "semaphore wait failed: {}", ret)
|
||||
return false;
|
||||
}
|
||||
|
||||
old = this->value_;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Semaphore::Lock()
|
||||
{
|
||||
auto status = Lock(0);
|
||||
SysAssert(status, "Couldn't lock semaphore");
|
||||
}
|
||||
|
||||
void Semaphore::Unlock(long count)
|
||||
{
|
||||
AuAtomicAdd<AuUInt32>(&this->value_, count);
|
||||
futex_wake(&this->value_, count);
|
||||
}
|
||||
|
||||
void Semaphore::Unlock()
|
||||
{
|
||||
Unlock(0);
|
||||
}
|
||||
|
||||
AUKN_SYM ISemaphore *SemaphoreNew(int iInitialCount)
|
||||
{
|
||||
return _new Semaphore(iInitialCount);
|
||||
}
|
||||
|
||||
AUKN_SYM void SemaphoreRelease(ISemaphore *pSemaphore)
|
||||
{
|
||||
AuSafeDelete<Semaphore *>(pSemaphore);
|
||||
}
|
||||
}
|
||||
#endif
|
28
Source/Threading/Primitives/AuSemaphore.Linux.hpp
Normal file
28
Source/Threading/Primitives/AuSemaphore.Linux.hpp
Normal file
@ -0,0 +1,28 @@
|
||||
/***
|
||||
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: AuSemaphore.Linux.hpp
|
||||
Date: 2022-12-28
|
||||
Author: Reece
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Threading::Primitives
|
||||
{
|
||||
struct Semaphore : ISemaphore
|
||||
{
|
||||
Semaphore(long intialValue = 0);
|
||||
~Semaphore();
|
||||
|
||||
bool HasOSHandle(AuMach &mach) override;
|
||||
bool HasLockImplementation() override;
|
||||
bool TryLock() override;
|
||||
bool Lock(AuUInt64 timeout) override;
|
||||
void Lock() override;
|
||||
void Unlock(long count) override;
|
||||
void Unlock() override;
|
||||
|
||||
private:
|
||||
AuUInt32 value_ {};
|
||||
};
|
||||
}
|
@ -7,29 +7,30 @@
|
||||
***/
|
||||
#include <Source/RuntimeInternal.hpp>
|
||||
#include "AuSemaphore.Generic.hpp"
|
||||
#include "AuSemaphore.Unix.hpp"
|
||||
|
||||
#if !defined(_AURUNTIME_GENERIC_SEMAPHORE) && !defined(AURORA_IS_XNU_DERIVED)
|
||||
#if !defined(_AURUNTIME_GENERIC_SEMAPHORE) && !defined(AURORA_IS_LINUX_DERIVED)
|
||||
#include <Source/Time/Time.hpp>
|
||||
|
||||
namespace Aurora::Threading::Primitives
|
||||
{
|
||||
Semaphore::Semaphore(long intialValue)
|
||||
Semaphore::Semaphore(long intialValue) : value_(intialValue)
|
||||
{
|
||||
auto status = sem_init(&this->value_, 0, intialValue) == 0;
|
||||
SysAssert(status, "Semaphore init failed");
|
||||
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");
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore()
|
||||
{
|
||||
int status = sem_destroy(&this->value_);
|
||||
RUNTIME_ASSERT_SHUTDOWN_SAFE(status == 0, "Semaphore destroy failed: {} {}", status, errno);
|
||||
::pthread_cond_destroy(&this->pthreadCv_);
|
||||
}
|
||||
|
||||
bool Semaphore::HasOSHandle(AuMach &mach)
|
||||
{
|
||||
mach = reinterpret_cast<AuMach>(&this->value_);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Semaphore::HasLockImplementation()
|
||||
@ -39,54 +40,79 @@ namespace Aurora::Threading::Primitives
|
||||
|
||||
bool Semaphore::TryLock()
|
||||
{
|
||||
return sem_trywait(&this->value_) == 0;
|
||||
auto old = this->value_;
|
||||
return (old != 0 && AuAtomicCompareExchange(&this->value_, old - 1, old) == old);
|
||||
}
|
||||
|
||||
bool Semaphore::Lock(AuUInt64 timeout)
|
||||
bool Semaphore::Lock(AuUInt64 uTimeout)
|
||||
{
|
||||
if (timeout == 0)
|
||||
if (this->TryLock())
|
||||
{
|
||||
int ret {};
|
||||
|
||||
do
|
||||
{
|
||||
if ((ret = sem_wait(&this->value_)) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
} while (errno == EINTR);
|
||||
|
||||
SysPanic("semaphore lock failed {}", errno);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
||||
AuUInt64 uStart = AuTime::SteadyClockMS();
|
||||
AuUInt64 uEnd = uStart + uTimeout;
|
||||
|
||||
AU_LOCK_GUARD(this->mutex_);
|
||||
auto mutex = reinterpret_cast<pthread_mutex_t*>(this->mutex_.GetOSHandle());
|
||||
|
||||
struct timespec tspec;
|
||||
if (uTimeout != 0)
|
||||
{
|
||||
struct timespec tspec;
|
||||
Time::ms2tsabsRealtime(&tspec, timeout);
|
||||
|
||||
//
|
||||
int ret {};
|
||||
Time::ms2tsabs(&tspec, uTimeout);
|
||||
}
|
||||
|
||||
do
|
||||
while (!this->TryLock())
|
||||
{
|
||||
if (uTimeout != 0)
|
||||
{
|
||||
ret = sem_timedwait(&this->value_, &tspec);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (errno == ETIMEDOUT)
|
||||
uStart = Time::SteadyClockMS();
|
||||
if (uStart >= uEnd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} while (errno == EINTR);
|
||||
|
||||
SysPanic("semaphore timed lock failed");
|
||||
return true;
|
||||
int ret {};
|
||||
do
|
||||
{
|
||||
ret = ::pthread_cond_timedwait(&this->pthreadCv_, mutex, &tspec);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ret == ETIMEDOUT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
while (ret == EINTR);
|
||||
|
||||
RUNTIME_ASSERT_SHUTDOWN_SAFE(false, "semaphore timed wait failed: {}", ret)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
int ret {};
|
||||
do
|
||||
{
|
||||
if ((ret = ::pthread_cond_wait(&this->pthreadCv_, mutex)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
while (ret == EINTR);
|
||||
|
||||
RUNTIME_ASSERT_SHUTDOWN_SAFE(false, "conditional wait failed: {}", ret)
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Semaphore::Lock()
|
||||
@ -97,26 +123,32 @@ namespace Aurora::Threading::Primitives
|
||||
|
||||
void Semaphore::Unlock(long count)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
AuAtomicAdd<AuInt32>(&this->value_, count);
|
||||
if (count == 1)
|
||||
{
|
||||
Unlock();
|
||||
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 Semaphore::Unlock()
|
||||
{
|
||||
auto status = sem_post(&this->value_) == 0;
|
||||
SysAssert(status, "Semaphore release error");
|
||||
Unlock(0);
|
||||
}
|
||||
|
||||
AUKN_SYM ISemaphore *SemaphoreNew(int initialCount)
|
||||
AUKN_SYM ISemaphore *SemaphoreNew(int iInitialCount)
|
||||
{
|
||||
return _new Semaphore(initialCount);
|
||||
return _new Semaphore(iInitialCount);
|
||||
}
|
||||
|
||||
AUKN_SYM void SemaphoreRelease(ISemaphore *waitable)
|
||||
AUKN_SYM void SemaphoreRelease(ISemaphore *pSemaphore)
|
||||
{
|
||||
AuSafeDelete<Semaphore *>(waitable);
|
||||
AuSafeDelete<Semaphore *>(pSemaphore);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
@ -7,6 +7,8 @@
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
#include "AuConditionMutex.Unix.hpp"
|
||||
|
||||
namespace Aurora::Threading::Primitives
|
||||
{
|
||||
struct Semaphore : ISemaphore
|
||||
@ -23,6 +25,8 @@ namespace Aurora::Threading::Primitives
|
||||
void Unlock() override;
|
||||
|
||||
private:
|
||||
sem_t value_{};
|
||||
AuInt32 value_ {};
|
||||
pthread_cond_t pthreadCv_ {};
|
||||
UnixConditionMutex mutex_;
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user