Can the following snippet cause an undefiled behaviora due to using the lock of the mutex already adopted by a lock_guard
? and will it be safe if I use unique_lock
instead of lock_guard
in the same snippet? I know that there are std::unique_lock<T>::lock/unlock()
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
std::mutex m1;
void func(int count ){
std::lock_guard lG{m1};
std::cout << std::this_thread::get_id() << std::endl;
if(count == 1) {
m1.unlock();
std::this_thread::sleep_for(std::chrono::duration<size_t, std::ratio<1, 1>>{6});
m1.lock();
}
std::cout << std::this_thread::get_id() << std::endl;
}
int main()
{
std::thread t1 {func, 1};
std::thread t2 {func, 9};
t1.join();
t2.join();
}
This particular code is likely safe, but I would not consider it good style. The problem is that if something throws an exception between m1.unlock()
and m1.lock()
, then the lock_guard
destructor is going to unlock the unlocked mutex a second time, causing UB. So even if everything between these statements is guaranteed not to throw, the reader of the code has to inspect that code all too closely to ensure there is no UB.
It would be much better to use unique_lock
and do the unlock/lock dance on the unique_lock
instead of directly on the mutex
to ensure proper unlocking in the exceptional case.