I know it's bad to even use recursive_mutex because of its poor performance, let alone a recursive shared mutex. However, I'm doing this just to practice. Any suggestion will be appreciated!
recursive_shared_mutex.hpp
#pragma once #include <optional> #include <shared_mutex> #include <thread> class recursive_shared_mutex : private std::shared_mutex { public: recursive_shared_mutex(); ~recursive_shared_mutex() = default; void lock(); bool try_lock(); void unlock(); /* * std::shared_mutex's shared locking is recursive by default. */ void lock_shared() { std::shared_mutex::lock_shared(); } bool try_lock_shared() { return std::shared_mutex::try_lock_shared(); } void unlock_shared() { std::shared_mutex::unlock_shared(); } private: std::mutex mtx_; std::condition_variable cv_; std::optional<std::thread::id> writer_id_; std::size_t writer_cnt_; };
recursive_shared_mutex.cpp
#include "recursive_shared_mutex.hpp" recursive_shared_mutex::recursive_shared_mutex() : std::shared_mutex(), mtx_(), cv_(), writer_id_(), writer_cnt_(0) { } void recursive_shared_mutex::lock() { std::thread::id this_id = std::this_thread::get_id(); std::unique_lock<std::mutex> ulock(mtx_); if (this_id == writer_id_) { // Same thread/writer trying to acquire the shared_mutex again. // Simply increase writer count. ++writer_cnt_; } else { // Another writer or no writer is holding the shared_mutex. // It's also likely that some readers are holding // the shared_mutex. if (writer_id_.has_value()) { // If another writer is holding the mutex, // waiting for it to release. cv_.wait(ulock, [this] { return writer_cnt_ == 0; }); } std::shared_mutex::lock(); writer_id_ = this_id; writer_cnt_ = 1; } } bool recursive_shared_mutex::try_lock() { // TODO return false; } void recursive_shared_mutex::unlock() { std::unique_lock<std::mutex> ulock(mtx_); if (!writer_id_.has_value()) { // No writer is holding the shared_mutex. // Call unlock() anyway. UB expected. std::shared_mutex::unlock(); } else { // At this point, the shared_mutex must be held by a writer // and writer_cnt_ must be greater than 0. --writer_cnt_; if (writer_cnt_ == 0) { // If writer count becomes 0, release the // underlying shared_mutex. // It's likely that we are unlocking in a // different thread from the one where the shared_mutex // has been acquired. // Call unlock() anyway. UB expected. std::shared_mutex::unlock(); writer_id_.reset(); // Manually unlock to let cv notify. ulock.unlock(); cv_.notify_one(); } } }
std::shared_mutex's shared locking is recursive by default
- this is not true. What you are seeing is a side effect of running on a system which implementshared_mutex
usingpthread_rwlock_t
which allows taking read lock multiple times.\$\endgroup\$