osiris/sync/once.rs
1#![allow(dead_code)]
2
3use crate::hal;
4
5use core::cell::UnsafeCell;
6use core::mem::MaybeUninit;
7use core::ops::Deref;
8use core::sync::atomic::AtomicU8;
9use core::sync::atomic::Ordering;
10
11/// A synchronization primitive that can be used to block a thread until a value is ready.
12/// The procedure is as follows:
13/// 1. The Caller calls step(NOT_READY) to indicate that it is about to start the initialization process.
14/// 2. The Caller initializes the value.
15/// 3. The Caller calls step(IN_TRANSIT) to indicate that the value is ready.
16/// If step 1 fails, the value is already being initialized and the Caller must wait until is() returns true.
17pub struct Ready {
18 ready: AtomicU8,
19}
20
21impl Ready {
22 const READY: u8 = 2;
23 const IN_TRANSIT: u8 = 1;
24 const NOT_READY: u8 = 0;
25
26 /// Initializes a new Ready.
27 pub const fn new() -> Self {
28 Self {
29 ready: AtomicU8::new(0),
30 }
31 }
32
33 /// Move the Ready to the next state, if it is in state `from`.
34 pub fn step(&self, from: u8) -> bool {
35 self.forward(from, from + 1)
36 }
37
38 /// Move the Ready to state `to` if it is in state `from`.
39 fn forward(&self, _from: u8, _to: u8) -> bool {
40 self.ready
41 .compare_exchange(_from, _to, Ordering::AcqRel, Ordering::Acquire)
42 .is_ok()
43 }
44
45 /// Returns true if the value is ready.
46 pub fn is(&self) -> bool {
47 self.ready.load(Ordering::Acquire) == Self::READY
48 }
49}
50
51/// A synchronization primitive that represents a value that is initialized at most once.
52pub struct OnceCell<T> {
53 value: UnsafeCell<MaybeUninit<T>>,
54 init: Ready,
55}
56
57/// Safety:
58/// 1. A `value` is only written to atomically and once.
59/// 2. A `value` is only readable from after the initialization process is finished.
60/// 3. A `init` is only written and read from atomically.
61unsafe impl<T> Sync for OnceCell<T> {}
62
63impl<T> OnceCell<T> {
64 /// Initializes a new OnceCell.
65 pub const fn new() -> Self {
66 Self {
67 value: UnsafeCell::new(MaybeUninit::uninit()),
68 init: Ready::new(),
69 }
70 }
71
72 /// Returns a reference to the value if it is initialized.
73 pub fn get(&self) -> Option<&T> {
74 if self.init.is() {
75 // Safety:
76 // 1. By contract, is the value initialized if init.is() returns true.
77 // 2. No writes are allowed to the value after the initialization process is finished.
78 Some(unsafe { self.get_unchecked() })
79 } else {
80 None
81 }
82 }
83
84 /// Sets the value if it is not already initialized, and returns a reference to the value.
85 pub fn set_or_get(&self, value: T) -> &T {
86 if let Some(value) = self.set(value) {
87 value
88 } else {
89 // If we reach this point, initialization is already in progress.
90 while !self.init.is() {
91 hal::asm::nop!();
92 }
93 // Safety:
94 // 1. By contract, is the value initialized if init.is() returns true.
95 // 2. No writes are allowed to the value after the initialization process is finished.
96 unsafe { self.get_unchecked() }
97 }
98 }
99
100 /// Sets the value if it is not already initialized, and returns a reference to the value.
101 pub fn do_or_get<F>(&self, f: F) -> &T
102 where
103 F: FnOnce() -> T,
104 {
105 self.get_or_init(f)
106 }
107
108 /// Returns the value, initializing it with `f` if needed.
109 pub fn get_or_init<F>(&self, f: F) -> &T
110 where
111 F: FnOnce() -> T,
112 {
113 if let Some(value) = self.get() {
114 return value;
115 }
116
117 if self.init.step(Ready::NOT_READY) {
118 // Safety: We are now in the IN_TRANSIT state, so we are the only ones that can write to the value.
119 unsafe {
120 self.value.get().write(MaybeUninit::new(f()));
121 }
122
123 if self.init.step(Ready::IN_TRANSIT) {
124 // Safety: We are now in the READY state, so no writes can happen to the value.
125 return unsafe { self.get_unchecked() };
126 }
127
128 // By contract, only the thread that started the initialization process can finish it.
129 unreachable!();
130 }
131
132 // If we reach this point, initialization is already in progress.
133 while !self.init.is() {
134 hal::asm::nop!();
135 }
136
137 // Safety:
138 // 1. By contract, is the value initialized if init.is() returns true.
139 // 2. No writes are allowed to the value after the initialization process is finished.
140 unsafe { self.get_unchecked() }
141 }
142
143 /// Sets the value if it is not already initialized, returns a reference to the value if it was not set previously.
144 pub fn set(&self, value: T) -> Option<&T> {
145 if self.init.is() {
146 return None;
147 }
148
149 if self.init.step(Ready::NOT_READY) {
150 // Safety: We are now in the IN_TRANSIT state, so we are the only ones that can write to the value.
151 // We are also the only ones that can read from the value.
152 unsafe {
153 self.value.get().write(MaybeUninit::new(value));
154 }
155
156 if self.init.step(Ready::IN_TRANSIT) {
157 // Safety: We are now in the READY state, so no writes can happen to the value.
158 // 1. It is safe to create a immutable reference to the value.
159 // 2. We initialized the value, so it is safe to return a reference to it.
160 return Some(unsafe { self.get_unchecked() });
161 }
162
163 // By contract, only the thread that started the initialization process can finish it.
164 unreachable!();
165 }
166
167 None
168 }
169
170 /// Returns a reference to the value, unchecked.
171 ///
172 /// # Safety
173 /// Preconditions: The value must be initialized.
174 /// Postconditions: The value is returned.
175 unsafe fn get_unchecked(&self) -> &T {
176 unsafe { (*self.value.get()).assume_init_ref() }
177 }
178}
179
180/// A lazily initialized value.
181pub struct LazyLock<T, F = fn() -> T> {
182 cell: OnceCell<T>,
183 init: UnsafeCell<Option<F>>,
184}
185
186// Safety:
187// 1. `cell` synchronizes initialization and only exposes shared references once ready.
188// 2. `init` is taken only by the thread that transitions `cell` into initialization.
189unsafe impl<T: Sync, F: Send> Sync for LazyLock<T, F> {}
190unsafe impl<T: Send, F: Send> Send for LazyLock<T, F> {}
191
192impl<T, F> LazyLock<T, F> {
193 /// Creates a new LazyLock.
194 pub const fn new(f: F) -> Self {
195 Self {
196 cell: OnceCell::new(),
197 init: UnsafeCell::new(Some(f)),
198 }
199 }
200}
201
202impl<T, F> LazyLock<T, F>
203where
204 F: FnOnce() -> T,
205{
206 /// Returns the lazily initialized value.
207 pub fn force(this: &Self) -> &T {
208 this.cell.get_or_init(|| {
209 // Safety:
210 // 1. `get_or_init` calls this closure only for the thread that won initialization.
211 // 2. No other thread can access `init` after initialization starts.
212 let init = unsafe {
213 (*this.init.get())
214 .take()
215 .expect("LazyLock initializer missing")
216 };
217 init()
218 })
219 }
220}
221
222impl<T, F> Deref for LazyLock<T, F>
223where
224 F: FnOnce() -> T,
225{
226 type Target = T;
227
228 fn deref(&self) -> &Self::Target {
229 Self::force(self)
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use super::*;
236
237 #[test]
238 fn once_cell_get_or_init_initializes_once() {
239 let cell = OnceCell::new();
240 let mut calls = 0usize;
241
242 assert_eq!(
243 *cell.get_or_init(|| {
244 calls += 1;
245 7
246 }),
247 7
248 );
249
250 assert_eq!(*cell.get_or_init(|| 11), 7);
251 assert_eq!(calls, 1);
252 }
253
254 #[test]
255 fn once_cell_do_or_get_does_not_call_initializer_when_ready() {
256 let cell = OnceCell::new();
257
258 assert_eq!(*cell.set_or_get(7), 7);
259 assert_eq!(*cell.do_or_get(|| panic!("initializer should not run")), 7);
260 }
261
262 #[test]
263 fn lazy_lock_initializes_on_first_access() {
264 let value = LazyLock::new(|| 7usize);
265
266 assert_eq!(*value, 7);
267 assert_eq!(*LazyLock::force(&value), 7);
268 }
269}