130 lines
2.1 KiB
C++
Executable File
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
|