Is shared_ptr destruction safe with multiple threads? - c++

I have two classes similar to this:
class Foo {
public:
void bar() {
std::lock_guard<std::mutex> lock(m_mutex);
m_data.push_back('x');
}
private:
std::string m_data;
std::mutex m_mutex;
};
class Pool {
public:
static std::shared_ptr<Foo> Create(int index) {
std::lock_guard<std::mutex> lock(m_mutex);
if (m_pool.size() > 10) {
m_pool.erase(m_pool.begin());
}
std::shared_ptr<Foo>& ptr = m_pool[index];
if (!ptr) ptr.reset(new Foo);
return ptr;
}
private:
static std::mutex m_mutex;
static std::map<int, std::shared_ptr<Foo>> m_pool;
};
and several threads running this code:
void parallel_function(int index) {
// several threads can get the same index
std::shared_ptr<Foo> foo = Pool::Create(index);
foo->bar();
}
Cppreference says
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.
Two questions:
Since Pool::Create always returns copies of the shared_ptr, I assume that the copy and destruction of each shared_ptr is thread-safe, either if it happens in m_pool.erase or at the end of parallel_function. Is this correct?
I call shared_ptr::operator->, which is a const member function, and the function Foo::bar is thread-safe. Is there a data race here?

To sum up my comments.
Yes, this is thread safe as you operate separate copies of shared_ptrs in different threads. Which is one of the few cases where passing copies of shared_ptrs is actually reasonable.
operator-> is a const member. So basically your code is fine as long as Foo::bar being race-free stands true (which it clearly is now).

Related

C++ std::shared_ptr custom deleter thread safety

[edited with a more concrete example]
Suppose I have a thread-safe object (all public member use a mutex) and a shared_ptr with a custom deleter, like so:
class A {
public:
void update(int x);
void print_sum();
...
}
class AContainer {
private SomeConcurrentMap<string, shared_ptr<A>> aMap;
void newA(string name) {
aMap.emplace(name, shared_ptr<A>(new A, [](A *p){p->print_sum(); delete p;}));
}
void finalizeA(string name) {
aMap.erase(name);
}
shared_ptr<A> getA(string name) const {
// fixme handle case of not found...
return aMap.find(name).second;
}
};
void someFunctionInSomeThread(const AContainer &cont, string name, int c) {
// fixme handle case of not found...
cont.getA(name)->update(c);
}
Let's assume all A operation are protected by a mutex, and that SomeConcurrentMap is thread-safe. The usage is scenario is:
call AContainer::newA() from any thread
call someFunctionInSomeThread() multiple times by multiple threads
call AContainer::finalizeA() from any thread - possibly in parallel to step 2
And the idea that A::print_sum() is called after both step 3 completed and all running A::update() operations complete.
Is it safe to assume that by the time p->print_sum() is called, all the A::update() operations on the object have been called?
Is it safe to assume that by the time p->print_sum() is called, all the A::update() operations on the object have been called?
Yes, it is safe to assume that. Only one thread is going to call the destructor, and no thread is going to call the destructor before calling other member functions of A (such a thread would be invoking UB even if no other threads existed, e.g. by keeping a raw pointer after destroying the shared pointer and then accessing the object via the raw pointer).

Is there a C++ smart pointer that could wrap up an object to make it thread safe?

I wanted to ask if there is a smart pointer that could take in any class in its template and then any operations done with such pointer would result in a thread-safe operation.
Basically an idea would be that such pointer would automatically hold an internal lock during a scope and release it when the pointer goes out of scope.
Use case would be for example to pull such pointer from a static, pre-allocated array into some scope and perform thread-safe operations inside that scope on the object itself.
I tried to find a C++ library/feature that could perhaps allow for some thread-safe mutation on objects by wrapping it into a single smart pointer object.
if there is a smart pointer that could take in any class in its template and then any operations done with such pointer would result in a thread-safe operation.
No, there is no such smart pointer in the C++ standard.
I don't think that's possible in the "usual" smart pointer sense, because when doing ptr->something() or (*ptr).something(), the operator-> and operator* methods are called, they return the pointer/reference and then something is invoked, so you don't have any way to know when to unlock the mutex after the operation has been done. This can be worked around through proxy objects, but that's another can of worms, especially when mixed with usage of auto.
Moreover, on a higher level this is rarely a kind of thread-safety guarantee one actually needs. In a codebase of ours someone once wrote a wrapper for std::map with a mutex protecting some common mutation operations; this was eminently useless for several reasons. The most obvious was that operator[] returns a reference anyway (so, you get a reference that may be instantly invalidated by someone else calling e.g. erase()); but most importantly, people did stuff like if (!map.count(key)) { map[key].do_something(); }, ignoring the fact that the result of count became stale immediately.
The takeaway here is that generally mutex-wrapping single operations on an objects doesn't gain you much: to actually work safely in a sane manner usually you need to take a mutex for a longer period, to ensure your code has a consistent snapshot of the protected object state.
A possibility to attack both these problems is to turn the whole thing to a different angle: you may wrap your object in an "escrow" object that forces you to take the mutex to access the data, but also think in terms of "doing all the operations where you need it" in a single "mutex-take". A sketch may be something like:
template<typename T>
class MutexedPtr {
std::mutex mtx;
std::unique_ptr<T> ptr;
public:
MutexedPtr(std::unique_ptr<T> ptr) : ptr(std::move(ptr)) {}
template<typename FnT>
void access(FnT fn) {
std::lock_guard<std::mutex> lk(mtx);
fn(*ptr);
}
};
The usage should be something like:
MutexedPtr<Something> ptr = ...;
...
ptr.access([&](Something &obj) {
// do your stuff with obj while the mutex is taken
});
whether this is something that could be useful to your use case is up to you.
I wanted to ask if there is a smart pointer that could take in any class in its template and then any operations done with such pointer would result in a thread-safe operation.
Yes, that's possible. Here's a simple implementation:
#include <thread>
#include <mutex>
#include <cstdio>
template <class T>
struct SyncronizedPtrImpl {
private:
std::scoped_lock<std::mutex> lock;
T* t;
public:
SyncronizedPtrImpl(std::mutex& mutex, T* t) : lock(mutex), t(t) {}
T* operator->() const { return t; }
};
template <class T>
struct SyncronizedPtr {
private:
std::mutex mutex;
T* p;
public:
SyncronizedPtrImpl<T> operator->() {
return SyncronizedPtrImpl<T>{mutex, p};
}
SyncronizedPtr(T* p) : p(p) {}
~SyncronizedPtr() { delete p; }
};
int main() {
struct Foo {
int val = 0;
};
SyncronizedPtr ptr(new Foo);
std::thread t1([&]{
for (int i = 0; i != 10; ++i) ++ptr->val;
});
std::thread t2([&]{
for (int i = 0; i != 10; ++i) --ptr->val;
});
t1.join();
t2.join();
return ptr->val == 0;
}

Could the mutex protect the data relative to the specific pointer?

My Qt app uses QMutex and QMutexLocker to ensure thread-safety.
Does the mutex protect the data or scope of the function?
For example:
class Counter
{
public:
Counter() { *ptr; }
void setObject(MyClass *pr){ptr = pr;}
void increment() { QMutexLocker locker(&mutex); //dowork for ptr; }
void decrement() { QMutexLocker locker(&mutex); //dowork for pointer to MyClass, ptr; }
private:
mutable QMutex mutex;
MyClass *ptr;
};
//Thread...
Counter counter;
MyClass *mclass= new MyClass;
//setting... mclass
counter.setOjbect(mclass);
OtherClass oc; //This `OtherClass` also works for mclass same as the value of Counter.
oc.setObject(mclass); //Counter and OtherClass work for mclass.
//Mutex protect mclass data?
The pointer to MyClass could be used in some other class.
Does QMutexLocker protect the data for ptr or protect only accessing function increment and decrement from the multiple calling?
How can I protect data at ptr?
Mutual exclusion is ensured for all threads using the same QMutex instance e.g. it protects data. So another class cannot synchronize its access to MyClass because it cannot access the mutex (unless you can ensure two thread do not touch the same member fields).
You should guarantee that everyone who accesses MyClass instance uses the same mutex instance. This can be done by:
Moving the mutex to MyClass instance. This is what you most likely need.
Use a single global pool of mutexes and use MyClass instance address to select a mutex for it.
The latter way is shown below:
const std::size_t SIZE = 47; // prime numbers work better here
statuc QMutex g_mtx[SIZE];
QMutex &get_mutex(const void *ptr)
{
return g_mtx[std::uintptr_t(ptr) % SIZE];
}
To guard an instance of MyClass pointer to ptr you should use QMutexLocker(get_mutex(ptr)). This is useful if MyClass is a small object and it exists in large numbers, so keeping a separate mutex for each instance becomes a problem.

What is the best way to lock object by private mutex?

I need to lock object by private mutex in some external functions. What is the best way to do this?
I want something like this
#include <thread>
#include <mutex>
class Test
{
public:
std::lock_guard<std::mutex> lockGuard()
{
return std::lock_guard<std::mutex>(mutex);
}
private:
std::mutex mutex;
};
int main()
{
Test test;
std::lock_guard<std::mutex> lock = test.lockGuard();
//...
}
But lock_guard copy contructor is deleted. How can I do something like this?
Just use std::unique_lock<std::mutex> instead. It is not copyable, but it is movable, allowing the pattern you show.
#include <thread>
#include <mutex>
class Test
{
public:
std::unique_lock<std::mutex> lockGuard()
{
return std::unique_lock<std::mutex>(mutex);
}
private:
std::mutex mutex;
};
int main()
{
Test test;
std::unique_lock<std::mutex> lock = test.lockGuard();
//...
}
std::unique_lock<std::mutex> has a broadened API relative to std::lock_guard including:
Move constructible.
Move assignable.
Swappable.
lock()
unlock()
try_lock()
try_lock_for()
try_lock_until()
release()
owns_lock()
In other words, since you can unlock and move from a unique_lock, it is not guaranteed to hold the lock on the mutex (you can check that it does with owns_lock()). In contrast an invariant of lock_guard is that it always holds the lock on the mutex.
The std::unique_lock<T> has a move constructor defined and can be used as you like, but the approach is not very successful itself.
You should review your locking granularity, usually if you can't provide internal synchronization and ask user to maintain lock while performing operations (or when you need to perform multiple operations) on an object, there is no reason to store the mutex inside the object.
If I had to store the mutex inside object, I would use some wrapper which allows me to do the following:
locking_wrapper<Test> test;
test.do_locked([] (Test & instance) {
/* The following code is guaranteed not to interleave with
* any operations performed on instance from other threads. */
// your code using instance here
});
The locking_wrapper<T> would store store an instance of an object inside and provide a reference to it while maintaining a lock on internal mutex. Relying on the compiler's ability to inline code, such approach should give no overhead above what you're trying to do in your question.
The general idea on implementing the locking_wrapper is as follows:
template<typename T>
class locking_wrapper
{
mutable std::mutex mutex;
// the object which requires external synchronization on access
T instance;
public:
/* Here we define whatever constructors required to construct the
* locking_wrapper (e.g. value-initialize the instance, take an
* instance passed by user or something different) */
locking_wrapper() = default;
locking_wrapper(const T & instance) : instance{instance} {}
// Takes a functor to be performed on instance while maintaining lock
template<typename Functor>
void do_locked(Functor && f) const {
const std::lock_guard<std::mutex> lock{mutex};
f(instance);
}
};
You may pass whatever callable-entity to do_locked as you see fit, however calling it with a lambda-expression as I've suggested previously will give it the best chances to be inlined without any overhead.
Please note that using this approach with references, movable objects or some other kind I have not yet foreseen would require some modifications to the code.

Designing a thread-safe copyable class

The straightforward way to make a class threadsafe is to add a mutex attribute and lock the mutex in the accessor methods
class cMyClass {
boost::mutex myMutex;
cSomeClass A;
public:
cSomeClass getA() {
boost::mutex::scoped_lock lock( myMutex );
return A;
}
};
The problem is that this makes the class non-copyable.
I can make things work by making the mutex a static. However, this means that every instance of the class blocks when any other instance is being accessed, because they all share the same mutex.
I wonder if there is a better way?
My conclusion is that there is no better way. Making a class thread-safe with private static mutex attribute is ‘best’: - it is simple, it works, and it hides the awkward details.
class cMyClass {
static boost::mutex myMutex;
cSomeClass A;
public:
cSomeClass getA() {
boost::mutex::scoped_lock lock( myMutex );
return A;
}
};
The disadvantage is that all instances of the class share the same mutex and so block each other unnecessarily. This cannot be cured by making the mutex attribute non-static ( so giving each instance its own mutex ) because the complexities of copying and assignment are nightmarish, if done properly.
The individual mutexes, if required, must be managed by an external non-copyable singleton with links established to each instance when created.
Thanks for all the responses.
Several people have mentioned writing my own copy constructor and assignment operator. I tried this. The problem is that my real class has many attributes which are always changing during development. Maintaining both the copy constructor and assignmet operator is tedious and error-prone, with errors creating hard to find bugs. Letting the compiler generate these for complex class is an enormous time saver and bug reducer.
Many responses are concerned about making the copy constructor and assignment operator thread-safe. This requirement adds even more complexity to the whole thing! Luckily for me, I do not need it since all the copying is done during set-up in a single thread.
I now think that the best approach would be to build a tiny class to hold just a mutex and the critical attributes. Then I can write a small copy constructor and assignment operator for the critical class and leave the compiler to look after all the other attributes in the main class.
class cSafe {
boost::mutex myMutex;
cSomeClass A;
public:
cSomeClass getA() {
boost::mutex::scoped_lock lock( myMutex );
return A;
}
(copy constructor)
(assignment op )
};
class cMyClass {
cSafe S;
( ... other attributes ... )
public:
cSomeClass getA() {
return S.getA();
}
};
You can define your own copy constructor (and copy assignment operator). The copy constructor would probably look something like this:
cMyClass(const cMyClass& x) : A(x.getA()) { }
Note that getA() would need to be const-qualified for this to work, which means the mutex would need to be mutable; you could make the parameter a non-const reference, but then you can't copy temporary objects, which usually isn't desirable.
Also, consider that it isn't always a good idea to perform locking at such a low level: if you lock the mutex in the accessor and the mutator functions, you lose a lot of functionality. For example, you can't perform a compare-and-swap because you can't get and set the member variable with a single lock of the mutex, and if you have multiple data members controlled by the mutex, you can't access more than one of them with the mutex locked.
As simple as the question might be, getting it right is not so simple. For starters we can work the easy copy constructor:
// almost pseudo code, mutex/lock/data types are synthetic
class test {
mutable mutex m;
data d;
public:
test( test const & rhs ) {
lock l(m); // Lock the rhs to avoid race conditions,
// no need to lock this object.
d = rhs.d; // perform the copy, data might be many members
}
};
Now creating an assignment operator is more complex. The first thing that comes to mind is just doing the same, but in this case locking both the lhs and rhs:
class test { // wrong
mutable mutex m;
data d;
public:
test( test const & );
test& operator=( test const & rhs ) {
lock l1( m );
lock l2( rhs.m );
d = rhs.d;
return *this;
}
};
Simple enough, and wrong. While we are guaranteeing single threaded access to the objects (both) during the operation, and thus we get no race conditions, we have a potential deadlock:
test a, b;
// thr1 // thr2
void foo() { void bar() {
a = b; b = a;
} }
And that is not the only potential deadlock, the code is not safe for self assignment (most mutex are not recursive, and trying to lock the same mutex twice will block the thread). The simple thing to solve is the self assignment:
test& test::operator=( test const & rhs ) {
if ( this == &rhs ) return *this; // nothing to do
// same (invalid) code here
}
For the other part of the problem you need to enforce an order in how the mutexes are acquired. That could be handled in different ways (storing a unique identifier per object an comparing...)
test & test::operator=( test const & rhs ) {
mutex *first, *second;
if ( unique_id(*this) < unique_id(rhs ) {
first = &m;
second = &rhs.m;
} else {
first = &rhs.m;
second = &rhs.m;
}
lock l1( *first );
lock l2( *second );
d = rhs.d;
}
The specific order is not as important as the fact that you need to ensure the same order in all uses, or else you will potentially deadlock the threads. As this is quite common, some libraries (including the upcoming c++ standard) have specific support for it:
class test {
mutable std::mutex m;
data d;
public:
test( const test & );
test& operator=( test const & rhs ) {
if ( this == &rhs ) return *this; // avoid self deadlock
std::lock( m, rhs.m ); // acquire both mutexes or wait
std::lock_guard<std::mutex> l1( m, std::adopt_lock ); // use RAII to release locks
std::lock_guard<std::mutex> l2( rhs.m, std::adopt_lock );
d = rhs.d;
return *this;
}
};
The std::lock function will acquire all locks passed in as argument and it ensures that the order of acquisition is the same, ensuring that if all code that needs to acquire these two mutexes does so by means of std::lock there will be no deadlock. (You can still deadlock by manually locking them somewhere else separately). The next two lines store the locks in objects implementing RAII so that if the assignment operation fails (exception is thrown) the locks are released.
That can be spelled differently by using std::unique_lock instead of std::lock_guard:
std::unique_lock<std::mutex> l1( m, std::defer_lock ); // store in RAII, but do not lock
std::unique_lock<std::mutex> l2( rhs.m, std::defer_lock );
std::lock( l1, l2 ); // acquire the locks
I just thought of a different much simpler approach that I am sketching here. The semantics are slightly different, but may be enough for many applications:
test& test::operator=( test copy ) // pass by value!
{
lock l(m);
swap( d, copy.d ); // swap is not thread safe
return *this;
}
}
There is a semantic difference in both approaches, as the one with copy-and-swap idiom has a potential race condition (that might or might not affect your application, but that you should be aware of). Since both locks are never held at once, the objects may change between the time the first lock is released (copy of the argument completes) and the second lock is acquired inside operator=.
For an example of how this might fail, consider that data is an integer and that you have two objects initialized with the same integer value. One thread acquires both locks and increments the values, while another thread copies one of the objects into the other:
test a(0), b(0); // ommited constructor that initializes the ints to the value
// Thr1
void loop() { // [1]
while (true) {
std::unique_lock<std::mutex> la( a.m, std::defer_lock );
std::unique_lock<std::mutex> lb( b.m, std::defer_lock );
std::lock( la, lb );
++a.d;
++b.d;
}
}
// Thr1
void loop2() {
while (true) {
a = b; // [2]
}
}
// [1] for the sake of simplicity, assume that this is a friend
// and has access to members
With the implementations of operator= that perform simultaneous locks on both objects, you can assert at any one given time (doing it thread safely by acquiring both locks) that a and b are the same, which seems to be expected by a cursory read of the code. That does not hold if operator= is implemented in terms of the copy-and-swap idiom. The issue is that in the line marked as [2], b is locked and copied into a temporary, then the lock is released. The first thread can then acquire both locks at once, and increment both a and b before a is locked by the second thread in [2]. Then a is overwritten with the value that b had before the increment.
The simple fact is that you cannot make a class thread safe by spewing mutexes at the problem. The reason that you can't make this work is because it doesn't work, not because you're doing this technique wrong. This is what everyone noticed when multithreading first came and started slaughtering COW string implementations.
Thread design occurs at the application level, not on a per-class basis. Only specific resource management classes should have thread-safety on this level- and for them you need to write explicit copy constructors/assignment operators anyway.