Andreas Hindborg <a.hindborg@xxxxxxxxxx> writes: > Introduce the `SetOnce` type, a container that can only be written once. > The container uses an internal atomic to synchronize writes to the internal > value. > > Signed-off-by: Andreas Hindborg <a.hindborg@xxxxxxxxxx> > --- > rust/kernel/sync.rs | 2 + > rust/kernel/sync/set_once.rs | 125 +++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 127 insertions(+) > > diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs > index 81e3a806e57e2..13e6bc7fa87ac 100644 > --- a/rust/kernel/sync.rs > +++ b/rust/kernel/sync.rs > @@ -18,6 +18,7 @@ > mod locked_by; > pub mod poll; > pub mod rcu; > +mod set_once; > > pub use arc::{Arc, ArcBorrow, UniqueArc}; > pub use completion::Completion; > @@ -26,6 +27,7 @@ > pub use lock::mutex::{new_mutex, Mutex, MutexGuard}; > pub use lock::spinlock::{new_spinlock, SpinLock, SpinLockGuard}; > pub use locked_by::LockedBy; > +pub use set_once::SetOnce; > > /// Represents a lockdep class. It's a wrapper around C's `lock_class_key`. > #[repr(transparent)] > diff --git a/rust/kernel/sync/set_once.rs b/rust/kernel/sync/set_once.rs > new file mode 100644 > index 0000000000000..e1e31f5faed09 > --- /dev/null > +++ b/rust/kernel/sync/set_once.rs > @@ -0,0 +1,125 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +//! A container that can be initialized at most once. > + > +use super::atomic::ordering::Acquire; > +use super::atomic::ordering::Relaxed; > +use super::atomic::ordering::Release; > +use super::atomic::Atomic; > +use core::ptr::drop_in_place; > +use kernel::types::Opaque; > + > +/// A container that can be populated at most once. Thread safe. > +/// > +/// Once the a [`SetOnce`] is populated, it remains populated by the same object for the > +/// lifetime `Self`. > +/// > +/// # Invariants > +/// > +/// - `init` may only increase in value. > +/// - `init` may only assume values in the range `0..=2`. > +/// - `init == 0` if and only if the container is empty. > +/// - `init == 1` if and only if being initialized. > +/// - `init == 2` if and only if the container is populated and valid for shared access. > +/// > +/// # Example > +/// > +/// ``` > +/// # use kernel::sync::SetOnce; > +/// let value = SetOnce::new(); > +/// assert_eq!(None, value.as_ref()); > +/// > +/// let status = value.populate(42u8); > +/// assert_eq!(true, status); > +/// assert_eq!(Some(&42u8), value.as_ref()); > +/// assert_eq!(Some(42u8), value.copy()); > +/// > +/// let status = value.populate(101u8); > +/// assert_eq!(false, status); > +/// assert_eq!(Some(&42u8), value.as_ref()); > +/// assert_eq!(Some(42u8), value.copy()); > +/// ``` > +pub struct SetOnce<T> { > + init: Atomic<u32>, > + value: Opaque<T>, > +} > + > +impl<T> Default for SetOnce<T> { > + fn default() -> Self { > + Self::new() > + } > +} > + > +// TODO: change names I just saw that this line decided to stick around, that was obviously not intentional. Just disregard this line. Best regards, Andreas Hindborg