Can using the lock of a mutex, adopted by a lock_guard, lead to a UB?

120 views Asked by At

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();
}
1

There are 1 answers

0
Howard Hinnant On BEST ANSWER

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.