I'm learning C++ and I saw that the source-code for a scope lock is quite simple. . How does it work, and how is this an example of "Resource Acquisition is Instantiation" (RAII) ?
Here is the little code that illustrates scoped lock:
void do_something()
{
//here in the constructor of scoped_lock, the mutex is locked,
//and a reference to it is kept in the object `lock` for future use
scoped_lock lock(shared_mutex_obj);
//here goes the critical section code
}//<---here : the object `lock` goes out of scope
//that means, the destructor of scoped_lock will run.
//in the destructor, the mutex is unlocked.
Read the comments. That explains how scoped_lock works.
And here is how scoped_lock is typically implemented (minimal code):
class scoped_lock : noncopyable
{
mutex_impl &_mtx; //keep ref to the mutex passed to the constructor
public:
scoped_lock(mutex_impl & mtx ) : _mtx(mtx)
{
_mtx.lock(); //lock the mutex in the constructor
}
~scoped_lock()
{
_mtx.unlock(); //unlock the mutex in the constructor
}
};
The idea of RAII (Resource Acquisition Is Initialisation) is that creating an object and initialising it are joined together into one unseparable action. This generally means they're performed in the object's constructor.
Scoped locks work by locking a mutex when they are constructed, and unlocking it when they are destructed. The C++ rules guarantee that when control flow leaves a scope (even via an exception), objects local to the scope being exited are destructed correctly. This means using a scoped lock instead of manually calling lock() and unlock() makes it impossible to accidentally not unlock the mutex, e.g. when an exception is thrown in the middle of the code between lock() and unlock().
This principle applies to all scenarios of acquiring resources which have to be released, not just to locking mutexes. It's good practice to provide such "scope guard" classes for other operations with similar syntax.
For example, I recently worked on a data structure class which normally sends signals when it's modified, but these have to be disabled for some bulk operations. Providing a scope guard class which disables them at construction and re-enables them at destruction prevents potential unbalanced calls to the disable/enable functions.
Basically it works like this:
template <class Lockable>
class lock{
public:
lock(Lockable & m) : mtx(m){
mtx.lock();
}
~lock(){
mtx.unlock();
}
private:
Lockable & mtx;
};
If you use it like
int some_function_which_uses_mtx(){
lock<std::mutex> lock(mtx);
/* Work with a resource locked by mutex */
if( some_condition())
return 1;
if( some_other_condition())
return 1;
function_witch_might_throw();
return;
}
you create a new object with a scope-based lifetime. Whenever the current scope is left and this lock gets destroyed it will automatically call mtx.unlock(). Note that in this particular example the lock on the mutex is aquired by the constructor of the lock, which is RAIII.
How would you do this without a scope guard? You would need to call mtx.unlock() whenever you leave the function. This is a) cumbersome and b) error-prone. Also you can't release the mutex after a return without a scope guard.
Related
I'm learning about mutex and threading right now. I was wondering if there's anything dangerous or inherently wrong with automating mutex with a class like this:
class AutoMutex
{
private:
std::mutex& m_Mutex;
public:
AutoMutex(std::mutex& m) : m_Mutex(m)
{
m_Mutex.lock();
}
~AutoMutex()
{
m_Mutex.unlock();
}
};
And then, of course, you would use it like this:
void SomeThreadedFunc()
{
AutoMutex m(Mutex); // With 'Mutex' being some global mutex.
// Do stuff
}
The idea is that, on construction of an AutoMutex object, it locks the mutex. Then, when it goes out of scope, the destructor automatically unlocks it.
You could even just put it in scopes if you don't need it for an entire function. Like this:
void SomeFunc()
{
// Do stuff
{
AutoMutex m(Mutex);
// Do race condition stuff.
}
// Do other stuff
}
Is this okay? I don't personally see anything wrong with it, but as I'm not the most experienced, I feel there's something I may be missing.
It's safe to use a RAII wrapper, and in fact safer than using mutex member functions directly, but it's also unnecessary to write since standard library already provides this. It's called std::lock_guard.
However, your implementation isn't entirely safe, because it's copyable, and a copy will attempt to re-unlock the mutex which will lead to undefined behaviour. std::lock_guard resolves this issue by being non-copyable.
There's also std::unique_lock which is very similar, but allows things such as releasing the lock within the lifetime. std::scoped_lock should be used if you need to lock multiple mutexes. Using multiple lock guard may lead to deadlock. std::scoped_lock is also fine to use with a single mutex, so you can replace all uses of lock guard with it.
I was using following kind of wait/signal way to let threads inform each other.
std::condition_variable condBiz;
std::mutex mutexBar;
..
void Foo::wait()
{
std::unique_lock<std::mutex> waitPoint(mutexBar);
if (waitPoint.owns_lock())
{
condBiz.wait(waitPoint);
}
}
void Foo::signal()
{
std::unique_lock<std::mutex> waitPoint(mutexBar);
condBiz.notify_all();
}
void Foo::safeSection(std::function<void(void)> & f)
{
std::unique_lock<std::mutex> waitPoint(mutexBar);
f();
}
Then converted lock/unlock mechanism from unique_lock to lock_guard because I'm not returning unique_lock to use somewhere else(other than wait/signal) and lock_guard is said to have less overhead:
void Foo::safeSection(std::function<void(void)> & f)
{
std::lock_guard<std::mutex> waitPoint(mutexBar); // same mutex object
f();
}
and it works.
Does this work for all platforms or just looks like working for current platform? Can unique_lock and lock_guard work with each other using same mutex object?
Both std::unique_lock and std::lock_guard lock the associated mutex in the constructor and unlock it in the destructor.
std::unique_lock:
Member functions
(constructor) constructs a unique_lock, optionally locking the supplied mutex
(destructor) unlocks the associated mutex, if owned
and the same for std::lock_guard:
Member functions
(constructor) constructs a lock_guard, optionally locking the given mutex
(destructor) destructs the lock_guard object, unlocks the underlying mutex
Since both behave the same, when used as a RAII style wrapper, I see no obstacle to use them together, even with the same mutex.
It has been pointed out in the comments to your post that checking if the unique_lock is owned in Foo::wait() is pointless, because the associated mutex must be owned by the lock at that point in order for the thread to be proceeding.
Instead your condition variable should be checking some meaningful condition, and it should do so in a while loop or by using the overload of condition_variable::wait which takes a predicate as its second argument, which is required by the C++ standard to have effect as:
while (!pred()) wait(lock);
The reason for checking the predicate in a while loop is that, apart from the fact that the condition may already be satisfied so no wait is necessary, the condition variable may spuriously wake up even when not signalled to do so.
Apart from that there is no reason why the signalling thread should not use a lock_guard with respect to the associated mutex. But I am not clear what you are trying to do.
I have a data structure consisting of few 'blocks'. For each block I have mutex. I want to implement a method that locks entire data structure and move this lock to calling function. Here's my code:
std::vector<std::unique_lock<boost::shared_mutex>> lock_array()
{
std::vector<std::unique_lock<boost::shared_mutex>> locks;
for(size_t block = 0; block < BLOCK_COUNT; ++block)
{
locks.push_back(std::unique_lock<boost::shared_mutex>(mutexes[block]));
}
return std::move(locks);
}
And if I call it in such way, will my array still be locked?
void some_method()
{
auto locks = lock_array();
...
}
Yes. Moving a unique_lock will preserve the lock, and moving the vector should not affect the locks at all. You could verify this:
void some_method()
{
auto locks = lock_array();
for (auto const & lock : locks) {
assert(lock.owns_lock());
}
}
Also, note that you don't need std::move when returning; return values are moved anyway (unless the move is elided).
You transferred locks out of lock_array() function, compiler should use RVO or move locks out of lock_array(), otherwise you will get compiler error as unique_lock is moveable but not copyable.
locks still locked because they acquire lock during construction and will destroy only when locks go out of scope. Your transfer action won't destroy locks.
ยง 30.4.2.2
An object of type unique_lock controls the ownership of a lockable object within a scope. Ownership of the lockable object may be acquired at construction or after construction, and may be transferred, after acquisition, to another unique_lock object. Objects of type unique_lock are not copyable but are movable.
Simple question - basically, do I have to unlock a mutex, or can I simply use the scope operators and the mutex will unlock automatically?
ie:
{
pthread_mutex_lock (&myMutex);
sharedResource++;
} // my mutex is now unlocked?
or should I:
{
pthread_mutex_lock (&myMutex);
sharedResource++;
pthread_mutex_unlock (&myMutex);
}
The mutex is not going out of scope in your examples; and there is no way for the compiler to know that a particular function needs calling at the end of the scope, so the first example does not unlock the mutex.
If you are using (error-prone) functions to lock and unlock the mutex, then you will need to ensure that you always call unlock() - even if the protected operation throws an exception.
The best way to do this is to use a RAII class to manage the lock, as you would for any other resource that needs releasing after use:
class lock_guard {
public:
explicit lock_guard(mutex & m) : m(m) {mutex_lock(m);}
~lock_guard() {mutex_unlock(m);}
lock_guard(lock_guard const &) = delete;
void operator=(lock_guard &) = delete;
private:
mutex & m;
};
// Usage
{
lock_guard lock(myMutex);
shared_resource++;
} // mutex is unlocked here (even if an exception was thrown)
In modern C++, use std::lock_guard or std::unique_lock for this.
Using the RAII scope method is much better because it guarantees that the mutex will always be unlocked even in the face of exceptions or early return.
If you have access to C++11 though you might consider using a std::atomic<int> instead in which case you don't need to lock it to increment.
In this case, no the mutex will not be unlocked when this code goes out of scope.
Mutex lockers following RAII use the fact that a destructor is automatically called when a non-heap allocated object goes out of scope. It then unlocks the mutex once the object that locked the mutex goes out of scope. In the case of your code, no object is allocated within the scope of the braces, so there is no potential for the mutex to be unlocked once the scope ends.
For example, using QMutexLocker from the Qt libraries, you can ensure that your mutex is unlocked when scope is ended:
{
QMutexLocker locker(myMutex);
if(checkSomething())
{
return;
}
doSomething();
}
This code is similar to:
{
mutex_lock(myMutex);
if(checkSomething())
{
mutex_unlock(myMutex);
return;
}
doSomething();
mutex_unlock(myMutex);
}
Although as Brian Neal points out, it does not safely handle the case where checkSomething() and doSomething() throw exceptions.
An alternative to Qt's QMutexLocker would be STD's std::lock_guard.
I am very confused about the difference between a lock and mutex. In Boost docs, it says,
Lock Types
Class template lock_guard
Class template unique_lock
Class template shared_lock
Class template upgrade_lock
Class template upgrade_to_unique_lock
Mutex-specific class scoped_try_lock
Mutex Types
Class mutex
Typedef try_mutex
Class timed_mutex
Class recursive_mutex
Typedef recursive_try_mutex
Class recursive_timed_mutex
Class shared_mutex
In another article, I see functions like this,
boost::shared_mutex _access;
void reader()
{
boost::shared_lock< boost::shared_mutex > lock(_access);
// do work here, without anyone having exclusive access
}
void conditional_writer()
{
boost::upgrade_lock< boost::shared_mutex > lock(_access);
// do work here, without anyone having exclusive access
if (something) {
boost::upgrade_to_unique_lock< boost::shared_mutex > uniqueLock(lock);
// do work here, but now you have exclusive access
}
// do more work here, without anyone having exclusive access
}
Updated questions
Can anyone offer some clarification between the "mutex" and "lock"?
Is it necessary to create a shared_lock for a shared_mutex?
What happen if I create a unique_lock for a shared_mutex?
Or if I create a shared_lock for a mutex, does it mean the mutex can
not be shared among multiple threads?
A mutex is a synchronization object. You acquire a lock on a mutex at the beginning of a section of code, and release it at the end, in order to ensure that no other thread is accessing the same data at the same time. A mutex typically has a lifetime equal to that of the data it is protecting, and that one mutex is accessed by multiple threads.
A lock object is an object that encapsulates that lock. When the object is constructed it acquires the lock on the mutex. When it is destructed the lock is released. You typically create a new lock object for every access to the shared data.
A mutex is an object which can be locked. A lock is the object which
maintains the lock. To create a lock, you need to pass it a mutex.
Locks can provide mutual exclusion but not condition synchronization.Unlike a semaphore, a lock has an owner, and ownership plays an important
role in the behavior of a lock
example -
class lockableObject { public void F() {
mutex.lock(); ...; mutex.unlock();
}
public void G() {
mutex.lock(); ...; F(); ...; mutex.unlock();
}
private mutexLock mutex; }
// method G() calls method F()
Lock mutex in class lockableObject is used to turn methods F() and G() into critical sections. Thus, only one thread at a time can execute inside a method of a lockableObject. When a thread calls method G(), the mutex is locked. When method G() calls method F(), mutex.lock() is executed in F(), but the calling thread is not blocked since it already owns mutex. If mutex were a binary semaphore instead of a lock, the call from G() to F() would block the calling thread when mutex.P() was executed in F(). (Recall that comple- tions of P() and V() operations on a binary semaphore must alternate.) This would create a deadlock since no other threads would be able execute inside F() or G().
These are differences between locks and binary semaphores:
1 For a binary semaphore,if two calls are made toP()without any intervening call to V(), the second call will block. But a thread that owns a lock and requests ownership again is not blocked. (Beware of the fact that locks are not always recursive, so check the documentation before using a lock.)
2 The owner for successive calls to lock() and unlock() must be the same thread. But successive calls to P () and V () can be made by different threads.