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