I am reading Effective C++, in Rule 14: Think carefully about copying behavior in resource-managing classes, there is an example:
class Lock {
public:
explicit Lock(Mutex* pm) : mutexPtr(pm) {
lock(mutexPtr);
}
~Lock() {
unlock(mutexPtr);
}
private:
Mutex *mutexPtr;
};
It points out that if we construct the Lock as above, there will be a problem if we run the code below:
Mutex m;
Lock ml1(&m);
Lock ml2(ml1);
I think it may because the code may runs like below:
// ml1 constructes
lock(m)
// copy ml2, but ml1.mutexPtr and ml2.mutexPtr both point to m
ml2.mutexPtr = ml1.mutexPtr
// ml1 destructs
unlock(m)
// ml2 destructs
unlock(m)
So the m will be unlock for twice. So is it the real reason that cause the problem below? thx!
Yes, that is why the author is saying to be careful. If you use a recursive_mutex instead of plain mutex you can simply lock in the copy ctor and copy-assign operator, but if it's a non-recursive mutex it would likely be better to make the lock type non-copyable.
Related
I'm trying to create a vector of threads that perform a search. This is the important stuff from my SearchThread class.
class SearchThread {
explicit SearchThread(int t_id) {
id = t_id;
run = true;
thread = std::jthread([&]{
while(run){
{
std::unique_lock lock(mtx);
cnd_var.wait(lock, [&] { return !run; });
if(!run) { return; }
}
Search();
}
});
}
void Search();
int id;
bool run;
std::jthread thread;
std::condition_variable cnd_var;
std::mutex mtx;
}
And i'm attempting to construct them like this.
std::vector<SearchThread> search_threads;
for (int t_id = 0; t_id < num_threads; t_id++) {
search_threads.emplace_back(t_id);
}
The error i'm getting is no matching function for call to 'construct_at'. The issue i believe is with std::mutex and std::conditional_variable. I can have the mutex and conditional variable declared as pointers and then construct them on the heap in the class constructor, however that seems very ugly to me.
I guess my question is, why am i not allowed to do it like this, and is there a way around it?
std::mutex is not copyable. Its copy constructor is deleted. "Copying a mutex" makes no logical sense. If the mutex is locked, what does that mean? Is the copy-constructed mutex is also locked? If so, who locked it? Who gets to unlock it?
This makes the SearchThread class also uncopyable, and have a deleted copy constructor. Since one of its members is an uncopyable std::mutex, that pretty much puts a kibosh on copy-ability of a SearchThread, too.
std::vector requires its contents to be copyable. One of std::vector's proud achievements is that it will automatically copy the values in the vector, as needed, when it grows. This requires its contents to be copyable. SearchThread is not copyable. You cannot put it into a vector. It doesn't matter how you try to put it, emplace_back, or some other way. reserve() won't help you. A std::vector must contain a copyable class, there is no workaround for that.
Your options are:
Provide a user-defined copy constructor for your SearchThread, that implements whatever it means for a SearchThread to be copy-constructed from another instance of SearchThread. Perhaps its sufficient to copy all of its other members, and just have the copy-constructed SearchThread default-construct its mutex. You also have to provide a user-defined assignment operator as well. Or implement user-defined move constructor and assignment operator. The bottom line is: somehow make your SearchThread copyable/movable/assignable.
Use some other container that does not require a copyable object, like std::list perhaps.
Let's say I have a member variable vector initialised in the constructor, and this vector is read (not written to anywhere else) in several other member functions. Would I need to protect access to the vector (including in the constructor), or is it guaranteed that the object will be fully initialised and flushed to main memory before it's used in other threads?
Let me provide an example:
class A
{
public:
A();
void f1();
void f2();
private:
std::vector<int> v;
};
A::A()
{
// do some setup work in v
v.push_back(1);
}
// called from thread1
void A::f1()
{
// some readonly work on v
for (auto i : v) {
// do something on i
}
}
// called from thread2
void A::f2()
{
// more readonly work on v
if (v.empty()) {
// do other work
}
}
Do I need to lock-protect v in A::A(), A::f1() and A::f2()?
An object is created by a single thread, so you never have to worry about thread safety when running code inside the constructor that touches member variables. However, if you are using static variables within the constructor then you may need to add some form of locking around the access.
There is an edge case where code within a constructor can be called by multiple threads, and this is when you are using either placement new. For example, let's say you've got a buffer somewhere, and you're going to allocate an object into it:
byte buffer[100];
Foo *foo = new (buffer) Foo;
Here, unless you are locking around the call to new then it's possible for two or more constructors to run in parallel as they're running against the same block of memory. However, this is a real specialized edge-case and would require special handling (eg locking around the placement-new construction).
An object is constructed by a single thread.
Other threads can access the object only by means of the instance reference.
In other words, the object's constructor will have finished its work before other threads call a method.
You therefore don't need to implement thread-safe code within a constructor.
Of course, in case another object is passed to the constructor as a parameter, eventual access to that object within the constructor, should be thread-safe.
As stated in the other answers, there is no point in implementing synchronization primitives in the constructer, but that doesn't mean you can't have a race, if you don't synchronize externally:
std::atomic<A*> g_ptr = nullptr;
void threadFun1() {
g_ptr.store(new A{}, std::memory_order_relaxed);
}
void threadFun2() {
A* l_ptr = nullptr;
while (l_ptr == nullptr) {
l_ptr = g_ptr.load(std::memory_order_relaxed);
}
l_ptr->f1();
}
In above code, you have a data race between the constructor of A and f1. The problem is that - without synchonization - from the point of view of thread2, g_ptr might be written before the object is completely constructed.
However, there is nothing you can do inside the constructor to prevent this kind of race. Instead you have to use external means of synchronization, like using non-relaxed memory ordering for the atomic load and store operations or starting thread2 from within thread1 after the global variable is set.
Take this code example below:
model.h
namespace Stackoverflow {
class Model {
public:
Model();
~Model();
std::vector<int> *integers() const { return _integers.get(); }; // read only
private:
std::unique_ptr<std::vector<int>> _integers; // registered before constructor
};
}
model.cpp
Stackoverflow::Model::Model() {
_integers = std::make_unique<std::vector<int>>(); // initialized
}
Stackoverflow::Model::~Model() {
_integers.release();
}
The private member "_integers" will be registered but not initialized until the constructor is being called by the caller.
Stackoverflow::Model stackoverflow;
When another thread want to access this vector, call the getter.
auto *vector = stackoverflow.integers();
The member will be fully initialized when the caller is actually asking for the vector.
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.
I have a ConcurrentQueue class that is based around a user provided container with a constructor like this...
ConcurrentQueue(const ConcurrentQueue& other) : m_Queue(other.m_Queue) {}
But, I need to lock other's mutex while it's being copied.
Option 1:
So I could not use the copy constructor at all, and do...
ConcurrentQueue(const ConcurrentQueue& other) : m_Queue(other.m_Queue)
{
std::lock_guard<std::mutex> lock(other.m_Mutex);
m_Queue = other.m_Queue;
}
But I can't guarantee that copy assignment and copy construction are equivalent functionality.
Option 2:
I could have a private method...
std::queue<T, Container> GetQueue() const
{
std::lock_guard<std::mutex> lock(other.m_Mutex);
return m_Queue;
}
And then in the constructor do this...
ConcurrentQueue(const ConcurrentQueue& other) : m_Queue(other.GetQueue()) {}
But this potentially (depending on optimizations) uses m_Queue's copy constructor once and it's move constructor once. And I also can't guarantee that a copy and a move is equivalent to just a copy. Additionally the user provided container could be bizarre and be copyable but unmoveable, which would also cause this approach to have problems.
So, what do I do?
ConcurrrentQueue::ConcurrrentQueue(
ConcurrrentQueue const& other )
: m_Queue( (std::lock_guard<std::mutex>( other.m_Mutex ),
other.m_Queue ) )
{
}
should work.
Lock, create a copy of the content and then swap it with the member. At least that's the easiest and IMHO cleanest way. Another less clean way is to use the comma operator: (a, b) yields b, but if a is a scoped lock, the temporary will live until the next sequence point, i.e. until you have used b to initialize your local copy.
That said, there are two things to consider:
Maybe copying is not such a smart idea anyway and your design works as well if you just disable copying.
If you have access to the queue and you can read it for copying, doesn't that imply that the mutex must already be locked? If not, how are you sure that you really want to copy the queue? I don't question that there are answers that justify the design, but it's unusual.
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.