Date: Mon, 11 Dec 2023 17:21:58 +0000
On Mon, 2023-12-11 at 14:54 +0000, Jonathan Wakely via Std-Proposals
wrote:
>
>
> 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.
I assume that's meant to be `sm2.use_count() == 1`. Isn't `use_count()`
a relaxed atomic load of the reference count? I don't think it is
suitable for this use case (or almost any use case). AFAIK it's the
same reason that `unique()` got deprecated.
> Since moving from sm is a non-const operation, other threads can't do
> anything with sm concurrently.
>
>
>
wrote:
>
>
> 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.
I assume that's meant to be `sm2.use_count() == 1`. Isn't `use_count()`
a relaxed atomic load of the reference count? I don't think it is
suitable for this use case (or almost any use case). AFAIK it's the
same reason that `unique()` got deprecated.
> Since moving from sm is a non-const operation, other threads can't do
> anything with sm concurrently.
>
>
>
Received on 2023-12-11 17:22:11