Strictly speaking, the question was about checking the lockedness of std::mutex
directly. However, if encapsulating it in a new class is allowed, it’s very easy to do so:
class mutex :
public std::mutex
{
public:
#ifndef NDEBUG
void lock()
{
std::mutex::lock();
m_holder = std::this_thread::get_id();
}
#endif // #ifndef NDEBUG
#ifndef NDEBUG
void unlock()
{
m_holder = std::thread::id();
std::mutex::unlock();
}
#endif // #ifndef NDEBUG
#ifndef NDEBUG
bool try_lock()
{
if (std::mutex::try_lock()) {
m_holder = std::thread::id();
return true;
}
return false;
}
#endif // #ifndef NDEBUG
#ifndef NDEBUG
/**
* @return true iff the mutex is locked by the caller of this method. */
bool locked_by_caller() const
{
return m_holder == std::this_thread::get_id();
}
#endif // #ifndef NDEBUG
private:
#ifndef NDEBUG
std::atomic<std::thread::id> m_holder = std::thread::id{};
#endif // #ifndef NDEBUG
};
Note the following:
- In release mode, this has zero overhead over
std::mutex
except possibly for construction/destruction (which is a non-issue for mutex objects). - The
m_holder
member is only accessed between taking the mutex and releasing it. Thus the mutex itself serves as the mutex ofm_holder
. With very weak assumptions on the typestd::thread::id
,locked_by_caller
will work correctly. - Other standard library types like
std::lock_guard
are templates, so they work well with this new class, because it satisfies the Mutex requirement.