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@kernel.org>
---
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
+
+impl<T> SetOnce<T> {
+ /// Create a new [`SetOnce`].
+ ///
+ /// The returned instance will be empty.
+ pub const fn new() -> Self {
+ // INVARIANT: The container is empty and we initialize `init` to `0`.
+ Self {
+ value: Opaque::uninit(),
+ init: Atomic::new(0),
+ }
+ }
+
+ /// Get a reference to the contained object.
+ ///
+ /// Returns [`None`] if this [`SetOnce`] is empty.
+ pub fn as_ref(&self) -> Option<&T> {
+ if self.init.load(Acquire) == 2 {
+ // SAFETY: By the type invariants of `Self`, `self.init == 2` means that `self.value`
+ // contains a valid value.
+ Some(unsafe { &*self.value.get() })
+ } else {
+ None
+ }
+ }
+
+ /// Populate the [`SetOnce`].
+ ///
+ /// Returns `true` if the [`SetOnce`] was successfully populated.
+ pub fn populate(&self, value: T) -> bool {
+ // INVARIANT: If the swap succeeds:
+ // - We increase `init`.
+ // - We write the valid value `1` to `init`.
+ // - Only one thread can succeed in this write, so we have exclusive access after the
+ // write.
+ if let Ok(0) = self.init.cmpxchg(0, 1, Relaxed) {
+ // SAFETY: By the type invariants of `Self`, the fact that we succeeded in writing `1`
+ // to `self.init` means we obtained exclusive access to the contained object.
+ unsafe { core::ptr::write(self.value.get(), value) };
+ // INVARIANT:
+ // - We increase `init`.
+ // - We write the valid value `2` to `init`.
+ // - We release our exclusive access to the contained object and the object is now
+ // valid for shared access.
+ self.init.store(2, Release);
+ true
+ } else {
+ false
+ }
+ }
+
+ /// Get a copy of the contained object.
+ ///
+ /// Returns [`None`] if the [`SetOnce`] is empty.
+ pub fn copy(&self) -> Option<T>
+ where
+ T: Copy,
+ {
+ self.as_ref().copied()
+ }
+}
+
+impl<T> Drop for SetOnce<T> {
+ fn drop(&mut self) {
+ if self.init.load(Acquire) == 2 {
+ // SAFETY: By the type invariants of `Self`, `self.init == 2` means that `self.value`
+ // contains a valid value. We have exclusive access, as we hold a `mut` reference to
+ // `self`.
+ unsafe { drop_in_place(self.value.get()) };
+ }
+ }
+}
--
2.47.2
On Mon Jul 7, 2025 at 3:29 PM CEST, Andreas Hindborg wrote:
> 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@kernel.org>
One nit and a safety comment fix below. (feel free to ignore the nit)
With the safety comment fixed:
Reviewed-by: Benno Lossin <lossin@kernel.org>
> ---
> 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;
I would have named this `once`.
> pub use arc::{Arc, ArcBorrow, UniqueArc};
> pub use completion::Completion;
> + /// Get a reference to the contained object.
> + ///
> + /// Returns [`None`] if this [`SetOnce`] is empty.
> + pub fn as_ref(&self) -> Option<&T> {
> + if self.init.load(Acquire) == 2 {
> + // SAFETY: By the type invariants of `Self`, `self.init == 2` means that `self.value`
> + // contains a valid value.
And the type invariants also ensure that the value of `self.init`
doesn't change.
So probably
// SAFETY: By the type invariants of `Self`, `self.init == 2` means that `self.value`
// contains a valid value. They also guarantee that `self.init` doesn't change.
If you come up with something better, feel free to use it.
---
Cheers,
Benno
> + Some(unsafe { &*self.value.get() })
> + } else {
> + None
> + }
> + }
"Benno Lossin" <lossin@kernel.org> writes:
> On Mon Jul 7, 2025 at 3:29 PM CEST, Andreas Hindborg wrote:
>> 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@kernel.org>
>
> One nit and a safety comment fix below. (feel free to ignore the nit)
> With the safety comment fixed:
>
> Reviewed-by: Benno Lossin <lossin@kernel.org>
>
>> ---
>> 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;
>
> I would have named this `once`.
So module `once` and struct `SetOnce`? Struct name `Once` would lead
thoughts to `std::sync::Once`, which is a different thing.
>
>> pub use arc::{Arc, ArcBorrow, UniqueArc};
>> pub use completion::Completion;
>
>> + /// Get a reference to the contained object.
>> + ///
>> + /// Returns [`None`] if this [`SetOnce`] is empty.
>> + pub fn as_ref(&self) -> Option<&T> {
>> + if self.init.load(Acquire) == 2 {
>> + // SAFETY: By the type invariants of `Self`, `self.init == 2` means that `self.value`
>> + // contains a valid value.
>
> And the type invariants also ensure that the value of `self.init`
> doesn't change.
>
> So probably
>
> // SAFETY: By the type invariants of `Self`, `self.init == 2` means that `self.value`
> // contains a valid value. They also guarantee that `self.init` doesn't change.
>
Sure 👍
Best regards,
Andreas Hindborg
On Tue Jul 8, 2025 at 3:06 PM CEST, Andreas Hindborg wrote: > "Benno Lossin" <lossin@kernel.org> writes: >> On Mon Jul 7, 2025 at 3:29 PM CEST, Andreas Hindborg wrote: >>> 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; >> >> I would have named this `once`. > > So module `once` and struct `SetOnce`? Struct name `Once` would lead > thoughts to `std::sync::Once`, which is a different thing. Hmm I thought that `Once` and `SetOnce` would live in the same module, but if they don't then I think it's better to keep the `set_once` module as-is. --- Cheers, Benno
"Benno Lossin" <lossin@kernel.org> writes: > On Tue Jul 8, 2025 at 3:06 PM CEST, Andreas Hindborg wrote: >> "Benno Lossin" <lossin@kernel.org> writes: >>> On Mon Jul 7, 2025 at 3:29 PM CEST, Andreas Hindborg wrote: >>>> 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; >>> >>> I would have named this `once`. >> >> So module `once` and struct `SetOnce`? Struct name `Once` would lead >> thoughts to `std::sync::Once`, which is a different thing. > > Hmm I thought that `Once` and `SetOnce` would live in the same module, > but if they don't then I think it's better to keep the `set_once` > module as-is. I guess they could live together. I was thinking a module for each. We can always move it, the module name is not part of a public API. Let's go with `set_once` for now and we can change it later, if we decide it is for the better? Best regards, Andreas Hindborg
On Wed Jul 9, 2025 at 10:56 AM CEST, Andreas Hindborg wrote: > "Benno Lossin" <lossin@kernel.org> writes: > >> On Tue Jul 8, 2025 at 3:06 PM CEST, Andreas Hindborg wrote: >>> "Benno Lossin" <lossin@kernel.org> writes: >>>> On Mon Jul 7, 2025 at 3:29 PM CEST, Andreas Hindborg wrote: >>>>> 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; >>>> >>>> I would have named this `once`. >>> >>> So module `once` and struct `SetOnce`? Struct name `Once` would lead >>> thoughts to `std::sync::Once`, which is a different thing. >> >> Hmm I thought that `Once` and `SetOnce` would live in the same module, >> but if they don't then I think it's better to keep the `set_once` >> module as-is. > > I guess they could live together. I was thinking a module for each. We > can always move it, the module name is not part of a public API. > > Let's go with `set_once` for now and we can change it later, if we > decide it is for the better? Sure. --- Cheers, Benno
On Mon, Jul 7, 2025 at 3:32 PM Andreas Hindborg <a.hindborg@kernel.org> wrote:
>
> 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@kernel.org>
LGTM:
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> +impl<T> Drop for SetOnce<T> {
> + fn drop(&mut self) {
> + if self.init.load(Acquire) == 2 {
> + // SAFETY: By the type invariants of `Self`, `self.init == 2` means that `self.value`
> + // contains a valid value. We have exclusive access, as we hold a `mut` reference to
> + // `self`.
> + unsafe { drop_in_place(self.value.get()) };
This load does not need to be Acquire. It can be a Relaxed load or
even an unsynchronized one since the access is exclusive.
Alice
"Alice Ryhl" <aliceryhl@google.com> writes:
> On Mon, Jul 7, 2025 at 3:32 PM Andreas Hindborg <a.hindborg@kernel.org> wrote:
>>
>> 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@kernel.org>
>
> LGTM:
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
>
>> +impl<T> Drop for SetOnce<T> {
>> + fn drop(&mut self) {
>> + if self.init.load(Acquire) == 2 {
>> + // SAFETY: By the type invariants of `Self`, `self.init == 2` means that `self.value`
>> + // contains a valid value. We have exclusive access, as we hold a `mut` reference to
>> + // `self`.
>> + unsafe { drop_in_place(self.value.get()) };
>
> This load does not need to be Acquire. It can be a Relaxed load or
> even an unsynchronized one since the access is exclusive.
Right, that is actually very cool. My rationale was that if a reference
has been shared to another thread of execution, we would need to
synchronize here to see a possible initialization from that other
thread. But I guess it is impossible to end the lifetime of a reference
without doing a synchronization somewhere else.
Best regards,
Andreas Hindborg
On Tue, Jul 8, 2025 at 10:48 AM Andreas Hindborg <a.hindborg@kernel.org> wrote:
>
> "Alice Ryhl" <aliceryhl@google.com> writes:
>
> > On Mon, Jul 7, 2025 at 3:32 PM Andreas Hindborg <a.hindborg@kernel.org> wrote:
> >>
> >> 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@kernel.org>
> >
> > LGTM:
> > Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> >
> >> +impl<T> Drop for SetOnce<T> {
> >> + fn drop(&mut self) {
> >> + if self.init.load(Acquire) == 2 {
> >> + // SAFETY: By the type invariants of `Self`, `self.init == 2` means that `self.value`
> >> + // contains a valid value. We have exclusive access, as we hold a `mut` reference to
> >> + // `self`.
> >> + unsafe { drop_in_place(self.value.get()) };
> >
> > This load does not need to be Acquire. It can be a Relaxed load or
> > even an unsynchronized one since the access is exclusive.
>
> Right, that is actually very cool. My rationale was that if a reference
> has been shared to another thread of execution, we would need to
> synchronize here to see a possible initialization from that other
> thread. But I guess it is impossible to end the lifetime of a reference
> without doing a synchronization somewhere else.
Yup, a mutable reference generally implies synchronization.
Alice
On Mon, Jul 07, 2025 at 03:38:58PM +0200, Alice Ryhl wrote:
> On Mon, Jul 7, 2025 at 3:32 PM Andreas Hindborg <a.hindborg@kernel.org> wrote:
> >
> > 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@kernel.org>
>
> LGTM:
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
>
> > +impl<T> Drop for SetOnce<T> {
> > + fn drop(&mut self) {
> > + if self.init.load(Acquire) == 2 {
> > + // SAFETY: By the type invariants of `Self`, `self.init == 2` means that `self.value`
> > + // contains a valid value. We have exclusive access, as we hold a `mut` reference to
> > + // `self`.
> > + unsafe { drop_in_place(self.value.get()) };
>
> This load does not need to be Acquire. It can be a Relaxed load or
> even an unsynchronized one since the access is exclusive.
Right, I think we can do the similar as Revocable here:
if *self.init.get_mut() == 2 { }
Further, with my following Benno's suggestion and making `Atomic<T>` an
`UnsafeCell<T>:
https://lore.kernel.org/rust-for-linux/aGhh-TvNOWhkt0JG@Mac.home/
compiler can generate a noalias reference here, which allows further
optimization.
Regards,
Boqun
>
> Alice
"Boqun Feng" <boqun.feng@gmail.com> writes:
> On Mon, Jul 07, 2025 at 03:38:58PM +0200, Alice Ryhl wrote:
>> On Mon, Jul 7, 2025 at 3:32 PM Andreas Hindborg <a.hindborg@kernel.org> wrote:
>> >
>> > 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@kernel.org>
>>
>> LGTM:
>> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
>>
>> > +impl<T> Drop for SetOnce<T> {
>> > + fn drop(&mut self) {
>> > + if self.init.load(Acquire) == 2 {
>> > + // SAFETY: By the type invariants of `Self`, `self.init == 2` means that `self.value`
>> > + // contains a valid value. We have exclusive access, as we hold a `mut` reference to
>> > + // `self`.
>> > + unsafe { drop_in_place(self.value.get()) };
>>
>> This load does not need to be Acquire. It can be a Relaxed load or
>> even an unsynchronized one since the access is exclusive.
>
> Right, I think we can do the similar as Revocable here:
>
> if *self.init.get_mut() == 2 { }
>
> Further, with my following Benno's suggestion and making `Atomic<T>` an
> `UnsafeCell<T>:
>
> https://lore.kernel.org/rust-for-linux/aGhh-TvNOWhkt0JG@Mac.home/
>
> compiler can generate a noalias reference here, which allows further
> optimization.
>
You would like to remove `PhantomPinned` to enable noalias? I guess that
makes sense in this case. I'll fix that for next spin.
Best regards,
Andreas Hindborg
On Tue Jul 8, 2025 at 10:54 AM CEST, Andreas Hindborg wrote:
> "Boqun Feng" <boqun.feng@gmail.com> writes:
>
>> On Mon, Jul 07, 2025 at 03:38:58PM +0200, Alice Ryhl wrote:
>>> On Mon, Jul 7, 2025 at 3:32 PM Andreas Hindborg <a.hindborg@kernel.org> wrote:
>>> >
>>> > 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@kernel.org>
>>>
>>> LGTM:
>>> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
>>>
>>> > +impl<T> Drop for SetOnce<T> {
>>> > + fn drop(&mut self) {
>>> > + if self.init.load(Acquire) == 2 {
>>> > + // SAFETY: By the type invariants of `Self`, `self.init == 2` means that `self.value`
>>> > + // contains a valid value. We have exclusive access, as we hold a `mut` reference to
>>> > + // `self`.
>>> > + unsafe { drop_in_place(self.value.get()) };
>>>
>>> This load does not need to be Acquire. It can be a Relaxed load or
>>> even an unsynchronized one since the access is exclusive.
>>
>> Right, I think we can do the similar as Revocable here:
>>
>> if *self.init.get_mut() == 2 { }
>>
>> Further, with my following Benno's suggestion and making `Atomic<T>` an
>> `UnsafeCell<T>:
>>
>> https://lore.kernel.org/rust-for-linux/aGhh-TvNOWhkt0JG@Mac.home/
>>
>> compiler can generate a noalias reference here, which allows further
>> optimization.
>>
>
> You would like to remove `PhantomPinned` to enable noalias? I guess that
> makes sense in this case. I'll fix that for next spin.
I think you two are talking about different things. Boqun is saying that
the `Atomic<T>` will use `UnsafeCell` rather than `Opaque`, which will
potentially allow more optimizations.
But you are talking about `SetOnce`, right? I think it makes more sense
for `SetOnce` to use `UnsafeCell<MaybeUninit<T>>` rather than `Opaque`
too. So feel free to change it in the next version.
---
Cheers,
Benno
"Benno Lossin" <lossin@kernel.org> writes:
> On Tue Jul 8, 2025 at 10:54 AM CEST, Andreas Hindborg wrote:
>> "Boqun Feng" <boqun.feng@gmail.com> writes:
>>
>>> On Mon, Jul 07, 2025 at 03:38:58PM +0200, Alice Ryhl wrote:
>>>> On Mon, Jul 7, 2025 at 3:32 PM Andreas Hindborg <a.hindborg@kernel.org> wrote:
>>>> >
>>>> > 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@kernel.org>
>>>>
>>>> LGTM:
>>>> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
>>>>
>>>> > +impl<T> Drop for SetOnce<T> {
>>>> > + fn drop(&mut self) {
>>>> > + if self.init.load(Acquire) == 2 {
>>>> > + // SAFETY: By the type invariants of `Self`, `self.init == 2` means that `self.value`
>>>> > + // contains a valid value. We have exclusive access, as we hold a `mut` reference to
>>>> > + // `self`.
>>>> > + unsafe { drop_in_place(self.value.get()) };
>>>>
>>>> This load does not need to be Acquire. It can be a Relaxed load or
>>>> even an unsynchronized one since the access is exclusive.
>>>
>>> Right, I think we can do the similar as Revocable here:
>>>
>>> if *self.init.get_mut() == 2 { }
Ok, now I got it. You are saying I don't need to use the atomic load
method, because I have mutable access. Sounds good.
But I guess a relaxed load and access through a mutable reference should
result in the same code generation on most (all?) platforms?
>>>
>>> Further, with my following Benno's suggestion and making `Atomic<T>` an
>>> `UnsafeCell<T>:
>>>
>>> https://lore.kernel.org/rust-for-linux/aGhh-TvNOWhkt0JG@Mac.home/
>>>
>>> compiler can generate a noalias reference here, which allows further
>>> optimization.
>>>
>>
>> You would like to remove `PhantomPinned` to enable noalias? I guess that
>> makes sense in this case. I'll fix that for next spin.
>
> I think you two are talking about different things. Boqun is saying that
> the `Atomic<T>` will use `UnsafeCell` rather than `Opaque`, which will
> potentially allow more optimizations.
>
> But you are talking about `SetOnce`, right? I think it makes more sense
> for `SetOnce` to use `UnsafeCell<MaybeUninit<T>>` rather than `Opaque`
> too. So feel free to change it in the next version.
Exactly. We don't need `UnsafePinned` mechanics here.
Best regards,
Andreas Hindborg
On Wed Jul 9, 2025 at 12:34 PM CEST, Andreas Hindborg wrote:
> "Benno Lossin" <lossin@kernel.org> writes:
>> On Tue Jul 8, 2025 at 10:54 AM CEST, Andreas Hindborg wrote:
>>> "Boqun Feng" <boqun.feng@gmail.com> writes:
>>>> On Mon, Jul 07, 2025 at 03:38:58PM +0200, Alice Ryhl wrote:
>>>>> On Mon, Jul 7, 2025 at 3:32 PM Andreas Hindborg <a.hindborg@kernel.org> wrote:
>>>>> >
>>>>> > 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@kernel.org>
>>>>>
>>>>> LGTM:
>>>>> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
>>>>>
>>>>> > +impl<T> Drop for SetOnce<T> {
>>>>> > + fn drop(&mut self) {
>>>>> > + if self.init.load(Acquire) == 2 {
>>>>> > + // SAFETY: By the type invariants of `Self`, `self.init == 2` means that `self.value`
>>>>> > + // contains a valid value. We have exclusive access, as we hold a `mut` reference to
>>>>> > + // `self`.
>>>>> > + unsafe { drop_in_place(self.value.get()) };
>>>>>
>>>>> This load does not need to be Acquire. It can be a Relaxed load or
>>>>> even an unsynchronized one since the access is exclusive.
>>>>
>>>> Right, I think we can do the similar as Revocable here:
>>>>
>>>> if *self.init.get_mut() == 2 { }
>
> Ok, now I got it. You are saying I don't need to use the atomic load
> method, because I have mutable access. Sounds good.
>
> But I guess a relaxed load and access through a mutable reference should
> result in the same code generation on most (all?) platforms?
AFAIK it is not the same on arm.
---
Cheers,
Benno
On Wed, Jul 09, 2025 at 08:22:16PM +0200, Benno Lossin wrote:
> On Wed Jul 9, 2025 at 12:34 PM CEST, Andreas Hindborg wrote:
> > "Benno Lossin" <lossin@kernel.org> writes:
> >> On Tue Jul 8, 2025 at 10:54 AM CEST, Andreas Hindborg wrote:
> >>> "Boqun Feng" <boqun.feng@gmail.com> writes:
> >>>> On Mon, Jul 07, 2025 at 03:38:58PM +0200, Alice Ryhl wrote:
> >>>>> On Mon, Jul 7, 2025 at 3:32 PM Andreas Hindborg <a.hindborg@kernel.org> wrote:
> >>>>> >
> >>>>> > 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@kernel.org>
> >>>>>
> >>>>> LGTM:
> >>>>> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> >>>>>
> >>>>> > +impl<T> Drop for SetOnce<T> {
> >>>>> > + fn drop(&mut self) {
> >>>>> > + if self.init.load(Acquire) == 2 {
> >>>>> > + // SAFETY: By the type invariants of `Self`, `self.init == 2` means that `self.value`
> >>>>> > + // contains a valid value. We have exclusive access, as we hold a `mut` reference to
> >>>>> > + // `self`.
> >>>>> > + unsafe { drop_in_place(self.value.get()) };
> >>>>>
> >>>>> This load does not need to be Acquire. It can be a Relaxed load or
> >>>>> even an unsynchronized one since the access is exclusive.
> >>>>
> >>>> Right, I think we can do the similar as Revocable here:
> >>>>
> >>>> if *self.init.get_mut() == 2 { }
> >
> > Ok, now I got it. You are saying I don't need to use the atomic load
> > method, because I have mutable access. Sounds good.
> >
> > But I guess a relaxed load and access through a mutable reference should
> > result in the same code generation on most (all?) platforms?
>
> AFAIK it is not the same on arm.
>
Right, when LTO=y, arm64 use acquire load to implement
READ_ONCE()/atomic_read().
Regards,
Boqun
> ---
> Cheers,
> Benno
>
On Wed Jul 9, 2025 at 10:12 PM CEST, Boqun Feng wrote:
> On Wed, Jul 09, 2025 at 08:22:16PM +0200, Benno Lossin wrote:
>> On Wed Jul 9, 2025 at 12:34 PM CEST, Andreas Hindborg wrote:
>> > "Benno Lossin" <lossin@kernel.org> writes:
>> >> On Tue Jul 8, 2025 at 10:54 AM CEST, Andreas Hindborg wrote:
>> >>> "Boqun Feng" <boqun.feng@gmail.com> writes:
>> >>>> On Mon, Jul 07, 2025 at 03:38:58PM +0200, Alice Ryhl wrote:
>> >>>>> On Mon, Jul 7, 2025 at 3:32 PM Andreas Hindborg <a.hindborg@kernel.org> wrote:
>> >>>>> >
>> >>>>> > 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@kernel.org>
>> >>>>>
>> >>>>> LGTM:
>> >>>>> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
>> >>>>>
>> >>>>> > +impl<T> Drop for SetOnce<T> {
>> >>>>> > + fn drop(&mut self) {
>> >>>>> > + if self.init.load(Acquire) == 2 {
>> >>>>> > + // SAFETY: By the type invariants of `Self`, `self.init == 2` means that `self.value`
>> >>>>> > + // contains a valid value. We have exclusive access, as we hold a `mut` reference to
>> >>>>> > + // `self`.
>> >>>>> > + unsafe { drop_in_place(self.value.get()) };
>> >>>>>
>> >>>>> This load does not need to be Acquire. It can be a Relaxed load or
>> >>>>> even an unsynchronized one since the access is exclusive.
>> >>>>
>> >>>> Right, I think we can do the similar as Revocable here:
>> >>>>
>> >>>> if *self.init.get_mut() == 2 { }
>> >
>> > Ok, now I got it. You are saying I don't need to use the atomic load
>> > method, because I have mutable access. Sounds good.
>> >
>> > But I guess a relaxed load and access through a mutable reference should
>> > result in the same code generation on most (all?) platforms?
>>
>> AFAIK it is not the same on arm.
>>
>
> Right, when LTO=y, arm64 use acquire load to implement
> READ_ONCE()/atomic_read().
But Andreas was talking about relaxed load vs mutable reference (=
normal unsynchronized write)?
---
Cheers,
Benno
On Wed, Jul 09, 2025 at 10:22:04PM +0200, Benno Lossin wrote:
[...]
> >> >>>>> > +impl<T> Drop for SetOnce<T> {
> >> >>>>> > + fn drop(&mut self) {
> >> >>>>> > + if self.init.load(Acquire) == 2 {
> >> >>>>> > + // SAFETY: By the type invariants of `Self`, `self.init == 2` means that `self.value`
> >> >>>>> > + // contains a valid value. We have exclusive access, as we hold a `mut` reference to
> >> >>>>> > + // `self`.
> >> >>>>> > + unsafe { drop_in_place(self.value.get()) };
> >> >>>>>
> >> >>>>> This load does not need to be Acquire. It can be a Relaxed load or
> >> >>>>> even an unsynchronized one since the access is exclusive.
> >> >>>>
> >> >>>> Right, I think we can do the similar as Revocable here:
> >> >>>>
> >> >>>> if *self.init.get_mut() == 2 { }
> >> >
> >> > Ok, now I got it. You are saying I don't need to use the atomic load
> >> > method, because I have mutable access. Sounds good.
> >> >
> >> > But I guess a relaxed load and access through a mutable reference should
> >> > result in the same code generation on most (all?) platforms?
> >>
> >> AFAIK it is not the same on arm.
> >>
> >
> > Right, when LTO=y, arm64 use acquire load to implement
> > READ_ONCE()/atomic_read().
>
> But Andreas was talking about relaxed load vs mutable reference (=
> normal unsynchronized write)?
>
No, I think it was a relaxed load (self.init.load(Relaxed)) vs a normal
unsynchronized *load* (*self.init.get_mut()). Yes, there is a mutable
reference, but we never use it for write.
Regards,
Boqun
> ---
> Cheers,
> Benno
>
Andreas Hindborg <a.hindborg@kernel.org> 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@kernel.org>
> ---
> 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
© 2016 - 2026 Red Hat, Inc.