Files
core_zero/tjp/core/threads/SpinMutex.hpp
Timothy Prepscius 996151a56a flatten 20260225
2026-02-25 12:43:06 -05:00

130 lines
2.1 KiB
C++
Executable File

// 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<size_t> 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<bool> locked { false };
};
#ifdef DEBUG
struct SpinMutex : LockedBy<SpinMutex_>
{
typedef std::lock_guard<SpinMutex> Lock;
typedef UnlockGuard<SpinMutex> Unlock;
typedef TryLockGuard<SpinMutex> TryLock;
} ;
#else
struct SpinMutex : SpinMutex_
{
typedef std::lock_guard<SpinMutex> Lock;
typedef UnlockGuard<SpinMutex> Unlock;
typedef TryLockGuard<SpinMutex> 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