flatten 20260225
This commit is contained in:
14
tjp/core/threads/Atomic.h
Executable file
14
tjp/core/threads/Atomic.h
Executable file
@@ -0,0 +1,14 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
template<typename T>
|
||||
using Atomic = std::atomic<T>;
|
||||
|
||||
} // namespace
|
||||
7
tjp/core/threads/Atomic.hpp
Executable file
7
tjp/core/threads/Atomic.hpp
Executable file
@@ -0,0 +1,7 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Atomic.h"
|
||||
12
tjp/core/threads/Lock.h
Executable file
12
tjp/core/threads/Lock.h
Executable file
@@ -0,0 +1,12 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(WIN32) && !defined(WIN64)
|
||||
#include "std/Lock.h"
|
||||
#else
|
||||
#include "windows/Lock.h"
|
||||
#endif
|
||||
|
||||
12
tjp/core/threads/Lock.hpp
Executable file
12
tjp/core/threads/Lock.hpp
Executable file
@@ -0,0 +1,12 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(WIN32) && !defined(WIN64)
|
||||
#include "std/Lock.hpp"
|
||||
#else
|
||||
#include "windows/Lock.hpp"
|
||||
#endif
|
||||
|
||||
12
tjp/core/threads/LockedBy.h
Executable file
12
tjp/core/threads/LockedBy.h
Executable file
@@ -0,0 +1,12 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
template<typename T>
|
||||
class LockedBy;
|
||||
|
||||
} // namespace
|
||||
56
tjp/core/threads/LockedBy.hpp
Executable file
56
tjp/core/threads/LockedBy.hpp
Executable file
@@ -0,0 +1,56 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "LockedBy.h"
|
||||
#include <thread>
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
template<typename T>
|
||||
class LockedBy
|
||||
{
|
||||
public:
|
||||
T m;
|
||||
|
||||
void lock()
|
||||
{
|
||||
m.lock();
|
||||
if (++rentry == 1)
|
||||
m_holder = std::this_thread::get_id();
|
||||
}
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
auto locked = m.try_lock();
|
||||
if (locked)
|
||||
{
|
||||
if (++rentry == 1)
|
||||
m_holder = std::this_thread::get_id();
|
||||
}
|
||||
|
||||
return locked;
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
if (--rentry == 0)
|
||||
m_holder = {};
|
||||
m.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff the mutex is locked by the caller of this method. */
|
||||
bool locked_by_caller() const
|
||||
{
|
||||
return m_holder == std::this_thread::get_id();
|
||||
}
|
||||
|
||||
private:
|
||||
size_t rentry = 0;
|
||||
std::thread::id m_holder;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
11
tjp/core/threads/Pin.h
Executable file
11
tjp/core/threads/Pin.h
Executable file
@@ -0,0 +1,11 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace tjp::core::threads {
|
||||
|
||||
struct Pin;
|
||||
|
||||
} // namespace
|
||||
27
tjp/core/threads/Pin.hpp
Executable file
27
tjp/core/threads/Pin.hpp
Executable file
@@ -0,0 +1,27 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Pin.h"
|
||||
#include <thread>
|
||||
|
||||
namespace tjp::core::threads {
|
||||
|
||||
struct Pin
|
||||
{
|
||||
std::thread::id id;
|
||||
|
||||
void pin(const std::thread &thread)
|
||||
{
|
||||
id = thread.get_id();
|
||||
}
|
||||
|
||||
bool on_thread()
|
||||
{
|
||||
return std::this_thread::get_id() == id;
|
||||
}
|
||||
} ;
|
||||
|
||||
} // namespace
|
||||
43
tjp/core/threads/SharedLockGuard.hpp
Executable file
43
tjp/core/threads/SharedLockGuard.hpp
Executable file
@@ -0,0 +1,43 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
template<typename SharedMutex>
|
||||
class SharedLockGuard
|
||||
{
|
||||
private:
|
||||
SharedMutex& m;
|
||||
bool locked = true;
|
||||
|
||||
public:
|
||||
typedef SharedMutex mutex_type;
|
||||
explicit SharedLockGuard(SharedMutex& m_):
|
||||
m(m_)
|
||||
{
|
||||
m.lock_shared();
|
||||
}
|
||||
|
||||
SharedLockGuard(SharedMutex& m_,std::adopt_lock_t):
|
||||
m(m_)
|
||||
{}
|
||||
|
||||
~SharedLockGuard()
|
||||
{
|
||||
unlock();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
if (!locked)
|
||||
return ;
|
||||
|
||||
locked = false;
|
||||
m.unlock_shared();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
25
tjp/core/threads/SpinMutex.cpp
Executable file
25
tjp/core/threads/SpinMutex.cpp
Executable file
@@ -0,0 +1,25 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#include "SpinMutex.hpp"
|
||||
|
||||
#include "../debug/Debug.h"
|
||||
#include "../log/Log.h"
|
||||
#include "../log/LogOf.h"
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
#if IS_DEBUG()
|
||||
SpinMutexLog SpinMutex_::log = { 0, 0 };
|
||||
|
||||
void spin_log()
|
||||
{
|
||||
auto &log = SpinMutex_::log;
|
||||
sLogDebug("core::SpinMutex", logVar(log.tries) << logVar(log.failures));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
} // namespace
|
||||
129
tjp/core/threads/SpinMutex.hpp
Executable file
129
tjp/core/threads/SpinMutex.hpp
Executable file
@@ -0,0 +1,129 @@
|
||||
// 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
|
||||
79
tjp/core/threads/TestCollision.h
Executable file
79
tjp/core/threads/TestCollision.h
Executable file
@@ -0,0 +1,79 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include "Lock.hpp"
|
||||
#include "../assert/release_assert.h"
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
struct ColliderImp
|
||||
{
|
||||
Mutex m;
|
||||
std::atomic<int> read = 0;
|
||||
std::atomic<int> write = 0;
|
||||
} ;
|
||||
|
||||
struct TestCollisionReadImp
|
||||
{
|
||||
ColliderImp &collider;
|
||||
|
||||
TestCollisionReadImp (ColliderImp &collider_) :
|
||||
collider(collider_)
|
||||
{
|
||||
auto l = lock_of(collider.m);
|
||||
release_assert(collider.write == 0);
|
||||
collider.read++;
|
||||
}
|
||||
|
||||
~TestCollisionReadImp ()
|
||||
{
|
||||
collider.read--;
|
||||
}
|
||||
};
|
||||
|
||||
struct TestCollisionWriteImp
|
||||
{
|
||||
ColliderImp &collider;
|
||||
|
||||
TestCollisionWriteImp (ColliderImp &collider_) :
|
||||
collider(collider_)
|
||||
{
|
||||
auto l = lock_of(collider.m);
|
||||
release_assert(collider.write == 0);
|
||||
release_assert(collider.read == 0);
|
||||
|
||||
collider.write++;
|
||||
}
|
||||
|
||||
~TestCollisionWriteImp ()
|
||||
{
|
||||
collider.write--;
|
||||
}
|
||||
};
|
||||
|
||||
struct ColliderEmpty {};
|
||||
struct TestCollisionEmpty
|
||||
{
|
||||
TestCollisionEmpty (ColliderEmpty &_){ (void)_; }
|
||||
} ;
|
||||
|
||||
#ifdef DEBUG
|
||||
using Collider = ColliderImp;
|
||||
using TestCollisionRead = TestCollisionReadImp;
|
||||
using TestCollisionWrite = TestCollisionWriteImp;
|
||||
#else
|
||||
using Collider = ColliderEmpty;
|
||||
using TestCollisionRead = TestCollisionEmpty;
|
||||
using TestCollisionWrite = TestCollisionEmpty;
|
||||
#endif
|
||||
|
||||
using ColliderAlways = ColliderImp;
|
||||
using TestCollisionReadAlways = TestCollisionReadImp;
|
||||
using TestCollisionWriteAlways = TestCollisionReadImp;
|
||||
|
||||
} // namespace
|
||||
|
||||
12
tjp/core/threads/TestMutex.h
Executable file
12
tjp/core/threads/TestMutex.h
Executable file
@@ -0,0 +1,12 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
struct TestMutex;
|
||||
|
||||
} // namespace
|
||||
|
||||
70
tjp/core/threads/TestMutex.hpp
Executable file
70
tjp/core/threads/TestMutex.hpp
Executable file
@@ -0,0 +1,70 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include "Lock.h"
|
||||
#include "../system/Debug.h"
|
||||
#include <thread>
|
||||
#include "../types/Types.h"
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define USE_REAL_TEST_MUTEX
|
||||
#endif
|
||||
|
||||
#ifdef USE_REAL_TEST_MUTEX
|
||||
|
||||
struct TestMutex
|
||||
{
|
||||
struct Lock;
|
||||
|
||||
using V = size_t;
|
||||
std::atomic<V> v = 0;
|
||||
std::atomic<u64> c = 0;
|
||||
|
||||
Lock lock_of();
|
||||
|
||||
u64 get_thread_id() const;
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
bool locked_by_caller() const;
|
||||
} ;
|
||||
|
||||
struct TestMutex::Lock
|
||||
{
|
||||
TestMutex *m = nullptr;
|
||||
u64 id;
|
||||
|
||||
Lock ();
|
||||
Lock (TestMutex::Lock &&rhs);
|
||||
Lock (TestMutex &m_);
|
||||
|
||||
~Lock ();
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
struct TestMutex {
|
||||
struct Lock {
|
||||
// avoids the unused variable warning
|
||||
~Lock() {}
|
||||
};
|
||||
|
||||
void lock () {}
|
||||
void unlock () {}
|
||||
|
||||
Lock lock_of () { return TestMutex::Lock {}; }
|
||||
bool locked_by_caller() { return true; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
#include "TestMutex.inl"
|
||||
92
tjp/core/threads/TestMutex.inl
Executable file
92
tjp/core/threads/TestMutex.inl
Executable file
@@ -0,0 +1,92 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include "Lock.h"
|
||||
|
||||
#include "../system/Debug.h"
|
||||
#include "../assert/debug_assert.h"
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
#ifdef USE_REAL_TEST_MUTEX
|
||||
|
||||
inline
|
||||
TestMutex::Lock TestMutex::lock_of()
|
||||
{
|
||||
return Lock(*this);
|
||||
}
|
||||
|
||||
inline
|
||||
void TestMutex::lock ()
|
||||
{
|
||||
auto id = get_thread_id();
|
||||
auto next = id;
|
||||
auto previous = v.exchange(next);
|
||||
|
||||
debug_assert(previous == 0 || previous == id);
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
inline
|
||||
void TestMutex::unlock ()
|
||||
{
|
||||
auto id = get_thread_id();
|
||||
auto next = (--c) ? get_thread_id() : 0;
|
||||
|
||||
auto previous = v.exchange(next);
|
||||
debug_assert(previous == id);
|
||||
}
|
||||
|
||||
inline
|
||||
u64 TestMutex::get_thread_id() const
|
||||
{
|
||||
return std::hash<std::thread::id>()(std::this_thread::get_id());
|
||||
}
|
||||
|
||||
inline
|
||||
bool TestMutex::locked_by_caller() const
|
||||
{
|
||||
return v == get_thread_id();
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
TestMutex::Lock::Lock ()
|
||||
{
|
||||
}
|
||||
|
||||
inline
|
||||
TestMutex::Lock::Lock (TestMutex::Lock &&rhs)
|
||||
{
|
||||
std::swap(rhs.m, m);
|
||||
}
|
||||
|
||||
inline
|
||||
TestMutex::Lock::Lock (TestMutex &m_)
|
||||
{
|
||||
m_.lock();
|
||||
m = &m_;
|
||||
}
|
||||
|
||||
inline
|
||||
TestMutex::Lock::~Lock ()
|
||||
{
|
||||
if (m)
|
||||
m->unlock();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
inline
|
||||
auto lock_of(TestMutex &m)
|
||||
{
|
||||
return m.lock_of();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
12
tjp/core/threads/ThreadSafe.h
Executable file
12
tjp/core/threads/ThreadSafe.h
Executable file
@@ -0,0 +1,12 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
template<typename T, typename MutexType = Mutex>
|
||||
struct ThreadSafe;
|
||||
|
||||
} // namespace
|
||||
65
tjp/core/threads/ThreadSafe.hpp
Executable file
65
tjp/core/threads/ThreadSafe.hpp
Executable file
@@ -0,0 +1,65 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Lock.h"
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
template<typename T, typename MutexType = Mutex>
|
||||
struct Accessor;
|
||||
|
||||
template<typename T, typename MutexType = Mutex>
|
||||
struct ThreadSafe
|
||||
{
|
||||
using A = Accessor<T, MutexType>;
|
||||
using A_const = Accessor<const T, MutexType>;
|
||||
|
||||
T v;
|
||||
mutable MutexType mutex;
|
||||
|
||||
A access ()
|
||||
{
|
||||
return A { &v, &mutex };
|
||||
}
|
||||
|
||||
A_const access () const
|
||||
{
|
||||
return A_const { &v, &mutex };
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
auto with(F &&f)
|
||||
{
|
||||
auto lk = lock_of(mutex);
|
||||
return f(v);
|
||||
}
|
||||
} ;
|
||||
|
||||
template<typename T, typename MutexType>
|
||||
struct Accessor
|
||||
{
|
||||
using V = ThreadSafe<T, MutexType>;
|
||||
T *v;
|
||||
typename MutexType::Lock lock;
|
||||
|
||||
Accessor(T *v_, MutexType *m) :
|
||||
v(v_),
|
||||
lock(*m)
|
||||
{
|
||||
}
|
||||
|
||||
T *operator ->()
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
T &operator *()
|
||||
{
|
||||
return *v;
|
||||
}
|
||||
} ;
|
||||
|
||||
} // namespace
|
||||
34
tjp/core/threads/TryLockGuard.hpp
Executable file
34
tjp/core/threads/TryLockGuard.hpp
Executable file
@@ -0,0 +1,34 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
template<typename T>
|
||||
class TryLockGuard
|
||||
{
|
||||
public:
|
||||
T &m;
|
||||
bool locked = false;
|
||||
|
||||
explicit TryLockGuard(T &m_) :
|
||||
m(m_)
|
||||
{
|
||||
locked = m.try_lock();
|
||||
}
|
||||
|
||||
~TryLockGuard()
|
||||
{
|
||||
if (locked)
|
||||
m.unlock();
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return locked;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
27
tjp/core/threads/UnlockGuard.hpp
Executable file
27
tjp/core/threads/UnlockGuard.hpp
Executable file
@@ -0,0 +1,27 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
template<typename T>
|
||||
class UnlockGuard
|
||||
{
|
||||
public:
|
||||
T &m;
|
||||
|
||||
explicit UnlockGuard(T &m_) :
|
||||
m(m_)
|
||||
{
|
||||
m.unlock();
|
||||
}
|
||||
|
||||
~UnlockGuard()
|
||||
{
|
||||
m.lock();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
25
tjp/core/threads/_tests/Lock.cpp
Executable file
25
tjp/core/threads/_tests/Lock.cpp
Executable file
@@ -0,0 +1,25 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#include <tjp/core/threads/Lock.hpp>
|
||||
|
||||
#include <tjp/core/testing/catch.hpp>
|
||||
|
||||
#include <tjp/core/log/Log.h>
|
||||
#include <tjp/core/log/LogOf.h>
|
||||
|
||||
namespace tjp::core {
|
||||
namespace {
|
||||
|
||||
SCENARIO("core::threads::lock")
|
||||
{
|
||||
GIVEN("Mutex")
|
||||
{
|
||||
Mutex m;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
5
tjp/core/threads/_tests/TestMutex.cpp
Executable file
5
tjp/core/threads/_tests/TestMutex.cpp
Executable file
@@ -0,0 +1,5 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#include <tjp/core/threads/TestMutex.hpp>
|
||||
30
tjp/core/threads/micropause.hpp
Executable file
30
tjp/core/threads/micropause.hpp
Executable file
@@ -0,0 +1,30 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(__aarch64__)
|
||||
|
||||
#elif defined(__x86_64__)
|
||||
#include <immintrin.h>
|
||||
#else
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
inline
|
||||
void micropause()
|
||||
{
|
||||
#if defined(__aarch64__)
|
||||
__builtin_arm_yield();
|
||||
#elif defined(__x86_64__)
|
||||
_mm_pause();
|
||||
#else
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
39
tjp/core/threads/process_queue.hpp
Executable file
39
tjp/core/threads/process_queue.hpp
Executable file
@@ -0,0 +1,39 @@
|
||||
// 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"
|
||||
|
||||
namespace tjp::core::threads {
|
||||
|
||||
template<typename Finished, typename Event, typename Mutex, typename Queue, typename Run>
|
||||
void process_queue (
|
||||
Finished &finished,
|
||||
Event &event,
|
||||
Mutex &mutex,
|
||||
Queue &queue,
|
||||
Run &&run)
|
||||
{
|
||||
auto lock = lock_of(mutex);
|
||||
while (!finished)
|
||||
{
|
||||
if (!queue.empty())
|
||||
{
|
||||
{
|
||||
auto unlock = unlock_of(mutex);
|
||||
run(queue.front());
|
||||
}
|
||||
|
||||
queue.pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
event.wait(mutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
17
tjp/core/threads/std/Lock.h
Executable file
17
tjp/core/threads/std/Lock.h
Executable file
@@ -0,0 +1,17 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
struct RecursiveMutex;
|
||||
struct Mutex;
|
||||
struct SharedMutex;
|
||||
struct Event;
|
||||
|
||||
struct EmptyLock;
|
||||
struct EmptyMutex;
|
||||
|
||||
} // namespace
|
||||
227
tjp/core/threads/std/Lock.hpp
Executable file
227
tjp/core/threads/std/Lock.hpp
Executable file
@@ -0,0 +1,227 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "../../assert/debug_assert.h"
|
||||
#include <vector>
|
||||
|
||||
#include "../LockedBy.hpp"
|
||||
|
||||
#include "../SharedLockGuard.hpp"
|
||||
#include "../UnlockGuard.hpp"
|
||||
#include "../TryLockGuard.hpp"
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
struct RecursiveMutex : LockedBy<std::recursive_mutex>
|
||||
{
|
||||
typedef std::lock_guard<RecursiveMutex> Lock;
|
||||
typedef TryLockGuard<RecursiveMutex> TryLock;
|
||||
} ;
|
||||
|
||||
struct Mutex : LockedBy<std::mutex>
|
||||
{
|
||||
typedef LockedBy<std::mutex> Super;
|
||||
typedef std::lock_guard<Mutex> Lock;
|
||||
typedef UnlockGuard<Mutex> Unlock;
|
||||
typedef TryLockGuard<Mutex> TryLock;
|
||||
|
||||
void lock ()
|
||||
{
|
||||
debug_assert(!locked_by_caller());
|
||||
Super::lock();
|
||||
}
|
||||
|
||||
auto try_lock()
|
||||
{
|
||||
return Super::try_lock();
|
||||
}
|
||||
|
||||
void unlock ()
|
||||
{
|
||||
Super::unlock();
|
||||
}
|
||||
} ;
|
||||
|
||||
struct SharedMutex : LockedBy<std::shared_mutex>
|
||||
{
|
||||
typedef LockedBy<std::shared_mutex> Super;
|
||||
typedef std::lock_guard<SharedMutex> Lock;
|
||||
typedef SharedLockGuard<SharedMutex> SharedLock;
|
||||
typedef TryLockGuard<SharedMutex> TryLock;
|
||||
|
||||
void lock ()
|
||||
{
|
||||
debug_assert(!locked_by_caller());
|
||||
Super::lock();
|
||||
}
|
||||
|
||||
auto try_lock()
|
||||
{
|
||||
return Super::try_lock();
|
||||
}
|
||||
|
||||
std::mutex shared_locks_mutex;
|
||||
std::vector<std::thread::id> shared_locks;
|
||||
|
||||
void lock_shared ()
|
||||
{
|
||||
m.lock_shared();
|
||||
|
||||
std::lock_guard l(shared_locks_mutex);
|
||||
shared_locks.push_back(std::this_thread::get_id());
|
||||
}
|
||||
|
||||
void unlock_shared ()
|
||||
{
|
||||
m.unlock_shared();
|
||||
|
||||
auto id = std::this_thread::get_id();
|
||||
|
||||
std::lock_guard l(shared_locks_mutex);
|
||||
for (auto &id_ : shared_locks)
|
||||
{
|
||||
if (id_ == id)
|
||||
{
|
||||
id_ = shared_locks.back();
|
||||
shared_locks.pop_back();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool shared_locked_by_caller()
|
||||
{
|
||||
auto id = std::this_thread::get_id();
|
||||
{
|
||||
std::lock_guard l(shared_locks_mutex);
|
||||
for (auto &id_ : shared_locks)
|
||||
if (id_ == id)
|
||||
return true;
|
||||
}
|
||||
|
||||
return locked_by_caller();
|
||||
}
|
||||
} ;
|
||||
|
||||
#else
|
||||
|
||||
struct RecursiveMutex : std::recursive_mutex
|
||||
{
|
||||
typedef std::lock_guard<RecursiveMutex> Lock;
|
||||
typedef UnlockGuard<RecursiveMutex> Unlock;
|
||||
using TryLock = TryLockGuard<RecursiveMutex>;
|
||||
} ;
|
||||
|
||||
struct Mutex : std::mutex {
|
||||
typedef std::lock_guard<Mutex> Lock;
|
||||
typedef UnlockGuard<Mutex> Unlock;
|
||||
using TryLock = TryLockGuard<Mutex>;
|
||||
} ;
|
||||
|
||||
struct SharedMutex : std::shared_mutex
|
||||
{
|
||||
typedef std::lock_guard<SharedMutex> Lock;
|
||||
typedef SharedLockGuard<SharedMutex> SharedLock;
|
||||
using TryLock = TryLockGuard<SharedMutex>;
|
||||
} ;
|
||||
|
||||
#endif
|
||||
|
||||
struct Event : std::condition_variable_any
|
||||
{
|
||||
struct Mutex : core::Mutex {
|
||||
using Lock = std::unique_lock<Mutex>;
|
||||
using TryLock = TryLockGuard<Mutex>;
|
||||
} ;
|
||||
} ;
|
||||
|
||||
struct EmptyMutex
|
||||
{
|
||||
struct Lock {};
|
||||
struct TryLock { operator bool() { return true; }};
|
||||
};
|
||||
|
||||
inline
|
||||
auto lock_of(RecursiveMutex &m)
|
||||
{
|
||||
return RecursiveMutex::Lock(m);
|
||||
}
|
||||
|
||||
inline
|
||||
auto lock_of(Mutex &m)
|
||||
{
|
||||
return Mutex::Lock(m);
|
||||
}
|
||||
|
||||
inline
|
||||
auto unlock_of(Mutex &m)
|
||||
{
|
||||
return Mutex::Unlock(m);
|
||||
}
|
||||
|
||||
inline
|
||||
auto unique_lock_of(Mutex &m)
|
||||
{
|
||||
return std::unique_lock(m);
|
||||
}
|
||||
|
||||
inline
|
||||
auto try_lock_of(Mutex &m)
|
||||
{
|
||||
return Mutex::TryLock(m);
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
auto lock_of(Event::Mutex &m)
|
||||
{
|
||||
return Event::Mutex::Lock(m);
|
||||
}
|
||||
|
||||
inline
|
||||
auto try_lock_of(Event::Mutex &m)
|
||||
{
|
||||
return Event::Mutex::TryLock(m);
|
||||
}
|
||||
|
||||
inline
|
||||
auto lock_of(SharedMutex &m)
|
||||
{
|
||||
return SharedMutex::Lock(m);
|
||||
}
|
||||
|
||||
inline
|
||||
auto try_lock_of(SharedMutex &m)
|
||||
{
|
||||
return SharedMutex::TryLock(m);
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
auto lock_of(EmptyMutex &m)
|
||||
{
|
||||
return EmptyMutex::Lock{};
|
||||
}
|
||||
|
||||
inline
|
||||
auto try_lock_of(EmptyMutex &m)
|
||||
{
|
||||
return EmptyMutex::TryLock{};
|
||||
}
|
||||
|
||||
inline
|
||||
SharedLockGuard<SharedMutex> shared_lock_of(SharedMutex &m_)
|
||||
{
|
||||
return SharedMutex::SharedLock(m_);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
16
tjp/core/threads/with_lock.hpp
Executable file
16
tjp/core/threads/with_lock.hpp
Executable file
@@ -0,0 +1,16 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
template<typename T, typename F>
|
||||
auto with_lock(T &t, F &&f)
|
||||
{
|
||||
auto lock = lock_of(t);
|
||||
return f();
|
||||
}
|
||||
|
||||
}
|
||||
16
tjp/core/threads/with_lock_value.hpp
Executable file
16
tjp/core/threads/with_lock_value.hpp
Executable file
@@ -0,0 +1,16 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
template<typename T, typename F>
|
||||
F with_lock_value(T &t, const F &f)
|
||||
{
|
||||
auto lock = lock_of(t);
|
||||
return f;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user