Search code examples
c++multithreadingsynchronizationshared-ptratomic

Does shared_ptr's deleter do any synchronization?


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?


Solution

  • 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.