[+] 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:
Reece Wilson 2022-12-28 23:44:45 +00:00
parent 87194ac1ea
commit 2209aeb7a8
16 changed files with 653 additions and 131 deletions

View File

@ -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
{

View File

@ -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();
}

View File

@ -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()

View File

@ -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;

View File

@ -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
{

View 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

View 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_ {};
};
}

View File

@ -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

View File

@ -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_;
};
}

View File

@ -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

View 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

View 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_ {};
};
}

View File

@ -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

View File

@ -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_;
};
}