1

I was reading about thread safety of std::shared_ptr and about the atomic operations overloads it provides and was wondering regarding a specific use case of it in a class.

From my understanding of the thread safety that is promised from shared_ptr, it is safe to have a get method like so:

class MyClass 
{
std::shared_ptr<int> _obj;
public:
    void start() 
    {
        std::lock_guard<std::mutex> lock(_mtx);
        _obj = std::make_shared<int>(1);
    }

    void stop()
    {
          std::lock_guard<std::mutex> lock(_mtx);
        _obj.reset();

    }
    std::shared_ptr<int> get_obj() const
    {
        return _obj; //Safe (?)
    }
};

The getter should be safe since the object will either be initializes or empty at any point from any thread.

But what if I want to throw an exception if the object is empty, I need to check it before returning it, do I have to put a lock there now (since stop() might be called between the if and the return)? Or is it possible to use the locking mechanism of the shared pointer and not use a lock in this method:

   std::shared_ptr<int> get_obj() const
    {
        auto tmp = _obj;
        if(!tmp) throw std::exception();
        return tmp;
    }
curiousguy
  • 8,038
  • 2
  • 40
  • 58
ZivS
  • 2,094
  • 2
  • 27
  • 48
  • 2
    `get_obj` races with `start` and `stop`. Something needs to additionally guarantee that the former doesn't get call concurrently with the latter. The second version has the same data race as the first - no better and no worse. – Igor Tandetnik Jan 13 '19 at 17:57
  • See also https://stackoverflow.com/questions/20705304/about-thread-safety-of-weak-ptr. Not an exact duplicate, but the answers are relevant to your question. – rsjaffe Apr 23 '20 at 17:53

1 Answers1

4

std::shared_ptr instances are not thread safe. Multiple instances all pointing to the same object can be modified from multiple threads but a single instance is not thread safe. See https://en.cppreference.com/w/cpp/memory/shared_ptr:

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.

You therefore either need to lock your mutex in your get_obj method or use std::atomic_load and std::atomic_store in your start and stop methods

Alan Birtles
  • 32,622
  • 4
  • 31
  • 60