/*** Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuSemaphore.Linux.cpp Date: 2022-12-28 Author: Reece ***/ #include #include "AuSemaphore.Generic.hpp" #include "SMPYield.hpp" #include #include #if !defined(_AURUNTIME_GENERIC_SEMAPHORE) #include 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() { return DoTryIf([=]() { 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 (this->TryLock()) { return true; } errno = 0; AuUInt64 uStart = AuTime::SteadyClockNS(); AuUInt64 uEnd = uStart + uTimeout; struct timespec tspec; if (uTimeout != 0) { Time::auabsns2ts(&tspec, uEnd); } 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::SteadyClockNS(); if (uStart >= uEnd) { return false; } int ret {}; do { ret = futex_wait(&this->value_, 0, &tspec); } while (ret == EINTR); } else { int ret {}; bool bStatus {}; do { if ((ret = futex_wait(&this->value_, 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->value_; } return true; } void Semaphore::Lock() { auto status = Lock(0); SysAssert(status, "Couldn't lock semaphore"); } void Semaphore::Unlock(long count) { AuAtomicAdd(&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(pSemaphore); } } #endif