Cort Ammon
Cort Ammon

Reputation: 10863

Checking for sole ownership of shared_ptr

I have often had the desire to check to see if a shared_ptr was the only owner of a shared object. It would be convenient for handing off behaviors before destroying the last shared_ptr, instead of having to do if after the destruction (my particular use case was dealing with preservation of weak pointers by rescuing them with another shared_ptr before destruction. After destruction has started, it's too late to rescue them).

C++11[draft] 20.7.2.1.4:

For purposes of determining the presence of a data race, member functions shall access and modify only the shared_ptr and weak_ptr objects themselves and not objects they refer to. Changes in use_count() do not reflect modifications that can introduce data races.

This question clarified a concern I had had about p.use_count() == 1 causing a data race. However, I am still not convinced it is safe to use in the way I want to use it.

In a single-threaded world, if use_count() returns 1, then you know you're the last owner of that object. In a multithreading world where you've avoided data races, it seems reasonable that use_count() of 1 is sufficient to ensure you are the sole owner, but I'm having a frustrating time trying to get that from the spec-eese. I can't tell if there's some loophole that would permit a use_count of 1 even though another shared_ptr exists on another thread. On the other hand, it seems frustrating that the definition of use_count might turn to goo just beacuse I handed a shared_ptr to another thread.

Can I ever get into a situation where use_count() is 1, but I am not the only owner, by the rules of the spec? I recognize that, thanks to races, a use_count of 2 does not explicitly mean I am sharing (the other thread might release my object after my call to use_count on this thread), but I'm interested in the other direction, once I have seen use_count of 1.

As a second related question: do the same rules apply for unique, which seems to be custom tailored to my desired implementation, but does not have any extra statements made regarding thread safety?


Edit: In response to answers I've gotten, the situation I am interested in has the shared_ptr we are calling unique_count on is only accessible by a single thread, so I do not have to worry about any other thread successfully copying it... they have to find their own shared_ptr to copy!

Upvotes: 1

Views: 131

Answers (2)

Barry
Barry

Reputation: 303017

Absolutely. Even if it were specified that use_count() and unique() were both atomic, and it doesn't:

long use_count() const noexcept;
Returns: the number of shared_ptr objects, *this included, that share ownership with *this, or 0 when *this is empty.
[ Note: use_count() is not necessarily efficient.—end note ]

bool unique() const noexcept;
Returns: use_count() == 1.
[ Note: unique() may be faster than use_count(). If you are using unique() to implement copy on write, do not rely on a specific value when get() == nullptr. —end note ]

There's nothing to stop something like this happening:

std::shared_ptr<Foo> sp;

// thread 1                      // thread 2    
bool uniq = sp.unique();    /**/
                            /**/   std::shared_ptr<Foo> cpy = sp;
if (uniq) {                 /**/
   /* mine?? */             /**/   cpy->foo();
   /* nope :-( */           /**/
}                           /**/

Upvotes: 0

Howard Hinnant
Howard Hinnant

Reputation: 218750

If there is a possibility that a weak_ptr exists that points to the same control block as your single shared_ptr, and if another thread might convert that weak_ptr to a shared_ptr, then your thread could observe a use_count() == 1, but by the time it can do anything with that information, the other thread may construct a new shared_ptr from the weak_ptr, bumping the use_count up to 2.

Even if there are no weak_ptrs, if more than one thread has read access to your shared_ptr (e.g. say its a global), then another thread might make a copy of it after you observe use_count() == 1.

If your shared_ptr is not accessible to any other thread, and there is no possibility that another thread might convert a weak_ptr to a shared_ptr, then use_count() == 1 is safe to depend on.

Upvotes: 4

Related Questions