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

From: Daniel Sedlak
Date: Fri Nov 29 2024 - 03:22:44 EST




On 11/27/24 8:46 PM, jens.korinth@xxxxxxx 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