/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuMutex.Unix.cpp Date: 2021-6-12 Author: Reece ***/ #include #include "AuMutex.Generic.hpp" #include "SMTYield.hpp" #if !defined(_AURUNTIME_GENERIC_MUTEX) && !defined(AURORA_IS_LINUX_DERIVED) #include namespace Aurora::Threading::Primitives { #define barrier() __asm__ __volatile__("sfence": : :"memory") #define compilerReorderBarrier() __asm__ __volatile__("": : :"memory") MutexImpl::MutexImpl() { 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"); } MutexImpl::~MutexImpl() { ::pthread_cond_destroy(&this->pthreadCv_); } bool MutexImpl::HasOSHandle(AuMach &mach) { return false; } bool MutexImpl::HasLockImplementation() { return true; } bool MutexImpl::TryLockNoSpin() { auto old = this->value_; return (old == 0 && AuAtomicCompareExchange(&this->value_, 1, old) == old); } bool MutexImpl::TryLock() { if (gRuntimeConfig.threadingConfig.bPreferUnixPrimitivesNoSpin) { return this->TryLockNoSpin(); } else { return return DoTryIf([=]() { return this->TryLockNoSpin(); }); } } bool MutexImpl::LockMS(AuUInt64 uTimeout) { return LockNS(AuMSToNS(uTimeout)); } bool MutexImpl::LockNS(AuUInt64 uTimeout) { AuUInt64 uStart {}; AuUInt64 uEnd {}; if (this->TryLock()) { return true; } AuAtomicAdd(&this->dwSleeping_, 1u); auto mutex = reinterpret_cast(this->mutex_.GetOSHandle()); struct timespec tspec; if (uTimeout != 0) { uStart = AuTime::SteadyClockNS(); uEnd = uStart + uTimeout; Time::monoabsns2ts(&tspec, uEnd); } int ret {}; while (!this->TryLock()) { AU_LOCK_GUARD(this->mutex_); if (this->TryLockNoSpin()) { return true; } if (uTimeout != 0) { if (Time::SteadyClockNS() >= uEnd) { AuAtomicSub(&this->dwSleeping_, 1u); return false; } do { ret = ::pthread_cond_timedwait(&this->pthreadCv_, mutex, &tspec); } while (ret == EINTR); } else { bool bStatus {}; do { if ((ret = ::pthread_cond_wait(&this->pthreadCv_, mutex)) == 0) { bStatus = true; break; } } while (ret == EINTR); RUNTIME_ASSERT_SHUTDOWN_SAFE(bStatus, "Mutex wait failed: {}", ret) } } AuAtomicSub(&this->dwSleeping_, 1u); return true; } void MutexImpl::Lock() { auto status = LockNS(0); SysAssert(status, "Couldn't lock mutex"); } void MutexImpl::Unlock() { __sync_lock_release(&this->value_); compilerReorderBarrier(); if (this->dwSleeping_) { { // Still required to barrier the mutually exclusive part of the condvar 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() { return _new MutexImpl(); } AUKN_SYM void MutexRelease(IWaitable *pMutex) { AuSafeDelete(pMutex); } AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, Mutex, MutexImpl) } #endif