Skip to content

Instantly share code, notes, and snippets.

@yohhoy
Created August 20, 2012 09:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yohhoy/3402666 to your computer and use it in GitHub Desktop.
Save yohhoy/3402666 to your computer and use it in GitHub Desktop.
recursive_mutex
/*
* Hand-coded recursive_mutex classes
*
* (C) Copyright yohhoy 2012.
* Distributed under the Boost Software License, Version 1.0.
* (See copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <cassert>
namespace sample1 {
class recursive_mutex {
std::mutex mtx_;
std::condition_variable cv_;
std::thread::id owner_; // lock owner
unsigned own_count_; // lock count
public:
recursive_mutex()
: own_count_(0) {}
~recursive_mutex()
{
assert(owner_ == std::thread::id() && own_count_ == 0);
}
recursive_mutex(const recursive_mutex&) = delete;
recursive_mutex& operator=(const recursive_mutex&) = delete;
void lock()
{
const std::thread::id tid = std::this_thread::get_id();
std::unique_lock<std::mutex> lk(mtx_);
if (owner_ != tid) {
cv_.wait(lk, [&]{ return owner_ == std::thread::id(); });
owner_ = tid;
assert(own_count_ == 0);
}
++own_count_;
}
bool try_lock()
{
const std::thread::id tid = std::this_thread::get_id();
std::unique_lock<std::mutex> lk(mtx_);
if (owner_ != tid) {
if (owner_ != std::thread::id())
return false;
owner_ = tid;
assert(own_count_ == 0);
}
++own_count_;
return true;
}
void unlock()
{
std::lock_guard<std::mutex> lk(mtx_);
assert(owner_ == std::this_thread::get_id());
assert(0 < own_count_);
if (--own_count_ == 0) {
owner_ = std::thread::id();
cv_.notify_all();
}
}
};
}
namespace sample2 {
class recursive_mutex {
std::mutex outer_mtx_; // outer lock
std::mutex inner_mtx_; // inner lock
std::thread::id owner_; // lock owner
unsigned own_count_; // lock count
typedef std::lock_guard<std::mutex> lock_type;
public:
recursive_mutex()
: own_count_(0) {}
~recursive_mutex()
{
assert(owner_ == std::thread::id() && own_count_ == 0);
}
recursive_mutex(const recursive_mutex&) = delete;
recursive_mutex& operator=(const recursive_mutex&) = delete;
void lock()
{
const std::thread::id tid = std::this_thread::get_id();
bool req_outer_lock = false;
{
lock_type lk(inner_mtx_); // LOCK: (outer) -> inner
if (owner_ == tid) {
++own_count_;
} else {
req_outer_lock = true;
}
}
if (req_outer_lock) {
outer_mtx_.lock(); // LOCK: outer
lock_type lk(inner_mtx_); // LOCK: outer -> inner
owner_ = tid;
own_count_ = 1;
}
}
bool try_lock()
{
const std::thread::id tid = std::this_thread::get_id();
bool req_outer_lock = false;
{
lock_type lk(inner_mtx_); // LOCK: (outer) -> inner
if (owner_ == tid) {
++own_count_;
} else {
req_outer_lock = true;
}
}
if (req_outer_lock) {
if (!outer_mtx_.try_lock()) // LOCK: outer
return false;
lock_type lk(inner_mtx_); // LOCK: outer -> inner
owner_ = tid;
own_count_ = 1;
}
return true;
}
void unlock()
{
lock_type lk(inner_mtx_); // LOCK: outer -> inner
assert(owner_ == std::this_thread::get_id());
assert(0 < own_count_);
if (--own_count_ == 0) {
owner_ = std::thread::id();
outer_mtx_.unlock();
}
}
};
}
namespace sample3 {
class recursive_mutex {
std::mutex mtx_;
std::atomic<std::thread::id> owner_; // lock owner
unsigned own_count_; // lock count
public:
recursive_mutex()
: owner_(std::thread::id()), own_count_(0) {}
~recursive_mutex()
{
assert(owner_ == std::thread::id() && own_count_ == 0);
}
recursive_mutex(const recursive_mutex&) = delete;
recursive_mutex& operator=(const recursive_mutex&) = delete;
void lock()
{
const std::thread::id tid = std::this_thread::get_id();
if (owner_.load(std::memory_order_acquire) == tid) {
++own_count_;
} else {
mtx_.lock();
owner_.store(tid, std::memory_order_relaxed);
own_count_ = 1;
}
}
bool try_lock()
{
const std::thread::id tid = std::this_thread::get_id();
if (owner_.load(std::memory_order_acquire) == tid) {
++own_count_;
} else {
if (!mtx_.try_lock())
return false;
owner_.store(tid, std::memory_order_relaxed);
own_count_ = 1;
}
return true;
}
void unlock()
{
assert(owner_ == std::this_thread::get_id());
assert(0 < own_count_);
if (--own_count_ == 0) {
owner_.store(std::thread::id(), std::memory_order_release);
mtx_.unlock();
}
}
};
}
@yohhoy
Copy link
Author

yohhoy commented Feb 7, 2013

@yohhoy
Copy link
Author

yohhoy commented Jan 20, 2017

integrate into https://github.com/yohhoy/yamc (only sample3)

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