Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is std::shared_ptr::unique() deprecated?

What is the technical problem with std::shared_ptr::unique() that is the reason for its deprecation in C++17?

According to cppreference.com, std::shared_ptr::unique() is deprecated in C++17 as

this function is deprecated as of C++17 because use_count is only an approximation in multi-threaded environment.

I understand this to be true for use_count() > 1: While I'm holding a reference to it, someone else might simultaneously let go of his or create a new copy.

But if use_count() returns 1 (which is what I'm interested in when calling unique()) then there is no other thread that could change that value in a racy way, so I would expect that this should be safe:

if (myPtr && myPtr.unique()) {     //Modify *myPtr } 

Results from my own search:

I found this document: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0521r0.html which proposes the deprecation in response to C++17 CD comment CA 14, but I couldn't find said comment itself.

As an alternative, that paper proposed adding some notes including the following:

Note: When multiple threads can affect the return value of use_count(), the result should be treated as approximate. In particular, use_count() == 1 does not imply that accesses through a previously destroyed shared_ptr have in any sense completed. — end note

I understand that this might be the case for the way use_count() is currently specified (due to the lack of guaranteed synchronization), but why was the resolution not just to specify such synchronization and hence make the above pattern safe? If there was a fundamental limitation that wouldn't allow such synchronization (or make it forbiddingly costly), then how is it possible to correctly implement the destructor?

Update:

I overlooked the obvious case presented by @alexeykuzmin0 and @rubenvb, because so far I only used unique() on instances of shared_ptr that were not accessible to other threads themselves. So there was no danger that that particular instance would get copied in a racy way.

I still would be interested to hear what exactly CA 14 was about, because I believe that all my use cases for unique() would work as long as it is guaranteed to synchronize with whatever happens to different shared_ptr instances on other threads. So it still seems like a useful tool to me, but I might overlook something fundamental here.

To illustrate what I have in mind, consider the following:

class MemoryCache { public:     MemoryCache(size_t size)         : _cache(size)     {         for (auto& ptr : _cache) {             ptr = std::make_shared<std::array<uint8_t, 256>>();         }     }      // the returned chunk of memory might be passed to a different thread(s),     // but the function is never accessed from two threads at the same time     std::shared_ptr<std::array<uint8_t,256>> getChunk()     {         auto it = std::find_if(_cache.begin(), _cache.end(), [](auto& ptr) { return ptr.unique(); });         if (it != _cache.end()) {             //memory is no longer used by previous user, so it can be given to someone else             return *it;         } else {             return{};         }     } private:     std::vector<std::shared_ptr<std::array<uint8_t, 256>>> _cache; }; 

Is there anything wrong with it (if unique() would actually synchronize with the destructors of other copies)?

like image 223
MikeMB Avatar asked Dec 14 '16 12:12

MikeMB


People also ask

Why would you choose shared_ptr instead of Unique_ptr?

In short: Use unique_ptr when you want a single pointer to an object that will be reclaimed when that single pointer is destroyed. Use shared_ptr when you want multiple pointers to the same resource.

Is shared_ptr thread safe?

std::shared_ptr is not thread safe. A shared pointer is a pair of two pointers, one to the object and one to a control block (holding the ref counter, links to weak pointers ...).

When should you use shared_ptr?

So, we should use shared_ptr when we want to assign one raw pointer to multiple owners. // referring to the same managed object. When to use shared_ptr? Use shared_ptr if you want to share ownership of a resource.

What happens when shared_ptr goes out of scope?

The smart pointer has an internal counter which is decreased each time that a std::shared_ptr , pointing to the same resource, goes out of scope – this technique is called reference counting. When the last shared pointer is destroyed, the counter goes to zero, and the memory is deallocated.


1 Answers

Consider the following code:

// global variable std::shared_ptr<int> s = std::make_shared<int>();  // thread 1 if (s && s.unique()) {     // modify *s }  // thread 2 auto s2 = s; 

Here we have a classic race condition: s2 may (or may not) be created as a copy of s in thread 2 while thread 1 is inside the if.

The unique() == true means that no one has a shared_ptr pointing to the same memory, but does not mean any other threads have no access to initial shared_ptr directly or through pointers or references.

like image 116
alexeykuzmin0 Avatar answered Oct 07 '22 21:10

alexeykuzmin0