/*** Copyright (C) 2021-2024 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuWoASemaphore.Unix.cpp Date: 2021-6-12 Date: 2023-12-29 Author: Reece ***/ #include #include "AuSemaphore.Generic.hpp" #include "SMTYield.hpp" #if !defined(AURORA_IS_LINUX_DERIVED) #include namespace Aurora::Threading::Primitives { WoAConditionMutex::UnixConditionMutex() { auto status = pthread_mutex_init(&this->value_, nullptr) == 0; SysAssert(status, "Mutex init failed"); } WoAConditionMutex::~UnixConditionMutex() { int status = pthread_mutex_destroy(&this->value_); RUNTIME_ASSERT_SHUTDOWN_SAFE(status == 0, "Mutex destruct failed, {} {}", status, errno); } void WoAConditionMutex::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) } bool WoAConditionMutex::TryLock() { int ret {}; do { if ((ret = pthread_mutex_trylock(&this->value_)) == 0) { return true; } } while (ret == EINTR); return false; } void WoAConditionMutex::Unlock() { auto status = pthread_mutex_unlock(&this->value_) == 0; SysAssert(status, "Mutex release error"); } WoAConditionVariable::WoAConditionVariable() { pthread_condattr_t attr; ::pthread_condattr_init(&attr); ::pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); auto ret = ::pthread_cond_init(&this->pthreadCv_, &attr); SysAssert(ret == 0, "Couldn't initialize CV"); } WoAConditionVariable::~WoAConditionVariable() { ::pthread_cond_destroy(&this->pthreadCv_); } bool WoAConditionVariable::WaitForSignalNsEx(WoAConditionMutex *pMutex, AuUInt64 qwTimeout, bool bSpin) { auto mutex = pMutex->value_; if (gRuntimeRunLevel >= 5) { return true; } if (qwTimeout == 0) { int ret {}; do { if ((ret = ::pthread_cond_wait(&this->pthreadCv_, mutex)) == 0) { return true; } } while (ret == EINTR); RUNTIME_ASSERT_SHUTDOWN_SAFE(false, "conditional wait failed: {}", ret) return false; } else { struct timespec tspec; Time::monoabsns2ts(&tspec, AuTime::SteadyClockNS() + qwTimeout); int ret {}; do { ret = ::pthread_cond_timedwait(&this->pthreadCv_, mutex, &tspec); if (ret == 0) { return true; } if (ret == ETIMEDOUT) { return false; } } while (ret == EINTR); RUNTIME_ASSERT_SHUTDOWN_SAFE(false, "conditional timed wait failed: {}", ret) return false; } } void WoAConditionVariable::Signal() { (void)::pthread_cond_signal(&this->pthreadCv_); } void WoAConditionVariable::Broadcast() { (void)::pthread_cond_broadcast(&this->pthreadCv_); } WoASemaphoreImpl::WoASemaphoreImpl(AuUInt16 uIntialValue) : value_(intialValue) { } WoASemaphoreImpl::~WoASemaphoreImpl() { } bool WoASemaphoreImpl::HasOSHandle(AuMach &mach) { return false; } bool WoASemaphoreImpl::HasLockImplementation() { return true; } bool WoASemaphoreImpl::TryLock() { auto old = this->value_; return (old != 0 && AuAtomicCompareExchange(&this->value_, old - 1, old) == old); } bool WoASemaphoreImpl::LockMS(AuUInt64 uTimeout) { return LockNS(AuMSToNS(uTimeout)); } bool WoASemaphoreImpl::LockNS(AuUInt64 uTimeout) { if (!uTimeout) { while (!this->TryLock()) { AU_LOCK_GUARD(this->mutex_); if (this->TryLock()) { return true; } (void)this->cond_.WaitForSignalNsEx(&this->mutex_, 0, true); } return true; } else { if (this->TryLock()) { return true; } { AU_LOCK_GUARD(this->mutex_); if (this->TryLock()) { return true; } (void)this->cond_.WaitForSignalNsEx(&this->mutex_, uTimeout, true); } return this->TryLock(); } } void WoASemaphoreImpl::Lock() { auto status = this->LockNS(0); SysAssert(status, "Couldn't lock semaphore"); } void WoASemaphoreImpl::Unlock(AuUInt16 count) { { AuAtomicAdd(&this->value_, count); } { AU_LOCK_GUARD(this->mutex_); } if (count > 1) { this->cond_.Broadcast(); } else { this->cond_.Signal(); } } void WoASemaphoreImpl::Unlock() { this->Unlock(1); } } #endif