Skip to content

Instantly share code, notes, and snippets.

@yohhoy
Created January 16, 2017 04:23
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/296152db5f7292ca178aace6c8b010cd to your computer and use it in GitHub Desktop.
Save yohhoy/296152db5f7292ca178aace6c8b010cd to your computer and use it in GitHub Desktop.
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
Copy link
Author

yohhoy commented Jan 19, 2017

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