Hi,
Let's say that we want to make class A thread-safe using an std::mutex. I am having my copy constructor and assignment operator similarly to the code below:
#include <mutex>
class A {
private:
int i;
mutable std::mutex mtx;
public:
A() : i(), mtx() { }
A(const A& other) : i(), mtx()
{
std::lock_guard<std::mutex> _lock(other.mtx);
i = other.i;
}
A& operator=(const A& other)
{
if (this!=&other) {
std::lock_guard<std::mutex> _mylock(mtx), _otherlock(other.mtx);
i = other.i;
}
return *this;
}
int get() const
{
std::lock_guard<std::mutex> _mylock(mtx);
return i;
}
};
I do not think that it has any problems, other than the possibility of other to be destroyed by another thread before being copied, which I can deal with.
My issue is that I haven't seen this pattern anywhere, so I do not know if people just haven't had a need for that or that it is plainly wrong for reasons I currently don't see.
Thanks
NOTES:
This is just an example. I can have an arbitrary number of member variables of any type, it does not have to be just an int.
After Martin York's comment for possible deadlocking, this is an updated version that uses copy-and-swap (copy elision is also possible, but it wouldn't handle efficiently the self-assignment case).
I also changed int to T, so people cannot assume that it is a POD.
template<typename T>
class A {
private:
T t;
mutable std::mutex mtx;
public:
A() : t(), mtx() { }
A(const A& other) : t(), mtx()
{
std::lock_guard<std::mutex> _lock(other.mtx);
t = other.t;
}
A& operator=(const A& other)
{
if (this!=&other) {
A tmp(other);
std::lock_guard<std::mutex> _lock(mtx);
std::swap(t, tmp.t);
}
return *this;
}
T get() const
{
std::lock_guard<std::mutex> _lock(mtx);
return t;
}
};