Created
April 15, 2016 08:34
-
-
Save jhaberstro/32e7f18b3c1878c945a1bd34efe412ad to your computer and use it in GitHub Desktop.
C++11 recursive spinlock
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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