Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional use of std::lock_guard

Tags:

I have a function where the statement foo should be executed under lock_guard but only when a pointer to a mutex object has been provided to the function as a parameter. Otherwise foo does not have to be protected by lock_guard.

I cannot use the lock_guard within an if because the lock will be released immediately when the if block ends.

so, this code is nonsense:

bar( std::mutex * optionalMutex = nullptr ) {    ...    if ( nullptr != optionalMutex ) {       std::lock_guard<std::mutex> lockScope( *optionalMutex );    }          <- Here the lock ends     foo...     <- foo is not protected when optionalMutex was provided } 

I tried something like this:

bar( std::mutex * optionalMutex = nullptr ) {    ...    nullptr == optionalMutex ? 0 : std::lock_guard<std::mutex> lockScope( *optionalMutex );     // this scope should be protected by lock_guard when optionalMutex was provided     foo... } 

More or less, the only one possible solution for me is to repeat foo:

bar( std::mutex * optionalMutex = nullptr ) {    ...    if ( nullptr != optionalMutex ) {       std::lock_guard<std::mutex> lockScope( *optionalMutex );       foo...    }  else {       foo...    } } 

The compiler gcc 4.9.3 does not compile the 2nd example and complains: error: expected primary-expression before 'lockScope'. Update: Superlokkus explained in his answer why.

But I do want to avoid any code duplicates and therefore also the duplicate foo.

My question:

Is there an elegant way how to implement this problem and not to use duplicate foo. I know, I could use a lambda function to group foo but I am curious if there is an another solution.

like image 621
Peter VARGA Avatar asked Jul 27 '16 10:07

Peter VARGA


People also ask

What is the role of std :: lock_guard?

std::lock_guard A lock guard is an object that manages a mutex object by keeping it always locked. On construction, the mutex object is locked by the calling thread, and on destruction, the mutex is unlocked.

What is the difference between unique_lock and lock_guard?

A lock_guard always holds a lock from its construction to its destruction. A unique_lock can be created without immediately locking, can unlock at any point in its existence, and can transfer ownership of the lock from one instance to another.

Does unique_lock automatically unlock?

@Prab, just to give the explanation: unique_lock() is automatically released when the destructor is called meaning it is exception safe and that it automatically unlocks when you leave scope.

Are lock guards deprecated?

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).

What is lock_guard in C++?

class lock_guard; (since C++11) The class lock_guard is a mutex wrapper that provides a convenient RAII-style mechanism for owning a mutex for the duration of a scoped block. When a lock_guard object is created, it attempts to take ownership of the mutex it is given.

Why is the argument to lock_guard () not logically const?

A mutex (regardless which implementation is used) must be acquired and released and that implies calls to the non-constant lock () and unlock () methods. So the argument to lock_guard cannot be logically const (as the mutex would be if the method was const ). The solution to this problem is to make the mutex mutable.

Should I unlock the mutex before or after a condition variable?

When notifying a condition_variable, default to doing it before you unlock the mutex instead of after. Rationale: It is never wrong to unlock after the notify, and it is sometimes wrong to unlock before the notify. And there is no performance penalty in unlocking after, as good implementations take this coding pattern into account.

What is the difference between lock_guard and unique_lock in C++?

One of the differences between std::lock_guard and std::unique_lock is that the programmer is able to unlock std::unique_lock, but she/he is not able to unlock std::lock_guard. Let’s explain it in more detail. then the constructor of guard1 locks the mutex.


1 Answers

How about this one?

void bar(std::mutex * optionalMutex = nullptr) {         auto lockScope = (optionalMutex == nullptr) ?                             std::unique_lock<std::mutex>()                           : std::unique_lock<std::mutex>(*optionalMutex); } 

Explanation: Your compiler had trouble with your prior statement because, you can not suddenly change the type of the ternary ? expression; i.e. the literal 0 is not a std::lock_guard and vice versa. So I changed the two branches to the same type, here std::unique_lock<std::mutex> because lock_guard isn't designed be used without a valid mutex. But still prefer std::lock_guard over std::unique_lock in the simpler cases, because it will make your code more readable.

Also your statement wasn't viable for the compiler, i.e. even syntactical correct, because the variable lockScope would only have existed in one branch.

like image 138
Superlokkus Avatar answered Oct 12 '22 12:10

Superlokkus