/*** Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuMutex.Linux.cpp Date: 2022-12-28 Author: Reece ***/ #include #include "AuMutex.Generic.hpp" #include #include #include "SMPYield.hpp" #if !defined(_AURUNTIME_GENERIC_MUTEX) #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); } Mutex::Mutex() { } Mutex::~Mutex() { } bool Mutex::HasOSHandle(AuMach &mach) { return false; } bool Mutex::HasLockImplementation() { return true; } bool Mutex::TryLock() { return DoTryIf([=]() { auto old = this->value_; return (old == 0 && AuAtomicCompareExchange(&this->value_, 1, old) == old); }); } bool Mutex::Lock(AuUInt64 uTimeout) { return LockNS(AuMSToNS(uTimeout)); } bool Mutex::LockNS(AuUInt64 uTimeout) { if (this->TryLock()) { return true; } AuUInt64 uStart = AuTime::SteadyClockNS(); AuUInt64 uEnd = uStart + uTimeout; struct timespec tspec; if (uTimeout != 0) { Time::auabsns2ts(&tspec, uEnd); } auto state = this->value_; while (!(state == 0 && AuAtomicCompareExchange(&this->value_, 1, state) == state)) { if (uTimeout != 0) { uStart = Time::SteadyClockNS(); if (uStart >= uEnd) { return false; } int ret {}; do { ret = futex_wait(&this->value_, state, &tspec); } while (ret == EINTR); } else { int ret {}; bool bStatus {}; do { if ((ret = futex_wait(&this->value_, state)) == 0) { bStatus = true; break; } if (ret == EAGAIN || errno == EAGAIN) { bStatus = true; break; } } while (ret == EINTR); RUNTIME_ASSERT_SHUTDOWN_SAFE(bStatus, "Mutex wait failed: {}", ret) } 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(pMutex); } } #endif