Elliott Goldstein
Elliott Goldstein

Reputation: 89

Thread Safety of Shared Pointers' Control Block

I am working on a small program that utilizes shared pointers. I have a simple class "Thing", that just is an class with an integer attribute:


class Thing{
public:
    Thing(int m){x=m;}
    int operator()(){
        return x;
    }
    void set_val(int v){
        x=v;
    }
    int x;
    ~Thing(){
        std::cout<<"Deleted thing with value "<<x<<std::endl;
    }

};

I have a simple function "fun", that takes in a shared_ptr instance, and an integer value, index, which just keeps track of which thread is outputting a given value. The function prints out the index value, passed to the function, and the reference count of the shared pointer that was passed in as an argument

std::mutex mtx1;
void fun(std::shared_ptr<Thing> t1,int index){
    std::lock_guard <std::mutex> loc(mtx1);
    int m=t1.use_count();
    std::cout<<index<<" : "<<m<<std::endl;
}

In main,I create one instance of a shared pointer which is a wrapper around a Thing object like so:

    std::shared_ptr<Thing> ptr5(nullptr);
    ptr5=std::make_shared<Thing>(110);

(declared this way for exception safety).

I then create 3 threads, each of which creates a thread executing the fun() function that takes in as arguments the ptr5 shared pointer and increasing index values:

    std::thread t1(fun,ptr5,1),t2(fun,ptr5,2),t3(fun,ptr5,3);
    t1.join();
    t2.join();
    t3.join();

My thought process here is that since each of the shared pointer control block member functions was thread safe, the call to use_count() within the fun() function is not an atomic instruction and therefore requires no locking. However, both running without and without a lock_guard,this still leads to a race condition. I expect to see an output of:

1:2 2:3 3:4

since each thread spawns a new reference to the original shared pointer, the use_count() will be incremented each time by 1 reference. However, my output is still random due to some race condition.

Upvotes: 0

Views: 370

Answers (1)

Fran&#231;ois Andrieux
Fran&#231;ois Andrieux

Reputation: 29022

In a multithreaded environment, use_count() is approximate. From cppreference :

In multithreaded environment, the value returned by use_count is approximate (typical implementations use a memory_order_relaxed load)

The control block for shared_ptr is otherwise thread-safe :

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.

Seeing an out-of-date use_count() is not an indication that the control-block was corrupted by a race condition.

Note that this does not extend to modifying the pointed object. It does not provide synchronization for the pointed object. Only the state of the shared_ptr and the control block are protected.

Upvotes: 3

Related Questions