Everybody who works with multithreaded environments knows you must synchronize between threads to avoid race cases. I'm particularly interested in synchronization that occurs within a shared_ptr deleter.
In my real situation, I have multiple classes interacting in a way that some of them are aware synchronization is going on, and others are not. For this example, I have artificially bundled them all onto one object to elucidate the question:
class TestObject
{
public:
TestObject()
: mMarked(false)
{ }
~TestObject()
{
// use of mMarked here indicates that the destructor must be synchronized
// with any thread that calls mark()
std::cout << "Object " << (mMarked ? "was marked." : "was not marked.");
}
void mark() { mMarked = true; }
void someBehaviorThatDoesntNeedSynchronization();
private:
bool mMarked;
};
thread 1:
std::shared_ptr<TestObject> objPtr1 = /* initialize to some instance */;
objPtr1->someBehaviorThatDoesntNeedSynchronization();
objPtr1.reset(); // may call the destructor
thread 2:
std::shared_ptr<TestObject> objPtr2 = /* initialize to the same instance */;
objPtr2->mark();
objPtr2.reset(); // may call the destructor
The spec seems to suggest that there is no synchronization at all. However, this seems very inconsiderate. It seems like thread 1 is expected to know all synchronization that happened to the object before it can have the privilege of calling a destructor (which could be brutal if the destructor is called during stack unwinding).
Am I missing something? I know every implementation of shared_ptr actually DOES do synchronization for this very reason, but I can't find anything in the spec to suggest I can trust it.
Is there anything in the spec to suggest that synchronization will occur before a deleter gets called?
From the reference:
All member functions (including copy constructor and copy assignment) can be called by multiple threads on different instances of shared_ptr without additional synchronization even if these instances are copies and share ownership of the same object. If multiple threads of execution access the same shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur, the shared_ptr overloads of atomic functions can be used to prevent the data race.