1

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!

Community
  • 1
  • 1
Cort Ammon
  • 10,221
  • 31
  • 45
  • Please be aware that you don't need another thread to copy the `shared_ptr` you are going to release, but it is enough that another thread copies a `shared_ptr` or creates a `shared_ptr` from a `weak_ptr` that *shares ownership* with the `shared_ptr` you are going to release. As you say there are `weak_ptr`s around, so @Barry is right by pointing out that `use_count` may return 1, but after seeing the value, another thread might have used a `weak_ptr` to create another reference. – Michael Karcher Jan 24 '15 at 09:49

2 Answers2

4

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.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
0

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 :-( */           /**/
}                           /**/
Barry
  • 286,269
  • 29
  • 621
  • 977