From: Alexandre Courbot <acourbot@nvidia.com>
To: "Miguel Ojeda" <ojeda@kernel.org>,
"Alex Gaynor" <alex.gaynor@gmail.com>,
"Boqun Feng" <boqun.feng@gmail.com>,
"Gary Guo" <gary@garyguo.net>,
"Danilo Krummrich" <dakr@kernel.org>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <benno.lossin@proton.me>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>
Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org,
Alexandre Courbot <acourbot@nvidia.com>
Subject: [PATCH] rust/revocable: add try_with() convenience method
Date: Thu, 13 Mar 2025 21:40:59 +0900 [thread overview]
Message-ID: <20250313-try_with-v1-1-adcae7ed98a9@nvidia.com> (raw)
Revocable::try_access() returns a guard through which the wrapped object
can be accessed. Code that can sleep is not allowed while the guard is
held ; thus, it is common that the caller will explicitly need to drop
it before running sleepable code, e.g:
let b = bar.try_access()?;
let reg = b.readl(...);
// Don't forget this or things could go wrong!
drop(b);
something_that_might_sleep();
let b = bar.try_access()?;
let reg2 = b.readl(...);
This is arguably error-prone. try_with() and try_with_ok() provides an
arguably safer alternative, by taking a closure that is run while the
guard is held, and by dropping the guard automatically after the closure
completes. This way, code can be organized more clearly around the
critical sections and the risk is forgetting to release the guard when
needed is considerably reduced:
let reg = bar.try_with_ok(|b| b.readl(...))?;
something_that_might_sleep();
let reg2 = bar.try_with_ok(|b| b.readl(...))?;
Unlike try_access() which returns an Option, try_with() and
try_with_ok() return Err(ENXIO) if the object cannot be acquired. The
Option returned by try_access() is typically converted to an error in
practice, so this saves one step for the caller.
try_with() requires the callback itself to return a Result that is
passed to the caller. try_with_ok() accepts a callback that never fails.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
This is a feature I found useful to have while writing Nova driver code
that accessed registers alongside other operations. I would find myself
quite confused about whether the guard was held or dropped at a given
point of the code, and it felt like walking through a minefield ; this
pattern makes things safer and easier to read IMHO.
---
rust/kernel/revocable.rs | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/rust/kernel/revocable.rs b/rust/kernel/revocable.rs
index 1e5a9d25c21b279b01f90b02997492aa4880d84f..0157b20373b5b2892cb618b46958bfe095e428b6 100644
--- a/rust/kernel/revocable.rs
+++ b/rust/kernel/revocable.rs
@@ -105,6 +105,28 @@ pub fn try_access(&self) -> Option<RevocableGuard<'_, T>> {
}
}
+ /// Tries to access the wrapped object and run the closure `f` on it with the guard held.
+ ///
+ /// This is a convenience method to run short non-sleepable code blocks while ensuring the
+ /// guard is dropped afterwards. [`Self::try_access`] carries the risk that the caller
+ /// will forget to explicitly drop that returned guard before calling sleepable code ; this
+ /// method adds an extra safety to make sure it doesn't happen.
+ ///
+ /// Returns `Err(ENXIO)` if the wrapped object has been revoked, or the result of `f` after it
+ /// has been run.
+ pub fn try_with<R, F: Fn(&T) -> Result<R>>(&self, f: F) -> Result<R> {
+ self.try_access().ok_or(ENXIO).and_then(|t| f(&*t))
+ }
+
+ /// Tries to access the wrapped object and run the closure `f` on it with the guard held.
+ ///
+ /// This is the same as [`Self::try_with`], with the exception that `f` is expected to
+ /// always succeed and thus does not need to return a `Result`. Thus the only error case is if
+ /// the wrapped object has been revoked.
+ pub fn try_with_ok<R, F: Fn(&T) -> R>(&self, f: F) -> Result<R> {
+ self.try_with(|t| Ok(f(t)))
+ }
+
/// Tries to access the revocable wrapped object.
///
/// Returns `None` if the object has been revoked and is therefore no longer accessible.
---
base-commit: 4d872d51bc9d7b899c1f61534e3dbde72613f627
change-id: 20250313-try_with-cc9f91dd3b60
Best regards,
--
Alexandre Courbot <acourbot@nvidia.com>
next reply other threads:[~2025-03-13 12:41 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-13 12:40 Alexandre Courbot [this message]
2025-03-13 14:19 ` [PATCH] rust/revocable: add try_with() convenience method Benno Lossin
2025-03-13 15:08 ` Alexandre Courbot
2025-03-13 15:38 ` Benno Lossin
2025-03-13 15:48 ` Danilo Krummrich
2025-03-13 17:50 ` Benno Lossin
2025-03-15 14:07 ` Alexandre Courbot
2025-03-15 14:17 ` Danilo Krummrich
2025-03-15 14:26 ` Alexandre Courbot
2025-03-15 17:48 ` Benno Lossin
2025-03-16 12:20 ` Alexandre Courbot
2025-03-16 12:42 ` Danilo Krummrich
2025-03-13 14:37 ` Danilo Krummrich
2025-03-13 15:11 ` Alexandre Courbot
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=20250313-try_with-v1-1-adcae7ed98a9@nvidia.com \
--to=acourbot@nvidia.com \
--cc=a.hindborg@kernel.org \
--cc=alex.gaynor@gmail.com \
--cc=aliceryhl@google.com \
--cc=benno.lossin@proton.me \
--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=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=tmgross@umich.edu \
/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