// License: Modified MIT (NON-AI) // See the LICENSE file in the root directory for license information. // Copyright 2025 Timothy Prepscius #pragma once #include "Lock.hpp" #include "LockedBy.hpp" #include "Atomic.hpp" #include "TryLockGuard.hpp" #include "UnlockGuard.hpp" #include "../debug/Debug.h" namespace tjp::core { // https://probablydance.com/2019/12/30/measuring-mutexes-spinlocks-and-how-bad-the-linux-scheduler-really-is/ struct SpinMutexLog { Atomic tries, failures; inline void failed () { tries++; failures++; } inline void succeeded () { tries++; } } ; #ifdef _DEBUG void spin_log(); #define SPIN_LOG() spin_log() #else #define SPIN_LOG() #endif struct SpinMutex_ { #ifdef _DEBUG static SpinMutexLog log; #endif static constexpr int threshold = 16; inline void micropause() const { // _mm_pause(); } void lock() { for (int spin_count = 0; !try_lock(); ++spin_count) { DEBUG_ONLY(log.failed()); if (spin_count < threshold) { micropause(); } else { std::this_thread::yield(); spin_count = 0; } } DEBUG_ONLY(log.succeeded()); } bool try_lock() { return !locked.load(std::memory_order_relaxed) && !locked.exchange(true, std::memory_order_acquire); } void unlock() { locked.store(false, std::memory_order_release); } private: Atomic locked { false }; }; #ifdef DEBUG struct SpinMutex : LockedBy { typedef std::lock_guard Lock; typedef UnlockGuard Unlock; typedef TryLockGuard TryLock; } ; #else struct SpinMutex : SpinMutex_ { typedef std::lock_guard Lock; typedef UnlockGuard Unlock; typedef TryLockGuard TryLock; } ; #endif inline auto lock_of(SpinMutex &m) { return SpinMutex::Lock(m); } inline auto try_lock_of(SpinMutex &m) { return SpinMutex::TryLock(m); } inline auto unlock_of(SpinMutex &m) { return SpinMutex::Unlock(m); } } // namespace