rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jens Korinth via B4 Relay <devnull+jens.korinth.tuta.io@kernel.org>
To: "Miguel Ojeda" <ojeda@kernel.org>,
	"Alex Gaynor" <alex.gaynor@gmail.com>,
	"Boqun Feng" <boqun.feng@gmail.com>,
	"Gary Guo" <gary@garyguo.net>,
	"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,
	 FUJITA Tomonori <fujita.tomonori@gmail.com>,
	 Dirk Behme <dirk.behme@gmail.com>,
	linux-kernel@vger.kernel.org,
	 Jens Korinth <jens.korinth@tuta.io>
Subject: [PATCH v4 1/3] rust: Add `OnceLite` for executing code once
Date: Tue, 26 Nov 2024 17:40:57 +0100	[thread overview]
Message-ID: <20241126-pr_once_macros-v4-1-410b8ca9643e@tuta.io> (raw)
In-Reply-To: <20241126-pr_once_macros-v4-0-410b8ca9643e@tuta.io>

From: Jens Korinth <jens.korinth@tuta.io>

Similar to `Once` in Rust's standard library, but with the same
non-blocking behavior as the kernel's `DO_ONCE_LITE` macro. Abstraction
allows easy replacement of the underlying sync mechanisms, see

https://lore.kernel.org/rust-for-linux/20241109-pr_once_macros-v3-0-6beb24e0cac8@tuta.io/.

Suggested-by: Boqun Feng <boqun.feng@gmail.com>
Signed-off-by: Jens Korinth <jens.korinth@tuta.io>
---
 rust/kernel/lib.rs       |   1 +
 rust/kernel/once_lite.rs | 127 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 128 insertions(+)

diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index bf8d7f841f9425d19a24f3910929839cfe705c7f..2b0a80435d24f5e168679ec2e25bd68cd970dcdd 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -44,6 +44,7 @@
 pub mod list;
 #[cfg(CONFIG_NET)]
 pub mod net;
+pub mod once_lite;
 pub mod page;
 pub mod prelude;
 pub mod print;
diff --git a/rust/kernel/once_lite.rs b/rust/kernel/once_lite.rs
new file mode 100644
index 0000000000000000000000000000000000000000..723c3244fc856fe974ddd33de5415e7ced37f315
--- /dev/null
+++ b/rust/kernel/once_lite.rs
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! A one-time only global execution primitive.
+//!
+//! This primitive is meant to be used to execute code only once. It is
+//! similar in design to Rust's
+//! [`std::sync:Once`](https://doc.rust-lang.org/std/sync/struct.Once.html),
+//! but borrowing the non-blocking mechanism used in the kernel's
+//! [`DO_ONCE_LITE`] macro.
+//!
+//! An example use case would be to print a message only the first time.
+//!
+//! [`DO_ONCE_LITE`]: srctree/include/linux/once_lite.h
+//!
+//! C header: [`include/linux/once_lite.h`](srctree/include/linux/once_lite.h)
+//!
+//! Reference: <https://doc.rust-lang.org/std/sync/struct.Once.html>
+
+use core::sync::atomic::{AtomicBool, Ordering::Relaxed};
+
+/// A low-level synchronization primitive for one-time global execution.
+///
+/// Based on the
+/// [`std::sync:Once`](https://doc.rust-lang.org/std/sync/struct.Once.html)
+/// interface, but internally equivalent the kernel's [`DO_ONCE_LITE`]
+/// macro. The Rust macro `do_once_lite` replacing it uses `OnceLite`
+/// internally.
+///
+/// [`DO_ONCE_LITE`]: srctree/include/linux/once_lite.h
+///
+/// # Examples
+///
+/// ```rust
+/// static START: kernel::once_lite::OnceLite =
+///     kernel::once_lite::OnceLite::new();
+///
+/// let mut x: i32 = 0;
+///
+/// START.call_once(|| {
+///   // run initialization here
+///   x = 42;
+/// });
+/// while !START.is_completed() { /* busy wait */ }
+/// assert_eq!(x, 42);
+/// ```
+///
+pub struct OnceLite(AtomicBool, AtomicBool);
+
+impl OnceLite {
+    /// Creates a new `OnceLite` value.
+    #[inline(always)]
+    pub const fn new() -> Self {
+        Self(AtomicBool::new(false), AtomicBool::new(false))
+    }
+
+    /// Performs an initialization routine once and only once. The given
+    /// closure will be executed if this is the first time `call_once` has
+    /// been called, and otherwise the routine will not be invoked.
+    ///
+    /// This method will _not_ block the calling thread if another
+    /// initialization is currently running. It is _not_ guaranteed that the
+    /// initialization routine will have completed by the time the calling
+    /// thread continues execution.
+    ///
+    /// Note that this is different from the guarantees made by
+    /// [`std::sync::Once`], but identical to the way the implementation of
+    /// the kernel's [`DO_ONCE_LITE_IF`] macro is behaving at the time of
+    /// writing.
+    ///
+    /// [`std::sync::Once`]: https://doc.rust-lang.org/std/sync/struct.Once.html
+    /// [`DO_ONCE_LITE_IF`]: srctree/include/once_lite.h
+    #[inline(always)]
+    pub fn call_once<F: FnOnce()>(&self, f: F) {
+        if !self.0.load(Relaxed) && !self.0.swap(true, Relaxed) {
+            f()
+        };
+        self.1.store(true, Relaxed);
+    }
+
+    /// Returns `true` if some `call_once` call has completed successfully.
+    /// Specifically, `is_completed` will return `false` in the following
+    /// situations:
+    ///
+    /// 1. `call_once()` was not called at all,
+    /// 2. `call_once()` was called, but has not yet completed.
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// static INIT: kernel::once_lite::OnceLite =
+    ///     kernel::once_lite::OnceLite::new();
+    ///
+    /// assert_eq!(INIT.is_completed(), false);
+    /// INIT.call_once(|| {
+    ///     assert_eq!(INIT.is_completed(), false);
+    /// });
+    /// assert_eq!(INIT.is_completed(), true);
+    /// ```
+    #[inline(always)]
+    pub fn is_completed(&self) -> bool {
+        self.1.load(Relaxed)
+    }
+}
+
+/// Executes code only once.
+///
+/// Equivalent to the kernel's [`DO_ONCE_LITE`] macro: Expression is
+/// evaluated at most once by the first thread, other threads will not be
+/// blocked while executing in first thread, nor are there any guarantees
+/// regarding the visibility of side-effects of the called expression.
+///
+/// [`DO_ONCE_LITE`]: srctree/include/linux/once_lite.h
+///
+/// # Examples
+///
+/// ```
+/// let mut x: i32 = 0;
+/// kernel::do_once_lite!((||{x = 42;})());
+/// ```
+#[macro_export]
+macro_rules! do_once_lite {
+    ($e: expr) => {{
+        #[link_section = ".data.once"]
+        static ONCE: $crate::once_lite::OnceLite = $crate::once_lite::OnceLite::new();
+        ONCE.call_once(|| $e);
+    }};
+}

-- 
2.47.0



  reply	other threads:[~2024-11-26 16:41 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <8gSHb0iuAT_Wz0rR1-qsnFIVndSpW229fA0lq-fslngTt5k1ooiMw5eAIc82F26Mdma4nkyU4X7_1RLZcnQf-Q==@protonmail.internalid>
2024-11-26 16:40 ` [PATCH v4 0/3] rust: Add pr_*_once macros Jens Korinth via B4 Relay
2024-11-26 16:40   ` Jens Korinth via B4 Relay [this message]
2024-11-26 18:57     ` [PATCH v4 1/3] rust: Add `OnceLite` for executing code once Daniel Sedlak
2024-11-27 19:46       ` jens.korinth
2024-11-29  8:22         ` Daniel Sedlak
2024-11-26 19:00     ` Miguel Ojeda
2024-11-27 12:26     ` Alice Ryhl
2024-11-27 20:12       ` jens.korinth
2024-11-27 20:18         ` Alice Ryhl
2024-12-05  6:49           ` jens.korinth
2024-12-05  8:47             ` Alice Ryhl
2025-02-04 11:11     ` Andreas Hindborg
2024-11-26 16:40   ` [PATCH v4 2/3] rust: print: Add pr_*_once macros Jens Korinth via B4 Relay
2024-11-26 16:40   ` [PATCH v4 3/3] rust: error: Replace pr_warn by pr_warn_once Jens Korinth via B4 Relay
2024-11-26 17:07     ` Miguel Ojeda
2024-11-27 20:39       ` jens.korinth
2024-11-30 18:18         ` Miguel Ojeda
2024-12-05  6:30           ` jens.korinth
2024-12-05 11:55             ` Miguel Ojeda
2024-12-05 19:22               ` jens.korinth
2024-11-26 16:59   ` [PATCH v4 0/3] rust: Add pr_*_once macros Miguel Ojeda
2025-02-11 15:04   ` Andreas Hindborg
2025-02-11 15:29     ` jens.korinth
2025-02-11 15:42       ` Andreas Hindborg
2025-07-17 16:07         ` [PATCH v4 0/3] rust: Add pr_*_once macros134 Onur Özkan
2025-07-19  3:33           ` Boqun Feng
2025-07-19 16:33             ` Onur

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=20241126-pr_once_macros-v4-1-410b8ca9643e@tuta.io \
    --to=devnull+jens.korinth.tuta.io@kernel.org \
    --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=dirk.behme@gmail.com \
    --cc=fujita.tomonori@gmail.com \
    --cc=gary@garyguo.net \
    --cc=jens.korinth@tuta.io \
    --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;
as well as URLs for NNTP newsgroup(s).