Why is lock_guard a template? - c++

I just learned about std::lock_guard and I was wondering why it is a template.
Until now I have only seen std::lock_guard<std::mutex> with std::mutex inside the angle brackets.

Using std::lock_guard<std::mutex> is indeed quite common.
But you can use std::lock_guard with other mutex types:
Various standard mutex types, e.g.: std::recursive_mutex.
Your own mutex type. You can use any type, as long as it is a BasicLockable, i.e. it supports the required methods: lock(), unlock().

Related

Why I can't lock two mutex with lock_guard [duplicate]

C++17 introduced a new lock class called std::scoped_lock.
Judging from the documentation it looks similar to the already existing std::lock_guard class.
What's the difference and when should I use it?
Late answer, and mostly in response to:
You can consider std::lock_guard deprecated.
For the common case that one needs to lock exactly one mutex, std::lock_guard has an API that is a little safer to use than scoped_lock.
For example:
{
std::scoped_lock lock; // protect this block
...
}
The above snippet is likely an accidental run-time error because it compiles and then does absolutely nothing. The coder probably meant:
{
std::scoped_lock lock{mut}; // protect this block
...
}
Now it locks/unlocks mut.
If lock_guard was used in the two examples above instead, the first example is a compile-time error instead of a run-time error, and the second example has identical functionality as the version which uses scoped_lock.
So my advice is to use the simplest tool for the job:
lock_guard if you need to lock exactly 1 mutex for an entire scope.
scoped_lock if you need to lock a number of mutexes that is not exactly 1.
unique_lock if you need to unlock within the scope of the block (which includes use with a condition_variable).
This advice does not imply that scoped_lock should be redesigned to not accept 0 mutexes. There exist valid use cases where it is desirable for scoped_lock to accept variadic template parameter packs which may be empty. And the empty case should not lock anything.
And that's why lock_guard isn't deprecated. scoped_lock and unique_lock may be a superset of functionality of lock_guard, but that fact is a double-edged sword. Sometimes it is just as important what a type won't do (default construct in this case).
The scoped_lock is a strictly superior version of lock_guard that locks an arbitrary number of mutexes all at once (using the same deadlock-avoidance algorithm as std::lock). In new code, you should only ever use scoped_lock.
The only reason lock_guard still exists is for compatibility. It could not just be deleted, because it is used in current code. Moreover, it proved undesirable to change its definition (from unary to variadic), because that is also an observable, and hence breaking, change (but for somewhat technical reasons).
The single and important difference is that std::scoped_lock has a variadic constructor taking more than one mutex. This allows to lock multiple mutexes in a deadlock avoiding way as if std::lock were used.
{
// safely locked as if using std::lock
std::scoped_lock<std::mutex, std::mutex> lock(mutex1, mutex2);
}
Previously you had to do a little dance to lock multiple mutexes in a safe way using std::lock as explained this answer.
The addition of scope lock makes this easier to use and avoids the related errors. You can consider std::lock_guard deprecated. The single argument case of std::scoped_lock can be implemented as a specialization and such you don't have to fear about possible performance issues.
GCC 7 already has support for std::scoped_lock which can be seen here.
For more information you might want to read the standard paper
Here is a sample and quote from C++ Concurrency in Action:
friend void swap(X& lhs, X& rhs)
{
if (&lhs == & rhs)
return;
std::lock(lhs.m, rhs.m);
std::lock_guard<std::mutex> lock_a(lhs.m, std::adopt_lock);
std::lock_guard<std::mutex> lock_b(rhs.m, std::adopt_lock);
swap(lhs.some_detail, rhs.some_detail);
}
vs.
friend void swap(X& lhs, X& rhs)
{
if (&lhs == &rhs)
return;
std::scoped_lock guard(lhs.m, rhs.m);
swap(lhs.some_detail, rhs.some_detail);
}
The existence of std::scoped_lock means that most of the cases where you would have used std::lock prior to c++17 can now be written using std::scoped_lock, with less potential for mistakes, which can only be a good thing!

Is the purpose of std::scoped_lock only to handle multiple mutexes, compared to std::lock_guard?

When reading the documentation about std::scoped_lock and std::lock_guard, it seams that the only difference is that scoped_lock can handle multiple lock guard and can avoid deadlock when unlocking.
Is this the only difference? If I have only one mutex, should I therefore keep using use lock_guard?
As far as I know the only important difference is that the scoped_lock has a variadic constructor taking more than one mutex as you mentioned. In addition you can implement a single-argument version of scoped_lock with template specialization.
So the lock_guard is kinda "deprecated" non-formally.
I think lock_guard still exists because of compatibility.

Condition variable and shared mutex

I wonder why I can't do this in C++14 (or 17)
std::shared_timed_mutex mutex;
std::unique_lock<std::shared_timed_mutex> lock(mutex);
std::condition_variable var;
while(!some_condition)
var.wait(lock);
Condition variables only seem to work with std::mutex. But why?
This is defined by the standard to allow for maximum efficiency of the implementation. If you want to use a different lock with condition_variable you need to use condition_variable_any. Note that the condition_variable_any implementation has some overhead though.
Quote from the standard: 30.5 Condition variables
Class condition_variable provides a condition variable that can only
wait on an object of type unique_lock<mutex>, allowing maximum
effciency on some platforms.

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.
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.
The unique_lock is a RAII system. When created, taking the mutex as parameter, it will lock the mutex and when leaving the scope it is destroyed, thus unlocking the mutex. When you need to unlock earlier, you can call the unlock() function like in your example.
Using the unique_lock like in your example doesn't create added value over using the mutex directly.
All answers explain very well why the example is not good. I will answer when you would want to use a std::unique_lock instead of a std::lock_guard.
When a thread is waiting on something, one may use a std::condition_variable. A condition variable makes use of deferred locking and therefore uses a unique_lock, which allows deferred locking. I don't think you explicitly need to say you need deferred locking, as in the example.
To grasp these concepts, you first need to learn what RAII is. Then have a look at this reference. An example of the condition variables can be found here.

Can you combine std::recursive_mutex with std::condition_variable?

Can you combine std::recursive_mutex with std::condition_variable, meaning do something like this:
std::unique_lock<std::recursive_mutex> lock(some_recursive_mutex)
some_condition_var.wait(lock);
If it's not allowed, then why not?
I am using VC++11.
You can, if you use std::condition_variable_any, which allows for any type of object that supports the Lockable concept.
However, in the case of recursive mutex, you do have to ensure that the given thread has only locked the recursive mutex once, since the condition variable only will use the unlock method on the unique_lock once during the wait.
You can do that with a std::condition_variable_any which can take any kind of lockable but plain std::condition_variable is specialized for std::unique_lock<std::mutex>.