flatten 20260225

This commit is contained in:
Timothy Prepscius
2026-02-25 12:43:06 -05:00
commit 996151a56a
521 changed files with 46722 additions and 0 deletions

14
tjp/core/threads/Atomic.h Executable file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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

View 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

View 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

View 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

View 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
View 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
}
}

View 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
View 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
View 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
View 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();
}
}

View 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;
}
}