4

Is there a thread-safe reference counter class in the standard C++ library, (or as an extension in Visual Studio), or would I need to write this kind of object from scratch?

I'm hoping for an object that purely performs reference counting as shared_ptr might, with the exception that it does so across multiple threads accurately, and without managing anything. shared_ptr and it's cousin structures are nice because they define all the copy constructors and assignment operators you'd need, which ... are the most error prone part of C++ to me; C++ Constructors are to C++ what the Kick-off is to American Football.

struct Fun {

    // this member behaves in a way I appreciate, save for 2 short-comings:
    // - needless allocation event (minor)
    // - ref counting is only estimate if shared across threads (major)
    std::shared_ptr<int> smartPtr {new int};  

    // this is the hypothetical object that I'm searching for
    // + allocates only a control block for the ref count
    // + refCount.unique() respects reality when refs exist across many threads
    //   I can always count on this being the last reference
    std::object_of_desire refCount;

    // no explicit copy constructors or assignment operators necessary
    // + both members of this class provide this paperwork for me, 
    //   so I can careless toss this Fun object around and it'll move
    //   as one would expect, making only shallow copies/moves and ref counting
    Fun(); 

    ~Fun(){
        if(refCount.unique()){
             smart_assert("I swear refCount truly is unique, on pain of death");
        }
    }
}
Anne Quinn
  • 12,609
  • 8
  • 54
  • 101
  • 3
    I dont understand your problem. Why do you not want to use a shared_ptr? – gerum Aug 26 '21 at 09:33
  • 1
    Does this answer your question? [std::shared\_ptr thread safety explained](https://stackoverflow.com/questions/9127816/stdshared-ptr-thread-safety-explained) – Yves Aug 26 '21 at 09:34
  • 2
    what's the purpose of `refCount.unique()` being thread safe if you can't lock it to stay at that value? It could increase between you calling unique and calling `smart_assert` – PeterT Aug 26 '21 at 09:37
  • 4
    `shared_ptr` does thread-safe reference counting, and it can manage nothing if you set it up for managing nothing. So there is no need to reinvent the wheel. – n. m. could be an AI Aug 26 '21 at 09:37
  • @PeterT - How would that be possible? If unique() is true, shouldn't I be able to assume I hold the only reference in existence? Who else could make a copy if I hold the only one left? – Anne Quinn Aug 26 '21 at 09:40
  • 2
    @AnneQuinn What's stopping another thread from copying that last reference just after you called `unique`? – super Aug 26 '21 at 09:43
  • @AnneQuinn New instances can be created by a weak ptr. – gerum Aug 26 '21 at 09:47
  • @super - oh, I think I understand now, we're talking about two threads with access to the refCount object itself? So while only one exists, two parties are executing on it. (as for weak_ptr creating copies... that's uh... why can a weak pointer even do that lmao) – Anne Quinn Aug 26 '21 at 09:47
  • Someone can be holding a `shared_ptr<...> &`, and copy from that – Caleth Aug 26 '21 at 09:48

1 Answers1

2

The warnings about thread safety w.r.t. std::shared_ptr are

  • If you have multiple threads that can access the same pointer object, then you can have a data race if one of those threads modifies the pointer. If each thread has it's own instance, pointing to the same shared state, there are no data races on the shared state.
  • The final modification of the pointed-to object on a thread does not inter-thread happens before another thread observing a use_count of 1. If nothing is modifying the pointed-to object, there are no data races on the pointed-to object.

Here's your desired type

class ref_count {
public:
    bool unique() const { return ptr.use_count() == 1; }
private:
    struct empty {};
    std::shared_ptr<empty> ptr = std::make_shared<empty>();
};
Caleth
  • 52,200
  • 2
  • 44
  • 75
  • [`use_count`](https://en.cppreference.com/w/cpp/memory/shared_ptr/use_count) is not necessarily accurate – M.M Aug 26 '21 at 09:50
  • 2
    @M.M there is a total order on accesses to the shared state, and we don't expose the pointer or the shared object, so the caveats don't apply – Caleth Aug 26 '21 at 09:53
  • I agree that this is safe given the caveats mentioned on cppref. IMO, would be useful to elaborate on them in the answer briefly. – Martin Ba Aug 26 '21 at 09:54