Leandro Gecozo Leandro Gecozo - 11 days ago 8
C++ Question

How do unique locks work compared to normal mutex locks?

I've came across with this sample of code provided by a book. Btw this book has bad reviews. I regret that I bought it

std::mutex m_mutex;
mutable std::unique_lock<std::mutex> m_finishedQueryLock{ m_mutex, std::defer_lock };

bool m_playerQuit{ false };
void SetPlayerQuit()
{
m_finishedQueryLock.lock();
m_playerQuit = true;
m_finishedQueryLock.unlock();
}


I'm not satisfied with the book's explanation of how it works and why should I use it. I already have an idea of how mutex locks work and the implementations of it, but I have a difficulty understanding the second line of the code above. And why does it have a mutable keyword in it?

I'm completely new on C++ Programming. So a basic level of explanation would help me a lot.

Answer

That example looks totally stupid.

The second line is declaring a non-static data member, and

  • the member is mutable (for reasons given below);
  • the member is an object of type std::unique_lock<std::mutex>, which is a helper type for locking/unlocking an associated mutex object;
  • the member is initialized when an instance of the class is created, by calling its constructor and passing m_mutex and the special tag std::defer_lock as arguments.

But doing that is stupid, and I'm not surprised the book has bad reviews if it has examples like that.

The point of unique_lock is to lock the associated mutex, and then to automatically unlock it when it goes out of scope. Creating a unique_lock member like this is stupid, because it doesn't go out of scope at the end of the function, so the code has absolutely no advantage over:

mutable std::mutex m_mutex;

bool m_playerQuit{ false };

void SetPlayerQuit()
{
    m_mutex.lock();
    m_playerQuit = true;
    m_mutex.unlock();
}

But this manual unlocking has all the problems that unique_lock was designed to solve, so it should be using a scoped lock (either unique_lock or lock_guard) but only in the function scope, not as a member:

mutable std::mutex m_mutex;

bool m_playerQuit{ false };

void SetPlayerQuit()
{
    std::lock_guard<std::mutex> lock(m_mutex);
    m_playerQuit = true;
}   // m_mutex is automatically unlocked by the ~lock_guard destructor

The mutable keyword is necessary so that you can lock the mutex in const member functions. Locking and unlocking a mutex is a non-const operation that modifies the mutex, which would not be allowed in a const member if it wasn't mutable.