In the following example, the method foo()
gets called, where it acquires ownership of a mutex, and locks it. It then calls check()
, which acquires ownership, but assumes that the mutex is already locked, and so simply adopts it using std::adopt_lock
.
But when check()
finishes, the mutex gets unlocked. So when foo()
continues, the section I was trying to guard is actually no longer guarded.
#include <mutex>
static std::mutex sessionLock;
bool check();
void foo() {
std::lock_guard<std::mutex> guard(sessionLock);
if (check()) {
// Do transaction
// Wait... the mutex is unlocked here!
}
}
bool check() {
std::lock_guard<std::mutex> guard(sessionLock, std::adopt_lock);
// Critical section
return true;
}
int main() {
foo();
return 0;
}
I find this behaviour very unintuitive. If a sub-method decides to take ownership of a lock using std::adopt_lock
(ie. it doesn't call lock()
), shouldn't it also release ownership without calling unlock()
? The standard says otherwise, but I'm curious if this was an oversight or if there is a particular reason this is expected.
This could be rewritten using std::recursive_mutex
, though in this case where a regular std::mutex
is used, is there a proper way inside check()
to ensure its critical section is guarded?