Skip to content

Instantly share code, notes, and snippets.

@jhaberstro
Created April 15, 2016 08:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jhaberstro/32e7f18b3c1878c945a1bd34efe412ad to your computer and use it in GitHub Desktop.
Save jhaberstro/32e7f18b3c1878c945a1bd34efe412ad to your computer and use it in GitHub Desktop.
C++11 recursive spinlock
// spinlock.h
#include <thread>
class Mutex
{
public:
Mutex();
Mutex(Mutex const&) = delete;
Mutex& operator=(Mutex) = delete;
/**
* Acquires exclusive access to this mutex.
* This is recursion-safe, i.e. from one thread it can be called multiple
* times.
*/
void acquire();
/**
* Releases a previously acquired lock.
* Number of calls should match acquire() to fully release access.
*/
void release();
protected:
size_t m_RecursionCount;
std::atomic<size_t> m_LockCount;
std::atomic<size_t> m_Owner;
};
// spinlock.cpp
Mutex::Mutex()
: m_RecursionCount(0)
, m_LockCount(0)
, m_Owner()
{}
void Mutex::acquire()
{
auto current_thread = std::this_thread::get_id();
std::hash<std::thread::id> thread_hasher;
size_t thread_hash = thread_hash(current_thread);
size_t old_hash;
// Attempt to acquire the mutex.
while (true)
{
size_t old_count = m_LockCount.exchange(1, std::memory_order::memory_order_acquire);
// Freshly acquired the lock, so set the owner
if (old_count == 0)
{
assert(m_RecursionCount == 0);
m_Owner.store(thread_hash, std::memory_order::memory_order_relaxed);
break
}
// Lock is already acquired, must be calling it recursively to be acquiring it
if (old_count == 1 && m_Owner.load(std::memory_order::memory_order_relaxed) == thread_hash)
{
assert(m_RecursionCount > 0);
break;
}
}
// Mutex acquired, increment the recursion counter
m_RecursionCount += 1;
}
void Mutex::release()
{
auto current_thread = std::this_thread::get_id();
assert(m_Owner == current_thread);
// decrement the recursion count and if we reach 0, actually release the lock
m_RecursionCount -= 1;
if (m_RecursionCount == 0)
{
std::hash<std::thread::id> thread_hasher;
m_Owner.store(thread_hasher(std::thread::id()), std::memory_order::memory_order_relaxed);
m_LockCount.exchange(0, std::memory_order::memory_order_release);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment