Skip to content

Instantly share code, notes, and snippets.

@mshockwave
Last active January 16, 2017 10:59
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save mshockwave/b314eb78d4019e7e106e705e864e0398 to your computer and use it in GitHub Desktop.
Simple Read-Write Lock implemented with full C++11 compatible
#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
@mshockwave
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment