rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Alice Ryhl <aliceryhl@google.com>
To: Lyude Paul <lyude@redhat.com>
Cc: "Paolo Bonzini" <pbonzini@redhat.com>,
	linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
	"Benno Lossin" <lossin@kernel.org>,
	"Peter Zijlstra" <peterz@infradead.org>,
	"Ingo Molnar" <mingo@redhat.com>, "Will Deacon" <will@kernel.org>,
	"Boqun Feng" <boqun.feng@gmail.com>,
	"Waiman Long" <longman@redhat.com>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"Alex Gaynor" <alex.gaynor@gmail.com>,
	"Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Trevor Gross" <tmgross@umich.edu>,
	"Danilo Krummrich" <dakr@kernel.org>
Subject: Re: [PATCH v4] rust: lock: Export Guard::do_unlocked()
Date: Fri, 31 Oct 2025 10:31:55 +0100	[thread overview]
Message-ID: <CAH5fLgiWceOs-VtDnFkx5EBxCbAnJ3cLkRwp9adQC7x9oJCDFQ@mail.gmail.com> (raw)
In-Reply-To: <e0112480a6786c64fa65888b5ce8befbba72a230.camel@redhat.com>

On Thu, Oct 30, 2025 at 6:41 PM Lyude Paul <lyude@redhat.com> wrote:
>
> On Thu, 2025-10-30 at 11:43 +0100, Paolo Bonzini wrote:
> > On 10/29/25 19:35, Lyude Paul wrote:
> > > +    /// // Since we hold work.lock, which work will also try to acquire in WorkItem::run. Dropping
> > > +    /// // the lock temporarily while we wait for completion works around this.
> > > +    /// g.do_unlocked(|| work.done.wait_for_completion());
> > > +    ///
> > > +    /// assert_eq!(*g, 42);
> > > +    /// ```
> > > +    pub fn do_unlocked<U>(&mut self, cb: impl FnOnce() -> U) -> U {
> > >           // SAFETY: The caller owns the lock, so it is safe to unlock it.
> > >           unsafe { B::unlock(self.lock.state.get(), &self.state) };
> >
> > Getting self as &mut is incorrect.  That's because owning a lock guard
> > implicitly tells you that no other thread can observe the intermediate
> > states of the object.  (The same is even more obviously true for a
> > RefCell's mutable borrow, i.e. core::cell::RefMut)
> >
> > Let's say you have a lock-protected data structure with an invariant
> > that is preserved at the end of every critical section.  Let's say also
> > that you have a function
> >
> >      fn do_something() {
> >          let g = self.inner.lock();
> >          g.mess_up_the_invariant();          // (1)
> >          self.do_something_else(&mut g);     // uses do_unlocked()
> >          g.fix_the_invariant();              // (2)
> >      }
> >
> > Because the function holds a guard between the calls (1) and (2), it
> > expects that other thread cannot observe the temporary state.  The fact
> > that do_unlocked() takes a &mut doesn't help, because the common case
> > for RAII objects is that they're passed around mutably.
> >
> > Instead, do_unlocked should take the guard and return another one:
> >
> >      fn do_something() {
> >          let mut g = self.inner.lock();
> >          g.mess_up_the_invariant();          // (1)
> >          g = self.do_something_else(g);      // uses do_unlocked()
> >          g.fix_the_invariant();              // (2)
> >      }
> >
> > This version of the interface makes it clear that (1) and (2) are in a
> > separate critical section.  Unfortunately it makes the signature uglier
> > for do_unlocked() itself:
> >
> >      #[must_use]
> >      pub fn do_unlocked<U>(self, cb: impl FnOnce() -> U) -> (Self, U)
>
> Hm, it seems then that we should probably fix this before exporting it then!
> Thank you for pointing this out, I'll fix it in the next respin.

I do agree that this behavior has a lot of potential to surprise
users, but I don't think it's incorrect per se. It was done
intentionally for Condvar, and it's not unsound. Just surprising.

Of course, that doesn't mean we can't change it. Condvar could be
updated to use ownership in that way, and doing say may be a good
idea.

Alice

  reply	other threads:[~2025-10-31  9:32 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-10-29 18:35 [PATCH v4] rust: lock: Export Guard::do_unlocked() Lyude Paul
2025-10-30 10:43 ` Paolo Bonzini
2025-10-30 17:41   ` Lyude Paul
2025-10-31  9:31     ` Alice Ryhl [this message]
2025-10-31  9:38       ` Paolo Bonzini
2025-10-31 10:24         ` Alice Ryhl
2025-11-05 20:41           ` Lyude Paul

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAH5fLgiWceOs-VtDnFkx5EBxCbAnJ3cLkRwp9adQC7x9oJCDFQ@mail.gmail.com \
    --to=aliceryhl@google.com \
    --cc=a.hindborg@kernel.org \
    --cc=alex.gaynor@gmail.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=dakr@kernel.org \
    --cc=gary@garyguo.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=longman@redhat.com \
    --cc=lossin@kernel.org \
    --cc=lyude@redhat.com \
    --cc=mingo@redhat.com \
    --cc=ojeda@kernel.org \
    --cc=pbonzini@redhat.com \
    --cc=peterz@infradead.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=tmgross@umich.edu \
    --cc=will@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).