Simple Read-Write Lock implemented with full C++11 compatible
This file contains 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
#ifndef _RW_LOCK_HPP_ | |
#define _RW_LOCK_HPP_ | |
#include <condition_variable> | |
#include <mutex> | |
#include <atomic> | |
#include <limits> | |
namespace rwlock { | |
enum WaitGenre { | |
SPIN, | |
SLEEP | |
}; | |
template <WaitGenre WAIT_GENRE> | |
class RWLock { | |
std::mutex wait_mutex_; | |
std::condition_variable wait_cv_; | |
std::atomic_int ref_counter_; | |
const int MIN_INT; | |
public: | |
RWLock(); | |
void LockWrite(); | |
bool TryLockWrite(); | |
void UnLockWrite(); | |
void LockRead(); | |
bool TryLockRead(); | |
void UnLockRead(); | |
}; | |
template <WaitGenre WAIT_GENRE> | |
RWLock<WAIT_GENRE>::RWLock() : | |
ref_counter_(0), | |
MIN_INT(std::numeric_limits<int>::min()){} | |
template <> | |
void RWLock<SPIN>::LockWrite() { | |
int expected = 0; | |
while(!ref_counter_.compare_exchange_weak(expected, MIN_INT, | |
std::memory_order_acquire, | |
std::memory_order_relaxed)){ | |
expected = 0; | |
} | |
} | |
template <> | |
void RWLock<SLEEP>::LockWrite(){ | |
int expected = 0; | |
if(!ref_counter_.compare_exchange_strong(expected, MIN_INT, | |
std::memory_order_acquire, | |
std::memory_order_relaxed)){ | |
expected = 0; | |
std::unique_lock<std::mutex> lk(wait_mutex_); | |
wait_cv_.wait(lk, [this,&expected] { | |
if(!ref_counter_.compare_exchange_strong(expected, MIN_INT, | |
std::memory_order_acquire, | |
std::memory_order_relaxed)){ | |
expected = 0; | |
return false; | |
} | |
return true; | |
}); | |
lk.unlock(); | |
} | |
} | |
template <WaitGenre WAIT_GENRE> | |
bool RWLock<WAIT_GENRE>::TryLockWrite() { | |
int expected = 0; | |
return ref_counter_.compare_exchange_strong(expected, MIN_INT, | |
std::memory_order_acquire, | |
std::memory_order_relaxed); | |
} | |
template <WaitGenre WAIT_GENRE> | |
void RWLock<WAIT_GENRE>::UnLockWrite() { | |
ref_counter_.store(0, std::memory_order_release); | |
if(WAIT_GENRE == SLEEP) | |
wait_cv_.notify_all(); | |
} | |
template <> | |
void RWLock<SPIN>::LockRead() { | |
while(ref_counter_.fetch_add(1, std::memory_order_acquire) < 0){ | |
ref_counter_.fetch_sub(1, std::memory_order_release); | |
} | |
} | |
template <> | |
void RWLock<SLEEP>::LockRead() { | |
if(ref_counter_.fetch_add(1, std::memory_order_acquire) < 0){ | |
ref_counter_.fetch_sub(1, std::memory_order_release); | |
std::unique_lock<std::mutex> lk(wait_mutex_); | |
wait_cv_.wait(lk, [this]{ | |
return ref_counter_.fetch_add(1, std::memory_order_acquire) >= 0; | |
}); | |
lk.unlock(); | |
} | |
} | |
template <WaitGenre WAIT_GENRE> | |
bool RWLock<WAIT_GENRE>::TryLockRead() { | |
return ref_counter_.fetch_add(1, std::memory_order_acquire) >= 0; | |
} | |
template <WaitGenre WAIT_GENRE> | |
void RWLock<WAIT_GENRE>::UnLockRead() { | |
ref_counter_.fetch_sub(1, std::memory_order_release); | |
if(WAIT_GENRE == SLEEP) | |
wait_cv_.notify_one(); | |
} | |
template <WaitGenre WAIT_GENRE> | |
class ScopeReadLock { | |
RWLock<WAIT_GENRE> &lock_; | |
public: | |
ScopeReadLock(RWLock<WAIT_GENRE> &lock) : lock_(lock) { | |
lock_.LockRead(); | |
} | |
~ScopeReadLock() { | |
lock_.UnLockRead(); | |
} | |
}; | |
template <WaitGenre WAIT_GENRE> | |
class ScopeWriteLock { | |
RWLock<WAIT_GENRE> &lock_; | |
public: | |
ScopeWriteLock(RWLock<WAIT_GENRE> &lock) : lock_(lock) { | |
lock_.LockWrite(); | |
} | |
~ScopeWriteLock() { | |
lock_.UnLockWrite(); | |
} | |
}; | |
}; | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here is my reference:
https://github.com/dryman/atomic_patterns/blob/master/atomic_rwlock.c