Reputation: 1
With GCC 4.8.2 (on Linux/Debian/Sid 64 bits) -or GCC 4.9 when available - in C++11- I have some mutex
std::mutex gmtx;
actually, it is a static
member in some class Foo
containing both alpha
and beta
methods below.
it is locked in alpha
like
void alpha(void) {
std::lock_guard<std::mutex> g(gmtx);
beta(void);
// some other work
}
and I want to check in beta
that indeed gmtx
is locked:
void beta(void) {
assert (gmtx.is_locked());
// some real work
}
(notice that is_locked
is only called inside assert
... It can be very inefficient or even sometimes inaccurate)
Of course, I have other functions calling beta
, e.g.
void gamma(void) {
std::lock_guard<std::mutex> g(gmtx);
beta();
// some other work
}
but is_locked
does not exist.... How should I define it? (actually I would like to be sure that the mutex has been locked in the same thread by some [indirect] caller...)
(the reason I want to test that with assert
is that beta
could be called elsewhere)
I cannot use try_lock
(unless using recursive mutexes), because in the common case it would lock an already locked mutex... (locked in the same thread by a caller) and this is not only undefined behavior but blocks entirely.
I want to avoid recursive mutexes (more costly than plain mutexes) unless I really have to.
NB: The real program is a bit more complex. Actually, all the methods are inside a class which maintain a naming bi-directional relation on "items". So I have inside that class a map from items to names and another from names to items. beta
would be the internal method adding really a naming, and alpha
and gamma
would be the methods finding -or adding- an item by its name, or a name by its item.
PS: the real program is not yet released, but should become part of MELT - its future monitor; you can download it (alpha stage, very buggy) from here (a temporary location)
Upvotes: 40
Views: 37116
Reputation: 449
You can use std::unique_lock
and its method .mutex()
to check what mutex it holds.
std::mutex mtx;
void alpha() {
std::unique_lock<std::mutex> lck(mtx);
beta(lck);
}
void beta(std::unique_lock<std::mutex>& lck) {
assert(lck.mutex() == &mtx);
}
in this case you also will see beta(...)
needs some lock before call.
Upvotes: -1
Reputation: 76316
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:
std::mutex
except possibly for construction/destruction (which is a non-issue for mutex objects).m_holder
member is only accessed between taking the mutex and releasing it. Thus the mutex itself serves as the mutex of m_holder
. With very weak assumptions on the type std::thread::id
, locked_by_caller
will work correctly.std::lock_guard
are templates, so they work well with this new class, because it satisfies the Mutex requirement.Upvotes: 24
Reputation: 21
It's not technically an assertion, but I've used a similar approach to prevent unlocked access to shared state: add a reference parameter to the lock guard class on the in the unsafe function (beta in your example). Then the function cannot be called unless the caller has created a lock guard. It solves the problem of accidentally calling the function outside of the lock, and it does so at compile time with no races.
So, using your example:
typedef std::lock_guard<std::mutex> LockGuard;
void alpha(void) {
LockGuard g(gmtx);
beta(g);
// some other work
}
void beta(LockGuard&) {
// some real work
}
void gamma(void) {
LockGuard g(gmtx);
beta(g);
// some other work
}
//works recursively too
void delta(LockGuard& g)
{
beta(g);
}
Drawbacks:
Upvotes: 2
Reputation: 1611
My solution is simple, use try_lock to test then unlock if needed:
std::mutex mtx;
bool is_locked() {
if (mtx.try_lock()) {
mtx.unlock();
return false;
}
return true; // locked thus try_lock failed
}
Upvotes: -3
Reputation: 1
Try atomic (e.g. atomic<bool>
or atomic<int>
), which has a nice load function that will do what you want, as well as other nice functions like compare_exchange_strong.
Upvotes: 2
Reputation: 3135
std::unique_lock<L>
has owns_lock
member function (equivalent of is_locked
as you say).
std::mutex gmtx;
std::unique_lock<std::mutex> glock(gmtx, std::defer_lock);
void alpha(void) {
std::lock_guard<decltype(glock)> g(glock);
beta(void);
// some other work
}
void beta(void) {
assert(glock.owns_lock()); // or just assert(glock);
// some real work
}
EDIT: In this solution, all lock operations should be performed via unique_lock glock
not 'raw' mutex gmtx
. For example, alpha
member function is rewritten with lock_guard<unique_lock<mutex>>
(or simply lock_guard<decltype(glock)>
).
Upvotes: 17
Reputation: 88195
Well, if the expense of the assertion really isn't an issue then you can just call try_lock()
from another thread where its behavior is guaranteed to be well defined:
void beta(void) {
assert(std::async(std::launch::async, [] { return gmtx.try_lock(); })
.get() == false &&
"error, beta called without locking gmtx");
// some real work
}
Upvotes: 2
Reputation: 4863
You could just use a recursive_mutex
, which can be locked multiple times on the same thread. Note: If it were my code, I would restructure it so that I don't need a recursive_mutex
, but it will address your problem.
Upvotes: 15