Re: [PATCH v3 2/4] rust: convert `Arc` to use `Refcount`

Tamir Duberstein
2025-02-21T17:28:26Z

On Fri, Feb 21, 2025 at 11:14 AM Gary Guo <gary@garyguo.net> wrote:
>
> On Wed, 19 Feb 2025 17:12:10 -0500
> Tamir Duberstein <tamird@gmail.com> wrote:
>
> >
> > Why did this signature need to change?
>
> I think I mentioned this in a earlier series. Smart pointers are not
> supposed to have methods (i.e. with a self receiver) as it may shadow
> deref'ed functions.

That probably deserves a separate commit, or at least a mention in the
commit message.

> > We could retain the unsynchronized operation here by taking a mutable
> > reference above and writing through it. Right? Could we remove `set`
> > from the abstraction in the previous patch?
>
> This was suggested as well in a previous series but I don't think it's
> a good idea. Creating a mutable reference and using unsynchronized
> write requires `unsafe`. `set` doesn't.
>
> Note that the `set` here is relaxed order. I doubt (if things are
> inlined properly) there'll be any codegen difference with a completely
> unsynchronized write.
>
> Not having an additional unsafe is a good trade-off to me.

Ack.

> > > @@ -412,16 +402,14 @@ fn clone(&self) -> Self {
> > >
> > > impl<T: ?Sized> Drop for Arc<T> {
> > > fn drop(&mut self) {
> > > - // SAFETY: By the type invariant, there is necessarily a reference to the object. We cannot
> > > - // touch `refcount` after it's decremented to a non-zero value because another thread/CPU
> > > - // may concurrently decrement it to zero and free it. It is ok to have a raw pointer to
> > > - // freed/invalid memory as long as it is never dereferenced.
> > > - let refcount = unsafe { self.ptr.as_ref() }.refcount.get();
> > > -
> > > // INVARIANT: If the refcount reaches zero, there are no other instances of `Arc`, and
> > > // this instance is being dropped, so the broken invariant is not observable.
> > > - // SAFETY: Also by the type invariant, we are allowed to decrement the refcount.
> > > - let is_zero = unsafe { bindings::refcount_dec_and_test(refcount) };
> > > + // SAFETY: By the type invariant, there is necessarily a reference to the object.
> > > + // NOTE: we cannot touch `refcount` after it's decremented to a non-zero value because
> > > + // another thread/CPU may concurrently decrement it to zero and free it. However it is okay
> > > + // to have a transient reference to decrement the refcount, see
> > > + // https://github.com/rust-lang/rust/issues/55005.
> > > + let is_zero = unsafe { self.ptr.as_ref().refcount.dec_and_test() };
> >
> > How come this careful handling is not required in into_unique_or_drop?
> > At least, the SAFETY comment there is much more mundane.
>
> Because `into_unique_or_drop` doesn't actually remove the allocation
> (it only decrements refcount for non-zero or turn it into `UniqueArc`).

I don't follow. This comment here talks about a race with another CPU
decrementing to zero; isn't the same race possible between any
combination of `into_unique_or_drop` and `drop` callers?