Reputation: 3388
For classes with mutex as members, when I create the copy constructor, I need to decide which mutex to lock. For the following code, I am wondering why I only need to lock rhs.mu_ but don't have to lock this->mu_? Could it be possible that the copy constructor being called by multiple threads for the same object?
class Obj {
public:
std::mutex mu_;
std::string data_;
// copy ctor
Obj(const Obj& rhs) {
std::unique_lock<std::mutex> lk(rhs.mu_); // why only lock rhs.mu_?
data_ = rhs.data_;
}
}
Updates: Is this piece of code calling the copy ctor at the same time using new?
Obj* t = nullptr;
Obj someObj;
// ... populate someObj
std::thread t1([&]() { t = new Obj(someObj); });
std::thread t2([&]() { t = new Obj(someObj); });
Upvotes: 0
Views: 722
Reputation: 126877
You'll need to lock the mutex of the copied-from object (to make sure you're copying an object in a consistent state), while you can leave alone the lock of the object being constructed.
This is true in general for constructors: a constructor call is single-thread by definition:
static
locals, static
class fields) are guaranteed to be initialized once by the standard (the compiler injects something similar to call_once
);new
are, again, safe, as each thread running an expression like new A
will allocate a different instance over which the constructor will run.The only problem you can have is if some client of yours plays with placement new
, but in that case I'd argue that it's the caller's responsibility not to call placement new
constructor concurrently over the same object - as:
new
twice over the same memory location (without a destructor call in between) is a contract violation on your caller's part, so serializing through a mutex in your constructor would not solve anything;So, long story short, don't worry about locking the object being constructed in construction. The language guarantees you that object construction is nonconcurrent.
Upvotes: 3
Reputation: 16424
If your variable is local, it's unaccessible to other threads during construction, because other thread can't name a local variable in this thread.
If your variable has static lifetime, thread safety is guaranteed by C++ standard:
Dynamic initialization of a block-scope variable with static storage duration or thread storage duration is performed the first time control passes through its declaration;
...
If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.
Thus the constructor won't be called twice.
Upvotes: 4