/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuSemaphore.Unix.cpp Date: 2021-6-12 Author: Reece ***/ #include #include "AuSemaphore.Generic.hpp" #include "SMPYield.hpp" #if !defined(_AURUNTIME_GENERIC_SEMAPHORE) && !defined(AURORA_IS_LINUX_DERIVED) #include namespace Aurora::Threading::Primitives { Semaphore::Semaphore(long intialValue) : value_(intialValue) { 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() { ::pthread_cond_destroy(&this->pthreadCv_); } bool Semaphore::HasOSHandle(AuMach &mach) { return false; } bool Semaphore::HasLockImplementation() { return true; } bool Semaphore::TryLock() { // Assume heavyweight pthread_cond_timedwait yield is followed by an instant hit, for now auto old = this->value_; return (old != 0 && AuAtomicCompareExchange(&this->value_, old - 1, old) == old); } bool Semaphore::Lock(AuUInt64 uTimeout) { return LockNS(AuMSToNS(uTimeout)); } bool Semaphore::LockNS(AuUInt64 uTimeout) { if (DoTryIf([=]() { auto old = this->value_; return (old != 0 && AuAtomicCompareExchange(&this->value_, old - 1, old) == old); })) { return true; } errno = 0; AuUInt64 uStart = AuTime::SteadyClockNS(); AuUInt64 uEnd = uStart + uTimeout; AU_LOCK_GUARD(this->mutex_); auto mutex = reinterpret_cast(this->mutex_.GetOSHandle()); struct timespec tspec; if (uTimeout != 0) { Time::auabsns2ts(&tspec, uEnd); } while (!this->TryLock()) { if (uTimeout != 0) { uStart = Time::SteadyClockNS(); if (uStart >= uEnd) { return false; } int ret {}; do { ret = ::pthread_cond_timedwait(&this->pthreadCv_, mutex, &tspec); } while (ret == EINTR); } else { int ret {}; bool bStatus {}; do { if ((ret = ::pthread_cond_wait(&this->pthreadCv_, mutex)) == 0) { bStatus = true; break; } } while (ret == EINTR); RUNTIME_ASSERT_SHUTDOWN_SAFE(bStatus, "semaphore wait failed: {}", ret) } } return true; } void Semaphore::Lock() { auto status = Lock(0); SysAssert(status, "Couldn't lock semaphore"); } void Semaphore::Unlock(long count) { AuAtomicAdd(&this->value_, count); if (count == 1) { 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() { Unlock(0); } AUKN_SYM ISemaphore *SemaphoreNew(int iInitialCount) { return _new Semaphore(iInitialCount); } AUKN_SYM void SemaphoreRelease(ISemaphore *pSemaphore) { AuSafeDelete(pSemaphore); } } #endif