C++ Logo

std-proposals

Advanced search

Re: [std-proposals] std::shared_ptr resurrect

From: Jonathan Wakely <cxx_at_[hidden]>
Date: Mon, 11 Dec 2023 14:54:38 +0000
On Mon, 11 Dec 2023 at 14:43, Andrey Semashev via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> On 12/11/23 15:00, Valentin Palade via Std-Proposals wrote:
> > Hello Everyone,
> >
> > I would like your opinion on a small addition to the std::shared_ptr:
> >
> > bool std::shared_ptr::resurrect();
> >
> > with the following behavior:
> >
> > * will atomically decrement the use counter and
> > o if use counter != 0 will reset the shared_ptr instance and
> > return false
> > o otherwise, increment the use counter, leave the shared_ptr
> > instance unchanged and return true (thus resurrecting the
> > shared_ptr instance).
> >
> > Usage scenario:
> >
> > * producer creates a shared_ptr message to be sent to multiple
> > consumers - threads/actors/connections
> > * on consumers, we want to be able to return the shared_ptr message to
> > the producer (e.g. to be reused), once consumed by all.
>
> Why can't you use use_count()? If it returns 1, your shared_ptr is the
> last instance and you may reuse it, as if your proposed resurrect()
> returned true.
>
> Note that this is thread safe since use_count() == 1 means there are no
> other threads that may concurrently modify the reference counter.
>

That's not true. If the same shared_ptr object is visible in other threads,
they could make a copy of it and increase the refcount. That could happen
between calling use_count() and reusing it, a TOCTTOU race. Making the copy
and calling use_count() are both const members, so the standard says you're
allowed to do that concurrently.

The proposed resurrect() function "solves" this by being a non-const
member, so if another thread concurrently makes a copy of that object they
have a data race and so UB. So they're not allowed to make that copy.

But another way to do the same is:

  std::shared_ptr<T> sm2(std::move(sm));
  if (sm2.use_count())

Here we have a new local object which we know is not visible to any other
threads, so if its use_count() is 1 then this really is a unique reference.
Since moving from sm is a non-const operation, other threads can't do
anything with sm concurrently.

Received on 2023-12-11 14:54:52