C++ thread-safe bracket operator proxy - c++

Given a simple wrapper around a standard vector, what is a good way to implement operator[] in a thread-safe way in order to be able to set the content as usual?
struct bracket_operator_proxy;
struct example
{
auto operator[](size_t i) const { return bracket_operator_proxy(v, m, i); }
private:
std::vector<double> v;
std::mutex m;
};
Here is my quick and naive attempt for bracket_operator_proxy:
struct bracket_operator_proxy
{
bracket_operator_proxy(std::vector<double>& v, std::mutex& m, int i)
: v(v), m(m), i(i) {}
operator double() const
{
std::lock_guard<std::mutex> l(m);
return v[i];
}
auto operator=(double d)
{
std::lock_guard<std::mutex> l(m);
v[i] = d;
return d;
}
//... further assignment operators ...
private:
std::vector<double>& v;
std::mutex& m;
int i;
};
Is this already enough? Or am I missing something which will blow my leg off?

Once you have operator-> (which is very useful) you'll need to return a -> proxy which extends lock lifetime until end of statement, and exposes you to single threaded deadlock.
Look at thread safe monads/functor/wrapper like the one here. It doesn't make the locks completely transparent, but they should not be.
Do not share data between threads
If you share data, make it immutable
If it must be mutated, isolate access through a bottleneck of known safe design. A message queue say.
If you cannot do that, consider redesign
Really. Atomic maybe?
Have a limited set of functions that manage locks explicitly
Ok, now wrap in reader/writer monad as above, with easy-ish compound operations
Make code that magically gets locks and looks just like non-thread interacting code, thus lulling your readers into a false sense of security and efficiency
In decreasing preference.
The dangerous and hard part of thread safety is not the fact that the syntax is awkward. It is that lock based thread safety is nearly impossible to prove correct and safe. Making the syntax easier to use is not a high value goal.
As an example, v[i]=v[i+1] is fraught with a lack of synchronization: between the read and the write anything could have changed. Let alone the problem of "is i a valid index?"

Related

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;
}

Best practices for move constructor of a class with mutex-locked caching

I sometimes have classes like this:
class HasMutexLockedCache {
private:
SomeType m_regularData;
mutable std::mutex m_mutex;
mutable std::optaional<T> m_cache;
// Etc.
public:
const m_regularData& getData() const { return m_regularData; }
const T& getExpensiveResult() const {
std::scoped_lock lock(m_mutex);
if (!m_cache) {
m_cache = expensiveFunction();
}
return m_cache;
}
HasMutexLockedCache(const HasMutexLockedCache& other)
: m_regularData(other.m_regularData)
{
std::scoped_lock lock(other.m_mutex);
// I figure we don't have to lock this->m_mutex because
// we are in the c'tor and so nobody else could possibly
// be messing with m_cache.
m_cache = other.m_cache;
}
HasMutexLockedCache(HasMutexLockedCache&& other)
: m_regularData(std::move(other.m_regularData))
{
// What here?
}
HasMutexLockedCache& operator=(HasMutexLockedCache&& other) {
m_regularData = std::move(other.m_regularData);
// Bonus points: What here? Lock both mutexes? One?
// Only lock this->m_mutex depending on how we
// document thread safety?
}
};
My question: what goes in HasMutexLockedCache(HasMutexLockedCache&& other) (and likewise in HasMutexLockedCache& operator=(HasMutexLockedCache&& other)? I think we don't need to lock other.m_mutex because, for other to be an rvalue reference, we know nobody else can see it, just like we don't have to lock this->m_mutex in the c'tor. However, I'd like some guidance. What are the best practices here? Should we be locking other.m_mutex?
You have to remember that l-value objects can be moved using std::move so we do need to lock them too:
HasMutexLockedCache(HasMutexLockedCache&& other) {
std::scoped_lock lock(other.m_mutex);
m_cache = std::move(other.m_cache);
}
HasMutexLockedCache& operator=(HasMutexLockedCache&& other) {
std::scoped_lock lock(m_mutex, other.m_mutex);
m_cache = std::move(other.m_cache);
return *this;
}
I'd like some guidance. What are the best practices here? Should we be locking other.m_mutex?
#Galik's answer explains how one could implement a move-constructor for this, however you should consider whether this is a safe and coherent idea for your abstraction.
If an object contains a std::mutex, generally this means that it may have concurrent accesses at different times which warrant this. If this is the case, then move-semantics can be quite difficult to work with when faced with multi-threading -- since you may have thread A move the contents of m_cache before thread B accesses it, resulting in it reading a moved-from state (which, depending on what the state is being checked, may not be well-defined). These types of errors can be quite complicated to debug, and even harder to reproduce!
Often if you have a type like this, it would be better to be sharing this type across threads explicitly, either with a shared lifetime via shared_ptr, or some form of synchronization from outside so each thread cannot destructively interfere with one another.

How should I deal with mutexes in movable types in C++?

By design, std::mutex is not movable nor copyable. This means that a class A holding a mutex won't receive a default move constructor.
How would I make this type A movable in a thread-safe way?
Let's start with a bit of code:
class A
{
using MutexType = std::mutex;
using ReadLock = std::unique_lock<MutexType>;
using WriteLock = std::unique_lock<MutexType>;
mutable MutexType mut_;
std::string field1_;
std::string field2_;
public:
...
I've put some rather suggestive type aliases in there that we won't really take advantage of in C++11, but become much more useful in C++14. Be patient, we'll get there.
Your question boils down to:
How do I write the move constructor and move assignment operator for this class?
We'll start with the move constructor.
Move Constructor
Note that the member mutex has been made mutable. Strictly speaking this isn't necessary for the move members, but I'm assuming you also want copy members. If that is not the case, there is no need to make the mutex mutable.
When constructing A, you do not need to lock this->mut_. But you do need to lock the mut_ of the object you're constructing from (move or copy). This can be done like so:
A(A&& a)
{
WriteLock rhs_lk(a.mut_);
field1_ = std::move(a.field1_);
field2_ = std::move(a.field2_);
}
Note that we had to default construct the members of this first, and then assign them values only after a.mut_ is locked.
Move Assignment
The move assignment operator is substantially more complicated because you do not know if some other thread is accessing either the lhs or rhs of the assignment expression. And in general, you need to guard against the following scenario:
// Thread 1
x = std::move(y);
// Thread 2
y = std::move(x);
Here is the move assignment operator that correctly guards the above scenario:
A& operator=(A&& a)
{
if (this != &a)
{
WriteLock lhs_lk(mut_, std::defer_lock);
WriteLock rhs_lk(a.mut_, std::defer_lock);
std::lock(lhs_lk, rhs_lk);
field1_ = std::move(a.field1_);
field2_ = std::move(a.field2_);
}
return *this;
}
Note that one must use std::lock(m1, m2) to lock the two mutexes, instead of just locking them one after the other. If you lock them one after the other, then when two threads assign two objects in opposite order as shown above, you can get a deadlock. The point of std::lock is to avoid that deadlock.
Copy Constructor
You didn't ask about the copy members, but we might as well talk about them now (if not you, somebody will need them).
A(const A& a)
{
ReadLock rhs_lk(a.mut_);
field1_ = a.field1_;
field2_ = a.field2_;
}
The copy constructor looks much like the move constructor except the ReadLock alias is used instead of the WriteLock. Currently these both alias std::unique_lock<std::mutex> and so it doesn't really make any difference.
But in C++14, you will have the option of saying this:
using MutexType = std::shared_timed_mutex;
using ReadLock = std::shared_lock<MutexType>;
using WriteLock = std::unique_lock<MutexType>;
This may be an optimization, but not definitely. You will have to measure to determine if it is. But with this change, one can copy construct from the same rhs in multiple threads simultaneously. The C++11 solution forces you to make such threads sequential, even though the rhs isn't being modified.
Copy Assignment
For completeness, here is the copy assignment operator, which should be fairly self explanatory after reading about everything else:
A& operator=(const A& a)
{
if (this != &a)
{
WriteLock lhs_lk(mut_, std::defer_lock);
ReadLock rhs_lk(a.mut_, std::defer_lock);
std::lock(lhs_lk, rhs_lk);
field1_ = a.field1_;
field2_ = a.field2_;
}
return *this;
}
And etc.
Any other members or free functions that access A's state will also need to be protected if you expect multiple threads to be able to call them at once. For example, here's swap:
friend void swap(A& x, A& y)
{
if (&x != &y)
{
WriteLock lhs_lk(x.mut_, std::defer_lock);
WriteLock rhs_lk(y.mut_, std::defer_lock);
std::lock(lhs_lk, rhs_lk);
using std::swap;
swap(x.field1_, y.field1_);
swap(x.field2_, y.field2_);
}
}
Note that if you just depend on std::swap doing the job, the locking will be at the wrong granularity, locking and unlocking between the three moves that std::swap would internally perform.
Indeed, thinking about swap can give you insight into the API you might need to provide for a "thread-safe" A, which in general will be different from a "non-thread-safe" API, because of the "locking granularity" issue.
Also note the need to protect against "self-swap". "self-swap" should be a no-op. Without the self-check one would recursively lock the same mutex. This could also be solved without the self-check by using std::recursive_mutex for MutexType.
Update
In the comments below Yakk is pretty unhappy about having to default construct things in the copy and move constructors (and he has a point). Should you feel strongly enough about this issue, so much so that you are willing to spend memory on it, you can avoid it like so:
Add whatever lock types you need as data members. These members must come before the data that is being protected:
mutable MutexType mut_;
ReadLock read_lock_;
WriteLock write_lock_;
// ... other data members ...
And then in the constructors (e.g. the copy constructor) do this:
A(const A& a)
: read_lock_(a.mut_)
, field1_(a.field1_)
, field2_(a.field2_)
{
read_lock_.unlock();
}
Oops, Yakk erased his comment before I had the chance to complete this update. But he deserves credit for pushing this issue, and getting a solution into this answer.
Update 2
And dyp came up with this good suggestion:
A(const A& a)
: A(a, ReadLock(a.mut_))
{}
private:
A(const A& a, ReadLock rhs_lk)
: field1_(a.field1_)
, field2_(a.field2_)
{}
Given there doesn't seem to be a nice, clean, easy way to answer this - Anton's solution I think is correct but its definitely debatable, unless a better answer comes up I would recommend putting such a class on the heap and looking after it via a std::unique_ptr:
auto a = std::make_unique<A>();
Its now a fully movable type and anyone who has a lock on the internal mutex whilst a move happens is still safe, even if its debatable whether this is a good thing to do
If you need copy semantics just use
auto a2 = std::make_shared<A>();
This is an upside-down answer. Instead of embedding "this objects needs to be synchronized" as a base of the type, instead inject it under any type.
You deal with a synchronized object very differently. One big issue is you have to worry about deadlocks (locking multiple objects). It also should basically never be your "default version of an object": synchronized objects are for objects that will be in contention, and your goal should be to minimize contention between threads, not sweep it under the rug.
But synchronizing objects is still useful. Instead of inheriting from a synchronizer, we can write a class that wraps an arbitrary type in synchronization. Users have to jump through a few hoops to do operations on the object now that it is synchronized, but they are not limited to some hand-coded limited set of operations on the object. They can compose multiple operations on the object into one, or have an operation on multiple objects.
Here is a synchronized wrapper around an arbitrary type T:
template<class T>
struct synchronized {
template<class F>
auto read(F&& f) const&->std::result_of_t<F(T const&)> {
return access(std::forward<F>(f), *this);
}
template<class F>
auto read(F&& f) &&->std::result_of_t<F(T&&)> {
return access(std::forward<F>(f), std::move(*this));
}
template<class F>
auto write(F&& f)->std::result_of_t<F(T&)> {
return access(std::forward<F>(f), *this);
}
// uses `const` ness of Syncs to determine access:
template<class F, class... Syncs>
friend auto access( F&& f, Syncs&&... syncs )->
std::result_of_t< F(decltype(std::forward<Syncs>(syncs).t)...) >
{
return access2( std::index_sequence_for<Syncs...>{}, std::forward<F>(f), std::forward<Syncs>(syncs)... );
};
synchronized(synchronized const& o):t(o.read([](T const&o){return o;})){}
synchronized(synchronized && o):t(std::move(o).read([](T&&o){return std::move(o);})){}
// special member functions:
synchronized( T & o ):t(o) {}
synchronized( T const& o ):t(o) {}
synchronized( T && o ):t(std::move(o)) {}
synchronized( T const&& o ):t(std::move(o)) {}
synchronized& operator=(T const& o) {
write([&](T& t){
t=o;
});
return *this;
}
synchronized& operator=(T && o) {
write([&](T& t){
t=std::move(o);
});
return *this;
}
private:
template<class X, class S>
static auto smart_lock(S const& s) {
return std::shared_lock< std::shared_timed_mutex >(s.m, X{});
}
template<class X, class S>
static auto smart_lock(S& s) {
return std::unique_lock< std::shared_timed_mutex >(s.m, X{});
}
template<class L>
static void lock(L& lockable) {
lockable.lock();
}
template<class...Ls>
static void lock(Ls&... lockable) {
std::lock( lockable... );
}
template<size_t...Is, class F, class...Syncs>
friend auto access2( std::index_sequence<Is...>, F&&f, Syncs&&...syncs)->
std::result_of_t< F(decltype(std::forward<Syncs>(syncs).t)...) >
{
auto locks = std::make_tuple( smart_lock<std::defer_lock_t>(syncs)... );
lock( std::get<Is>(locks)... );
return std::forward<F>(f)(std::forward<Syncs>(syncs).t ...);
}
mutable std::shared_timed_mutex m;
T t;
};
template<class T>
synchronized< T > sync( T&& t ) {
return {std::forward<T>(t)};
}
C++14 and C++1z features included.
this assumes that const operations are multiple-reader safe (which is what std containers assume).
Use looks like:
synchronized<int> x = 7;
x.read([&](auto&& v){
std::cout << v << '\n';
});
for an int with synchronized access.
I'd advise against having synchronized(synchronized const&). It is rarely needed.
If you need synchronized(synchronized const&), I'd be tempted to replace T t; with std::aligned_storage, allowing manual placement construction, and do manual destruction. That allows proper lifetime management.
Barring that, we could copy the source T, then read from it:
synchronized(synchronized const& o):
t(o.read(
[](T const&o){return o;})
)
{}
synchronized(synchronized && o):
t(std::move(o).read(
[](T&&o){return std::move(o);})
)
{}
for assignment:
synchronized& operator=(synchronized const& o) {
access([](T& lhs, T const& rhs){
lhs = rhs;
}, *this, o);
return *this;
}
synchronized& operator=(synchronized && o) {
access([](T& lhs, T&& rhs){
lhs = std::move(rhs);
}, *this, std::move(o));
return *this;
}
friend void swap(synchronized& lhs, synchronized& rhs) {
access([](T& lhs, T& rhs){
using std::swap;
swap(lhs, rhs);
}, *this, o);
}
the placement and aligned storage versions are a bit messier. Most access to t would be replaced by a member function T&t() and T const&t()const, except at construction where you'd have to jump through some hoops.
By making synchronized a wrapper instead of part of the class, all we have to ensure is that the class internally respects const as being multiple-reader, and write it in a single-threaded manner.
In the rare cases we need a synchronized instance, we jump through hoops like the above.
Apologies for any typos in the above. There are probably some.
A side benefit to the above is that n-ary arbitrary operations on synchronized objects (of the same type) work together, without having to hard-code it before hand. Add in a friend declaration and n-ary synchronized objects of multiple types might work together. I might have to move access out of being an inline friend to deal with overload conficts in that case.
live example
First of all, there must be something wrong with your design if you want to move an object containing a mutex.
But if you decide to do it anyway, you have to create a new mutex in move constructor, that is e.g:
// movable
struct B{};
class A {
B b;
std::mutex m;
public:
A(A&& a)
: b(std::move(a.b))
// m is default-initialized.
{
}
};
This is thread-safe, because the move constructor can safely assume that its argument isn't used anywhere else, so the locking of the argument isn't required.
Using mutexes and C++ move semantics is an excellent way to safely and efficiently transfer data between threads.
Imagine a 'producer' thread that makes batches of strings and provides them to (one or more) consumers. Those batches could be represented by an object containing (potentially large) std::vector<std::string> objects.
We absolutely want to 'move' the internal state of those vectors into their consumers without unnecessary duplication.
You simply recognize the mutex as part of the object not part of the object's state. That is, you don't want to move the mutex.
What locking you need depends on your algorithm or how generalized your objects are and what range of uses you permit.
If you only ever move from a shared state 'producer' object to a thread-local 'consuming' object you might be OK to only lock the moved from object.
If it's a more general design you will need to lock both. In such a case you need to then consider dead-locking.
If that is a potential issue then use std::lock() to acquire locks on both mutexes in a deadlock free way.
http://en.cppreference.com/w/cpp/thread/lock
As a final note you need to make sure you understand the move semantics.
Recall that the moved from object is left in a valid but unknown state.
It's entirely possible that a thread not performing the move has a valid reason to attempt access the moved from object when it may find that valid but unknown state.
Again my producer is just banging out strings and the consumer is taking away the whole load. In that case every time the producer tries to add to the vector it may find the vector non-empty or empty.
In short if the potential concurrent access to the moved from object amounts to a write it's likely to be OK. If it amounts to a read then think about why it's OK to read an arbitrary state.

Is this hideous? Copyable mutex to protect class member

I'm trying to make a class thread safe by using a mutex.
class Container
{
private:
vector<Foo> v;
boost::mutex m;
public:
void add(Foo item)
{
m.lock();
v.push_back(item);
m.unlock();
}
};
The problem is that boost::mutex is non-copyable, so this makes Container noncopyable. Of course if I copy Container the new instance presumably doesn't need to keep the same mutex as the old one - it can have a new mutex of its own. I could write custom copy constructors for Container that do this but in reality it's a complex class and I don't want to. So how about this:
class CopyableMutex
{
private:
boost::mutex m;
public:
CopyableMutex() {}
CopyableMutex(CopyableMutex&) {} //don't copy, just create a new one
CopyableMutex& operator=(CopyableMutex&) {return *this;} //don't assign, keep it the same
void lock() {m.lock();}
void unlock() {m.unlock();}
};
...and then replacing boost::mutex in Container with CopyableMutex.
Is this a hideous thing to do? If not then am I reinventing the wheel - is there a library class that does this already?
Yes it's hideous.
The correct solution to the problem is a custom copy constructor and assignment operator for your container.
If the class is "too complex" to write a custom copy constructor for, then separate the thread-safety from the container and have base class container which doesn't contain a mutex and, perhaps, a derived class "thread safe container" which contains a mutex and has a custom copy constructor and assignment op that just call through to the automatically generated base class ones.
I believe that one of the things that doing it this way invalidates is the correct way to use mutexes these days:
void func()
{
std::lock_guard<std::mutex> lock(mtx);
// do things
}
As you can not return a mutex and so there would be no way to up-scope it to where you want to use it. The reason for the above style of usage is to prevent issues that arise when you use a lock, and then some form of unhanded exception happens prior to you unlock (ie it ensures a more reliable unlock).
I agree with other answer though, that a better way would be to just encapsulate your thread safety part and isolate it from your complex code (if possible), and then just make explicit copy constructors for that smaller class.

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.