Skip to content

Commit e600425

Browse files
fujitaojeda
authored andcommitted
rust: print: Add support for calling a function exactly once
Add the Rust equivalent of the kernel's `DO_ONCE_LITE` macro. While it would be possible to implement the feature entirely as a Rust macro, the functionality that can be implemented as regular functions has been extracted and implemented as the `OnceLite` struct for better code maintainability. Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Reviewed-by: Gary Guo <gary@garyguo.net> Link: https://patch.msgid.link/20251117002452.4068692-2-fujita.tomonori@gmail.com [ Added prefix to title. - Miguel ] Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
1 parent 966f79c commit e600425

1 file changed

Lines changed: 83 additions & 0 deletions

File tree

rust/kernel/print.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ use crate::{
1111
fmt,
1212
prelude::*,
1313
str::RawFormatter,
14+
sync::atomic::{
15+
Atomic,
16+
AtomicType,
17+
Relaxed, //
18+
},
1419
};
1520

1621
// Called from `vsprintf` with format specifier `%pA`.
@@ -423,3 +428,81 @@ macro_rules! pr_cont (
423428
$crate::print_macro!($crate::print::format_strings::CONT, true, $($arg)*)
424429
)
425430
);
431+
432+
/// A lightweight `call_once` primitive.
433+
///
434+
/// This structure provides the Rust equivalent of the kernel's `DO_ONCE_LITE` macro.
435+
/// While it would be possible to implement the feature entirely as a Rust macro,
436+
/// the functionality that can be implemented as regular functions has been
437+
/// extracted and implemented as the `OnceLite` struct for better code maintainability.
438+
pub struct OnceLite(Atomic<State>);
439+
440+
#[derive(Clone, Copy, PartialEq, Eq)]
441+
#[repr(i32)]
442+
enum State {
443+
Incomplete = 0,
444+
Complete = 1,
445+
}
446+
447+
// SAFETY: `State` and `i32` has the same size and alignment, and it's round-trip
448+
// transmutable to `i32`.
449+
unsafe impl AtomicType for State {
450+
type Repr = i32;
451+
}
452+
453+
impl OnceLite {
454+
/// Creates a new [`OnceLite`] in the incomplete state.
455+
#[inline(always)]
456+
#[allow(clippy::new_without_default)]
457+
pub const fn new() -> Self {
458+
OnceLite(Atomic::new(State::Incomplete))
459+
}
460+
461+
/// Calls the provided function exactly once.
462+
///
463+
/// There is no other synchronization between two `call_once()`s
464+
/// except that only one will execute `f`, in other words, callers
465+
/// should not use a failed `call_once()` as a proof that another
466+
/// `call_once()` has already finished and the effect is observable
467+
/// to this thread.
468+
pub fn call_once<F>(&self, f: F) -> bool
469+
where
470+
F: FnOnce(),
471+
{
472+
// Avoid expensive cmpxchg if already completed.
473+
// ORDERING: `Relaxed` is used here since no synchronization is required.
474+
let old = self.0.load(Relaxed);
475+
if old == State::Complete {
476+
return false;
477+
}
478+
479+
// ORDERING: `Relaxed` is used here since no synchronization is required.
480+
let old = self.0.xchg(State::Complete, Relaxed);
481+
if old == State::Complete {
482+
return false;
483+
}
484+
485+
f();
486+
true
487+
}
488+
}
489+
490+
/// Run the given function exactly once.
491+
///
492+
/// This is equivalent to the kernel's `DO_ONCE_LITE` macro.
493+
///
494+
/// # Examples
495+
///
496+
/// ```
497+
/// kernel::do_once_lite! {
498+
/// kernel::pr_info!("This will be printed only once\n");
499+
/// };
500+
/// ```
501+
#[macro_export]
502+
macro_rules! do_once_lite {
503+
{ $($e:tt)* } => {{
504+
#[link_section = ".data..once"]
505+
static ONCE: $crate::print::OnceLite = $crate::print::OnceLite::new();
506+
ONCE.call_once(|| { $($e)* });
507+
}};
508+
}

0 commit comments

Comments
 (0)