kernel/sync/once.rs
1use core::cell::UnsafeCell;
2use core::mem::MaybeUninit;
3use core::sync::atomic::AtomicU8;
4use core::sync::atomic::Ordering;
5
6/// A synchronization primitive that can be used to block a thread until a value is ready.
7/// The procedure is as follows:
8/// 1. The Caller calls step(NOT_READY) to indicate that it is about to start the initialization process.
9/// 2. The Caller initializes the value.
10/// 3. The Caller calls step(IN_TRANSIT) to indicate that the value is ready.
11/// If step 1 fails, the value is already being initialized and the Caller must wait until is() returns true.
12pub struct Ready {
13 ready: AtomicU8,
14}
15
16impl Ready {
17 const READY: u8 = 2;
18 const IN_TRANSIT: u8 = 1;
19 const NOT_READY: u8 = 0;
20
21 /// Initializes a new Ready.
22 pub const fn new() -> Self {
23 Self {
24 ready: AtomicU8::new(0),
25 }
26 }
27
28 /// Move the Ready to the next state, if it is in state `from`.
29 pub fn step(&self, from: u8) -> bool {
30 self.forward(from, from + 1)
31 }
32
33 /// Move the Ready to state `to` if it is in state `from`.
34 fn forward(&self, _from: u8, _to: u8) -> bool {
35 self.ready
36 .compare_exchange(_from, _to, Ordering::AcqRel, Ordering::Acquire)
37 .is_ok()
38 }
39
40 /// Returns true if the value is ready.
41 pub fn is(&self) -> bool {
42 self.ready.load(Ordering::Acquire) == Self::READY
43 }
44}
45
46/// A synchronization primitive that represents a value that is initialized at most once.
47pub struct OnceCell<T> {
48 value: UnsafeCell<MaybeUninit<T>>,
49 init: Ready,
50}
51
52/// Safety:
53/// 1. A `value` is only written to atomically and once.
54/// 2. A `value` is only readable from after the initialization process is finished.
55/// 3. A `init` is only written and read from atomically.
56unsafe impl<T> Sync for OnceCell<T> {}
57
58impl<T> OnceCell<T> {
59 /// Initializes a new OnceCell.
60 pub const fn new() -> Self {
61 Self {
62 value: UnsafeCell::new(MaybeUninit::uninit()),
63 init: Ready::new(),
64 }
65 }
66
67 /// Returns a reference to the value if it is initialized.
68 pub fn get(&self) -> Option<&T> {
69 if self.init.is() {
70 // Safety:
71 // 1. By contract, is the value initialized if init.is() returns true.
72 // 2. No writes are allowed to the value after the initialization process is finished.
73 Some(unsafe { self.get_unchecked() })
74 } else {
75 None
76 }
77 }
78
79 /// Sets the value if it is not already initialized, and returns a reference to the value.
80 pub fn set_or_get(&self, value: T) -> &T {
81 if let Some(value) = self.set(value) {
82 value
83 } else {
84 // If we reach this point, initialization is already in progress.
85 while !self.init.is() {
86 hal::asm::nop!();
87 }
88 // Safety:
89 // 1. By contract, is the value initialized if init.is() returns true.
90 // 2. No writes are allowed to the value after the initialization process is finished.
91 unsafe { self.get_unchecked() }
92 }
93 }
94
95 /// Sets the value if it is not already initialized, and returns a reference to the value.
96 pub fn do_or_get<F>(&self, f: F) -> &T
97 where
98 F: FnOnce() -> T,
99 {
100 self.set_or_get(f())
101 }
102
103 /// Sets the value if it is not already initialized, returns a reference to the value if it was not set previously.
104 pub fn set(&self, value: T) -> Option<&T> {
105 if self.init.is() {
106 return None;
107 }
108
109 if self.init.step(Ready::NOT_READY) {
110 // Safety: We are now in the IN_TRANSIT state, so we are the only ones that can write to the value.
111 // We are also the only ones that can read from the value.
112 unsafe {
113 self.value.get().write(MaybeUninit::new(value));
114 }
115
116 if self.init.step(Ready::IN_TRANSIT) {
117 // Safety: We are now in the READY state, so no writes can happen to the value.
118 // 1. It is safe to create a immutable reference to the value.
119 // 2. We initialized the value, so it is safe to return a reference to it.
120 return Some(unsafe { self.get_unchecked() });
121 }
122
123 // By contract, only the thread that started the initialization process can finish it.
124 unreachable!();
125 }
126
127 None
128 }
129
130 /// Returns a reference to the value, unchecked.
131 ///
132 /// # Safety
133 /// Preconditions: The value must be initialized.
134 /// Postconditions: The value is returned.
135 unsafe fn get_unchecked(&self) -> &T {
136 unsafe { (*self.value.get()).assume_init_ref() }
137 }
138}