Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Mutex with recursive locking detection
#include <cassert>
#include <cstdlib>
#include <condition_variable>
#include <mutex>
#include <system_error>
#include <thread>
#define CHECKED_MUTEX_ISSUE_ABORT 0
namespace checked {
//
// Mutex with recursive locking detection
//
class mutex {
std::thread::id owner_;
std::mutex mtx_;
std::condition_variable cv_;
public:
mutex() = default;
~mutex() = default;
mutex(const mutex&) = delete;
mutex& operator=(const mutex&) = delete;
void lock()
{
const auto this_tid = std::this_thread::get_id();
std::unique_lock<std::mutex> lk(mtx_);
if (owner_ == this_tid) {
// C++14 [thread.mutex.requirements.mutex]/p6-7
// The expression m.lock() shall be well-formed and have the following semantics:
// Requires: If m is of type std::mutex, std::timed_mutex, or std::shared_timed_mutex, the calling thread does not own the mutex.
#if CHECKED_MUTEX_ISSUE_ABORT
assert(!"recursive lock");
std::abort();
#else
throw std::system_error(std::make_error_code(std::errc::resource_deadlock_would_occur), "recursive lock");
#endif
}
while (owner_ != std::thread::id())
cv_.wait(lk);
owner_ = this_tid;
}
bool try_lock()
{
const auto this_tid = std::this_thread::get_id();
std::lock_guard<std::mutex> lk(mtx_);
if (owner_ == this_tid) {
// C++14 [thread.mutex.requirements.mutex]/p14-15
// The expression m.try_lock() shall be well-formed and have the following semantics:
// Requires: If m is of type std::mutex, std::timed_mutex, or std::shared_timed_mutex, the calling thread does not own the mutex.
#if CHECKED_MUTEX_ISSUE_ABORT
assert(!"recursive try_lock");
std::abort();
#else
throw std::system_error(std::make_error_code(std::errc::resource_deadlock_would_occur), "recursive try_lock");
#endif
}
if (owner_ != std::thread::id())
return false;
owner_ = this_tid;
return true;
}
void unlock()
{
std::lock_guard<std::mutex> lk(mtx_);
if (owner_ != std::this_thread::get_id()) {
// C++14 [thread.mutex.requirements.mutex]/p21-22
// The expression m.unlock() shall be well-formed and have the following semantics:
// Requires: The calling thread shall own the mutex.
#if CHECKED_MUTEX_ISSUE_ABORT
assert(!"invalid unlock");
std::abort();
#else
throw std::system_error(std::make_error_code(std::errc::operation_not_permitted), "invalid unlock");
#endif
}
owner_ = std::thread::id();
cv_.notify_all();
}
};
} // namespace checked
#if 1
#include <iostream>
int main()
{
bool catch_exeption = false;
checked::mutex mtx;
std::cout << "lock(1st)" << std::endl;
mtx.lock();
try {
std::cout << "lock(2nd)" << std::endl;
mtx.lock();
} catch (std::system_error& e) {
std::cout << "detect deadlock" << std::endl;
assert(e.code() == std::errc::resource_deadlock_would_occur);
catch_exeption = true;
}
mtx.unlock();
assert(catch_exeption);
std::cout << "done" << std::endl;
}
#endif
@yohhoy

This comment has been minimized.

Show comment
Hide comment
Owner

yohhoy commented Jan 19, 2017

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