Add spintex
This commit is contained in:
125
openVulkanoCpp/Data/Concurent/Spintex.hpp
Normal file
125
openVulkanoCpp/Data/Concurent/Spintex.hpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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 openVulkanoCpp
|
||||
{
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user