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.
Related
I was reading about thread safety of std::shared_ptr and about the atomic operations overloads it provides and was wondering regarding a specific use case of it in a class.
From my understanding of the thread safety that is promised from shared_ptr, it is safe to have a get method like so:
class MyClass
{
std::shared_ptr<int> _obj;
public:
void start()
{
std::lock_guard<std::mutex> lock(_mtx);
_obj = std::make_shared<int>(1);
}
void stop()
{
std::lock_guard<std::mutex> lock(_mtx);
_obj.reset();
}
std::shared_ptr<int> get_obj() const
{
return _obj; //Safe (?)
}
};
The getter should be safe since the object will either be initializes or empty at any point from any thread.
But what if I want to throw an exception if the object is empty, I need to check it before returning it, do I have to put a lock there now (since stop() might be called between the if and the return)? Or is it possible to use the locking mechanism of the shared pointer and not use a lock in this method:
std::shared_ptr<int> get_obj() const
{
auto tmp = _obj;
if(!tmp) throw std::exception();
return tmp;
}
std::shared_ptr instances are not thread safe. Multiple instances all pointing to the same object can be modified from multiple threads but a single instance is not thread safe. See https://en.cppreference.com/w/cpp/memory/shared_ptr:
All member functions (including copy constructor and copy assignment) can be called by multiple threads on different instances of shared_ptr without additional synchronization even if these instances are copies and share ownership of the same object. If multiple threads of execution access the same shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur; the shared_ptr overloads of atomic functions can be used to prevent the data race.
You therefore either need to lock your mutex in your get_obj method or use std::atomic_load and std::atomic_store in your start and stop methods
For example consider
class ProcessList {
private
std::vector<std::shared_ptr<SomeObject>> list;
Mutex mutex;
public:
void Add(std::shared_ptr<SomeObject> o) {
Locker locker(&mutex); // Start of critical section. Locker release so will the mutex - In house stuff
list.push_back(std::make_shared<SomeObject>(o).
}
void Remove(std::shared_ptr<SomeObject> o) {
Locker locker(&mutex); // Start of critical section. Locker release so will the mutex - In house stuff
// Code to remove said object but indirectly modifying the reference count in copy below
}
void Process() {
std::vector<std::shared_ptr<SomeObject>> copy;
{
Locker locker(&mutes);
copy = std::vector<std::shared_ptr<SomeObject>>(
list.begin(), list.end()
)
}
for (auto it = copy.begin(), it != copy.end(); ++it) {
it->Procss(); // This may take time/add/remove to the list
}
}
};
One thread runs Process. Multiple threads run add/remove.
Will the reference count be safe and always correct - or should a mutex be placed around that?
Yes, the standard (at §20.8.2.2, at least as of N3997) that's intended to require that the reference counting be thread-safe.
For your simple cases like Add:
void Add(std::shared_ptr<SomeObject> o) {
Locker locker(&mutex);
list.push_back(std::make_shared<SomeObject>(o).
}
...the guarantees in the standard are strong enough that you shouldn't need the mutex, so you can just have:
void Add(std::shared_ptr<SomeObject> o) {
list.push_back(std::make_shared<SomeObject>(o).
}
For some of your operations, it's not at all clear that thread-safe reference counting necessarily obviates your mutex though. For example, inside of your Process, you have:
{
Locker locker(&mutes);
copy = std::vector<std::shared_ptr<SomeObject>>(
list.begin(), list.end()
)
}
This carries out the entire copy as an atomic operation--nothing else can modify the list during the copy. This assures that your copy gives you a snapshot of the list precisely as it was when the copy was started. If you eliminate the mutex, the reference counting will still work, but your copy might reflect changes made while the copy is being made.
In other words, the thread safety of the shared_ptr only assures that each individual increment or decrement is atomic--it doesn't assure that manipulations of the entire list are atomic as the mutex does in this case.
Since your list is actually a vector, you should be able to simplify the copying code a bit to just copy = list.
Also note that your Locker seems to be a subset of what std::lock_guard provides. It appears you could use:
std::lock_guard<std::mutex> locker(&mutes);
...in its place quite easily.
It will be an overhead to have a mutex for reference counting.
Internally, mutexes use atomic operations, basically a mutex does internal thread safe reference counting. So you can just use atomics for your reference counting directly instead of using a mutex and essentially doing double the work.
Unless your CPU architecture have an atomic increment/decrement and you used that for reference count, then, no, it's not safe; C++ makes no guarantee on the thread safety of x++/x-- operation on any of its standard types.
Use atomic<int> if your compiler supports them (C++11), otherwise you'll need to have the lock.
Further references:
https://www.threadingbuildingblocks.org/docs/help/tbb_userguide/Atomic_Operations.htm
I have a std::map that is going to be modified from multiple threads, and I have a mutex to lock these writes. However, the map must occasionally be used during a long-running operation, and it would not be good to hold the lock during this entire operation, blocking all of those writes.
I have two questions about this:
Is it thread-safe to perform that operation on a copy of the map while other threads are writing to the original?
Is it thread-safe to copy the map while other threads are mutating it?
For example:
class Foo {
void writeToMap(Bar &bar, Baz &baz) {
// lock mutex
map[bar] = baz;
// unlock mutex
}
std::map<Bar, Baz> getMapCopy() {
// lock mutex (Is this necessary?)
std::map<Bar, Baz> copy (map);
// unlock mutex
return copy;
}
std::map<Bar, Baz> map;
};
void expensiveOperation(Foo &f) {
std::map<Bar, Baz> mapCopy = f.getMapCopy();
// Can I safely read mapCopy?
}
Sounds like the copy operation itself is not thread safe, the issue being atomicity of copying 64-bit values. You copy the first 4 bytes, while the second 4 is being modified by another thread leaving you with inconsistent 8 bytes.
Please have a look here: Is copy thread-safe?
If you manage to create a consistent copy, however, I do not see why not...
You have undefined behavior in that code, as you return a reference to a local variable. This locla variable will be destructed once the function returns, and you now have a reference to a destructed object.
If you want to return a copy then you have to return by value, so it would be e.g.
std::map<Bar, Baz> getMapCopy() {
return map;
}
And if you use the mutexes and locks from the C++11 standard thread library you don't need an explicit unlock, the mutex will be unlocked with the destruction of the lock.
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'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.