[PATCH v4 1/3] rust: Add `OnceLite` for executing code once

Jens Korinth via B4 Relay posted 3 patches 1 year, 2 months ago
[PATCH v4 1/3] rust: Add `OnceLite` for executing code once
Posted by Jens Korinth via B4 Relay 1 year, 2 months ago
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
Re: [PATCH v4 1/3] rust: Add `OnceLite` for executing code once
Posted by Andreas Hindborg 1 year ago
"Jens Korinth via B4 Relay" <devnull+jens.korinth.tuta.io@kernel.org> writes:

> 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>

Thanks for the patch!

I was using the series for `pr_warn_once!` elsewhere, and clippy gave me
this suggestion:

  CLIPPY L rust/kernel.o
error: you should consider adding a `Default` implementation for `OnceLite`
  --> /home/aeh/src/linux-rust/module-params/rust/kernel/once_lite.rs:52:5
   |
52 | /     pub const fn new() -> Self {
53 | |         Self(AtomicBool::new(false), AtomicBool::new(false))
54 | |     }
   | |_____^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
   = note: `-D clippy::new-without-default` implied by `-D warnings`
   = help: to override `-D warnings` add `#[allow(clippy::new_without_default)]`
help: try adding this
   |
49 + impl Default for OnceLite {
50 +     fn default() -> Self {
51 +         Self::new()
52 +     }
53 + }
   |



Best regards,
Andreas Hindborg
Re: [PATCH v4 1/3] rust: Add `OnceLite` for executing code once
Posted by Alice Ryhl 1 year, 2 months ago
On Tue, Nov 26, 2024 at 5:41 PM Jens Korinth via B4 Relay
<devnull+jens.korinth.tuta.io@kernel.org> wrote:
>
> 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>

> +    pub fn is_completed(&self) -> bool {
> +        self.1.load(Relaxed)
> +    }

What is the use-case for this function? Why not just have one atomic?
Re: [PATCH v4 1/3] rust: Add `OnceLite` for executing code once
Posted by jens.korinth@tuta.io 1 year, 2 months ago
> What is the use-case for this function?

`DO_ONCE_LITE` has very few uses in C; frankly, the only other use I could think
of was initialization. But since `OnceLite` cannot block or guarantee visibility
of the side-effects of the `call_once` expression in other threads, it can't be 
used for this case, either. _Unless_ there is some mechanism to wait
voluntarily when this is required, hence `is_completed`. (And it also exists in
`std::sync::Once`.)

`DO_ONCE_LITE_IF` has more uses in C, but this is a bit harder to do well with
`OnceLite`: A `do_once_lite_if` Rust macro can't short-circuit the condition to
avoid the evaluation if the atomic load already shows that it has been done / is
being done rn. Maybe a
`pub fn call_once<C: FnOnce() -> bool, F: FnOnce()>(cond: C, f: F)` could be
used to simulate the effect. Thoughts?

> Why not just have one atomic?

Do you also have an `AtomicU32` state var in mind, as Daniel suggested?

Jens
Re: [PATCH v4 1/3] rust: Add `OnceLite` for executing code once
Posted by Alice Ryhl 1 year, 2 months ago
On Wed, Nov 27, 2024 at 9:12 PM <jens.korinth@tuta.io> wrote:
>
> > What is the use-case for this function?
>
> `DO_ONCE_LITE` has very few uses in C; frankly, the only other use I could think
> of was initialization. But since `OnceLite` cannot block or guarantee visibility
> of the side-effects of the `call_once` expression in other threads, it can't be
> used for this case, either. _Unless_ there is some mechanism to wait
> voluntarily when this is required, hence `is_completed`. (And it also exists in
> `std::sync::Once`.)
>
> `DO_ONCE_LITE_IF` has more uses in C, but this is a bit harder to do well with
> `OnceLite`: A `do_once_lite_if` Rust macro can't short-circuit the condition to
> avoid the evaluation if the atomic load already shows that it has been done / is
> being done rn. Maybe a
> `pub fn call_once<C: FnOnce() -> bool, F: FnOnce()>(cond: C, f: F)` could be
> used to simulate the effect. Thoughts?
>
> > Why not just have one atomic?
>
> Do you also have an `AtomicU32` state var in mind, as Daniel suggested?

What I had in mind was to use a single AtomicBool and get rid of the
`is_completed` logic entirely. The purpose is to use this for printing
once, and printing doesn't need `is_completed`. We can have another
helper for other purposes.

Alice
Re: [PATCH v4 1/3] rust: Add `OnceLite` for executing code once
Posted by jens.korinth@tuta.io 1 year, 2 months ago
I'm afraid you lost me. You wrote:

> Using a Once type for this seems like a good idea to me.

and

> One advantage of using a Once type is that we can use core::sync::atomic
> until we have working LKMM atomics and then we just swap out the Once
> type without having to modify the warn_once abstractions.

That made sense to me, so I started in this direction. `std::sync::Once`
has `is_completed` [1], which made particular sense to implement in my
mind to increase the utility of `OnceLite`.

[1]: https://doc.rust-lang.org/std/sync/struct.Once.html#method.is_completed

> The purpose is to use this for printing once, and printing doesn't need
> `is_completed`. We can have another helper for other purposes.

Why have `OnceLite` then at all, instead of the hidden Rust macro that was
proposed initially? 

Jens
Re: [PATCH v4 1/3] rust: Add `OnceLite` for executing code once
Posted by Alice Ryhl 1 year, 2 months ago
On Thu, Dec 5, 2024 at 7:49 AM <jens.korinth@tuta.io> wrote:
>
> I'm afraid you lost me. You wrote:
>
> > Using a Once type for this seems like a good idea to me.
>
> and
>
> > One advantage of using a Once type is that we can use core::sync::atomic
> > until we have working LKMM atomics and then we just swap out the Once
> > type without having to modify the warn_once abstractions.
>
> That made sense to me, so I started in this direction. `std::sync::Once`
> has `is_completed` [1], which made particular sense to implement in my
> mind to increase the utility of `OnceLite`.
>
> [1]: https://doc.rust-lang.org/std/sync/struct.Once.html#method.is_completed

The stdlib Once type guarantees that when call_once has returned, then
the closure has finished running *even* if you are not the caller who
is running the closure. It achieves this by having other callers go to
sleep until the main caller completes. If we actually want to mirror
Once, then we should have that logic too, and I really don't think we
want such complicated logic in our pr_warn_once macro.

> > The purpose is to use this for printing once, and printing doesn't need
> > `is_completed`. We can have another helper for other purposes.
>
> Why have `OnceLite` then at all, instead of the hidden Rust macro that was
> proposed initially?

Well, one advantage is that when Boqun manages to add support for LKMM
atomics, we can switch them out without having to modify macro code.
Moving logic out of macro code is usually a good idea. It's also
possible that there are other user-cases of an OnceLite that doesn't
have is_completed.

Alice
Re: [PATCH v4 1/3] rust: Add `OnceLite` for executing code once
Posted by Miguel Ojeda 1 year, 2 months ago
On Tue, Nov 26, 2024 at 5:41 PM Jens Korinth via B4 Relay
<devnull+jens.korinth.tuta.io@kernel.org> wrote:
>
> 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/.

Nit: you could use a Link tag for this, e.g.

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

And then you can refer to it using [1], like "see [1].".

> +//! 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),

In one case you used a shortcut reference link for this -- perhaps you
could do that here and below.

> +//! but borrowing the non-blocking mechanism used in the kernel's
> +//! [`DO_ONCE_LITE`] macro.

I would suggest mentioning C here, e.g. C macro, to reduce ambiguity
(it is true we have just used "kernel's" in some cases).

> +//! An example use case would be to print a message only the first time.

Ideally we would mention one or two concrete use cases here, e.g. you
could grep to see a couple common C use cases.

Also, since we are going to have the `pr_..._once` macros, it may not
be the best example use case, since the developer would use those
instead, right?

The docs look well-formatted etc., so thanks for taking the time writing them :)

> +/// A low-level synchronization primitive for one-time global execution.

I wonder if we should move part of the docs from the module level here
to avoid duplication.

> +/// macro. The Rust macro `do_once_lite` replacing it uses `OnceLite`

Please link these with intra-doc links.

> +/// ```rust

You can remove `rust` from this one, like in the others.

> +/// static START: kernel::once_lite::OnceLite =
> +///     kernel::once_lite::OnceLite::new();

I would have a hidden `use` line to simplify the example -- since we
are reading the example about this item, it is OK to shorten it here,
e.g.

    static START: OnceLite = OnceLite::new();

> +///   // run initialization here

Please use the usual comment style: "Run initialization here.".

> +/// while !START.is_completed() { /* busy wait */ }

Hmm... without threads this looks a bit strange.

> +    /// Creates a new `OnceLite` value.

Please use intra-doc links wherever possible.

> +    /// This method will _not_ block the calling thread if another

Should we save "does _not_ ... regardless ..."? i.e. it never blocks.

> +    /// [`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.

"at the time of writing" is implicit, so we don't need to say it.

(Of course, it would be nice to have someone making sure we don't get
out of sync!

> +/// Executes code only once.

"an expression" or "a Rust expression" could perhaps be more precise
and hints what the argument is (which may help those not accustomed to
macro signatures).

> +/// kernel::do_once_lite!((||{x = 42;})());

Please format the examples if possible. Not a big deal, but it is always nicer.

Can we add an `assert` here too like in the other examples, so that
this doubles as a test?

By the way, does this need to be an immediately called closure? i.e.
the macro takes an expression, can't we do e.g.

    do_once_lite!(x = 42);

?

> +        #[link_section = ".data.once"]
> +        static ONCE: $crate::once_lite::OnceLite = $crate::once_lite::OnceLite::new();

I let Boqun et al. review the sync parts, but a few questions: Do we
want/need two `AtomicBool`s? Should the docs for `OnceLite`
mention/suggest `link_section`? Should we have a macro to declare them
instead?

By the way, please test with `CLIPPY=1`, I got `new_without_default`.

Cheers,
Miguel
Re: [PATCH v4 1/3] rust: Add `OnceLite` for executing code once
Posted by Daniel Sedlak 1 year, 2 months ago
On 11/26/24 5:40 PM, Jens Korinth via B4 Relay wrote:
> 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
The `rust` part should be default value for rustdoc tests, can we please 
omit that?
> +/// 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);
Have you considered it to be implemented like `AtomicU32`? I think™ that 
one atomic variable is more than enough.
> +
> +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);
I think the memory ordering is wrong here. Since it is `Relaxed`, the 
`self.1` and `self.0` can get reordered during execution. So `self.0` 
can be false, even though `self.1` will be true. Not on x86, but on 
architectures with weaker memory models it can happen.

Even the implementation in the Rust std, uses at least 
`Acquire`/`Release`, where the reordering shouldn't be possible see [1].

[1]: 
https://github.com/rust-lang/rust/blob/master/library/std/src/sys/sync/once/futex.rs
> +    }
> +
> +    /// 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
Also here the `rust` part should be the default value.
> +    /// 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);
> +    }};
> +}
> 

Thanks for working on that!

Daniel

Re: [PATCH v4 1/3] rust: Add `OnceLite` for executing code once
Posted by jens.korinth@tuta.io 1 year, 2 months ago
> Have you considered it to be implemented like `AtomicU32`? I think™ that
> one atomic variable is more than enough.

Just to clarify - you mean something like this? Nevermind the magic numbers,
I'd replace them, of course.

diff --git a/rust/kernel/once_lite.rs b/rust/kernel/once_lite.rs
index 723c3244fc85..0622ecbfced5 100644
--- a/rust/kernel/once_lite.rs
+++ b/rust/kernel/once_lite.rs
@@ -16,7 +16,7 @@
//!
//! Reference: <https://doc.rust-lang.org/std/sync/struct.Once.html>
 
-use core::sync::atomic::{AtomicBool, Ordering::Relaxed};
+use core::sync::atomic::{AtomicU32, Ordering::Acquire, Ordering::Relaxed};
 
/// A low-level synchronization primitive for one-time global execution.
///
@@ -44,13 +44,13 @@
/// assert_eq!(x, 42);
/// ```
///
-pub struct OnceLite(AtomicBool, AtomicBool);
+pub struct OnceLite(AtomicU32);
 
impl OnceLite {
     /// Creates a new `OnceLite` value.
     #[inline(always)]
     pub const fn new() -> Self {
-        Self(AtomicBool::new(false), AtomicBool::new(false))
+        Self(AtomicU32::new(0))
     }
 
     /// Performs an initialization routine once and only once. The given
@@ -71,10 +71,10 @@ pub const fn new() -> Self {
     /// [`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()
+        if self.0.load(Relaxed) == 0 && self.0.compare_exchange(0, 1, Acquire, Relaxed) == Ok(0) {
+            f();
+            self.0.store(2, Relaxed);
         };
-        self.1.store(true, Relaxed);
     }
 
     /// Returns `true` if some `call_once` call has completed successfully.
@@ -98,7 +98,7 @@ pub fn call_once<F: FnOnce()>(&self, f: F) {
     /// ```
     #[inline(always)]
     pub fn is_completed(&self) -> bool {
-        self.1.load(Relaxed)
+        self.0.load(Relaxed) > 1
     }
}

> The `rust` part should be default value for rustdoc tests, can we please
> omit that?

Will do!

Jens
Re: [PATCH v4 1/3] rust: Add `OnceLite` for executing code once
Posted by Daniel Sedlak 1 year, 2 months ago

On 11/27/24 8:46 PM, jens.korinth@tuta.io wrote:
>> Have you considered it to be implemented like `AtomicU32`? I think™ that
>> one atomic variable is more than enough.
> 
> Just to clarify - you mean something like this? Nevermind the magic numbers,
> I'd replace them, of course.

Yes, that's what I meant. But as Alice pointed out, you _may_ be able to 
reduce it to the one AtomicBool.

Daniel
> 
> diff --git a/rust/kernel/once_lite.rs b/rust/kernel/once_lite.rs
> index 723c3244fc85..0622ecbfced5 100644
> --- a/rust/kernel/once_lite.rs
> +++ b/rust/kernel/once_lite.rs
> @@ -16,7 +16,7 @@
> //!
> //! Reference: <https://doc.rust-lang.org/std/sync/struct.Once.html>
>   
> -use core::sync::atomic::{AtomicBool, Ordering::Relaxed};
> +use core::sync::atomic::{AtomicU32, Ordering::Acquire, Ordering::Relaxed};
>   
> /// A low-level synchronization primitive for one-time global execution.
> ///
> @@ -44,13 +44,13 @@
> /// assert_eq!(x, 42);
> /// ```
> ///
> -pub struct OnceLite(AtomicBool, AtomicBool);
> +pub struct OnceLite(AtomicU32);
>   
> impl OnceLite {
>       /// Creates a new `OnceLite` value.
>       #[inline(always)]
>       pub const fn new() -> Self {
> -        Self(AtomicBool::new(false), AtomicBool::new(false))
> +        Self(AtomicU32::new(0))
>       }
>   
>       /// Performs an initialization routine once and only once. The given
> @@ -71,10 +71,10 @@ pub const fn new() -> Self {
>       /// [`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()
> +        if self.0.load(Relaxed) == 0 && self.0.compare_exchange(0, 1, Acquire, Relaxed) == Ok(0) {
> +            f();
> +            self.0.store(2, Relaxed);
>           };
> -        self.1.store(true, Relaxed);
>       }
>   
>       /// Returns `true` if some `call_once` call has completed successfully.
> @@ -98,7 +98,7 @@ pub fn call_once<F: FnOnce()>(&self, f: F) {
>       /// ```
>       #[inline(always)]
>       pub fn is_completed(&self) -> bool {
> -        self.1.load(Relaxed)
> +        self.0.load(Relaxed) > 1
>       }
> }
> 
>> The `rust` part should be default value for rustdoc tests, can we please
>> omit that?
> 
> Will do!
> 
> Jens