]>
Commit | Line | Data |
---|---|---|
136023e0 | 1 | //! Thread-safe, non-blocking, "first one wins" flavor of `OnceCell`. |
5869c6ff XL |
2 | //! |
3 | //! If two threads race to initialize a type from the `race` module, they | |
4 | //! don't block, execute initialization function together, but only one of | |
5 | //! them stores the result. | |
6 | //! | |
7 | //! This module does not require `std` feature. | |
923072b8 FG |
8 | //! |
9 | //! # Atomic orderings | |
10 | //! | |
11 | //! All types in this module use `Acquire` and `Release` | |
12 | //! [atomic orderings](Ordering) for all their operations. While this is not | |
13 | //! strictly necessary for types other than `OnceBox`, it is useful for users as | |
14 | //! it allows them to be certain that after `get` or `get_or_init` returns on | |
15 | //! one thread, any side-effects caused by the setter thread prior to them | |
16 | //! calling `set` or `get_or_init` will be made visible to that thread; without | |
17 | //! it, it's possible for it to appear as if they haven't happened yet from the | |
18 | //! getter thread's perspective. This is an acceptable tradeoff to make since | |
19 | //! `Acquire` and `Release` have very little performance overhead on most | |
20 | //! architectures versus `Relaxed`. | |
5869c6ff | 21 | |
a2a8927a XL |
22 | #[cfg(feature = "atomic-polyfill")] |
23 | use atomic_polyfill as atomic; | |
24 | #[cfg(not(feature = "atomic-polyfill"))] | |
25 | use core::sync::atomic; | |
26 | ||
27 | use atomic::{AtomicUsize, Ordering}; | |
28 | use core::num::NonZeroUsize; | |
5869c6ff XL |
29 | |
30 | /// A thread-safe cell which can be written to only once. | |
31 | #[derive(Default, Debug)] | |
32 | pub struct OnceNonZeroUsize { | |
33 | inner: AtomicUsize, | |
34 | } | |
35 | ||
36 | impl OnceNonZeroUsize { | |
37 | /// Creates a new empty cell. | |
6a06907d | 38 | #[inline] |
5869c6ff XL |
39 | pub const fn new() -> OnceNonZeroUsize { |
40 | OnceNonZeroUsize { inner: AtomicUsize::new(0) } | |
41 | } | |
42 | ||
43 | /// Gets the underlying value. | |
6a06907d | 44 | #[inline] |
5869c6ff XL |
45 | pub fn get(&self) -> Option<NonZeroUsize> { |
46 | let val = self.inner.load(Ordering::Acquire); | |
47 | NonZeroUsize::new(val) | |
48 | } | |
49 | ||
50 | /// Sets the contents of this cell to `value`. | |
51 | /// | |
52 | /// Returns `Ok(())` if the cell was empty and `Err(())` if it was | |
53 | /// full. | |
6a06907d | 54 | #[inline] |
5869c6ff | 55 | pub fn set(&self, value: NonZeroUsize) -> Result<(), ()> { |
6a06907d XL |
56 | let exchange = |
57 | self.inner.compare_exchange(0, value.get(), Ordering::AcqRel, Ordering::Acquire); | |
58 | match exchange { | |
59 | Ok(_) => Ok(()), | |
60 | Err(_) => Err(()), | |
5869c6ff XL |
61 | } |
62 | } | |
63 | ||
64 | /// Gets the contents of the cell, initializing it with `f` if the cell was | |
65 | /// empty. | |
66 | /// | |
67 | /// If several threads concurrently run `get_or_init`, more than one `f` can | |
68 | /// be called. However, all threads will return the same value, produced by | |
69 | /// some `f`. | |
70 | pub fn get_or_init<F>(&self, f: F) -> NonZeroUsize | |
71 | where | |
72 | F: FnOnce() -> NonZeroUsize, | |
73 | { | |
74 | enum Void {} | |
75 | match self.get_or_try_init(|| Ok::<NonZeroUsize, Void>(f())) { | |
76 | Ok(val) => val, | |
77 | Err(void) => match void {}, | |
78 | } | |
79 | } | |
80 | ||
81 | /// Gets the contents of the cell, initializing it with `f` if | |
82 | /// the cell was empty. If the cell was empty and `f` failed, an | |
83 | /// error is returned. | |
84 | /// | |
85 | /// If several threads concurrently run `get_or_init`, more than one `f` can | |
86 | /// be called. However, all threads will return the same value, produced by | |
87 | /// some `f`. | |
88 | pub fn get_or_try_init<F, E>(&self, f: F) -> Result<NonZeroUsize, E> | |
89 | where | |
90 | F: FnOnce() -> Result<NonZeroUsize, E>, | |
91 | { | |
92 | let val = self.inner.load(Ordering::Acquire); | |
93 | let res = match NonZeroUsize::new(val) { | |
94 | Some(it) => it, | |
95 | None => { | |
96 | let mut val = f()?.get(); | |
6a06907d XL |
97 | let exchange = |
98 | self.inner.compare_exchange(0, val, Ordering::AcqRel, Ordering::Acquire); | |
99 | if let Err(old) = exchange { | |
100 | val = old; | |
5869c6ff XL |
101 | } |
102 | unsafe { NonZeroUsize::new_unchecked(val) } | |
103 | } | |
104 | }; | |
105 | Ok(res) | |
106 | } | |
107 | } | |
108 | ||
109 | /// A thread-safe cell which can be written to only once. | |
110 | #[derive(Default, Debug)] | |
111 | pub struct OnceBool { | |
112 | inner: OnceNonZeroUsize, | |
113 | } | |
114 | ||
115 | impl OnceBool { | |
116 | /// Creates a new empty cell. | |
6a06907d | 117 | #[inline] |
5869c6ff XL |
118 | pub const fn new() -> OnceBool { |
119 | OnceBool { inner: OnceNonZeroUsize::new() } | |
120 | } | |
121 | ||
122 | /// Gets the underlying value. | |
6a06907d | 123 | #[inline] |
5869c6ff XL |
124 | pub fn get(&self) -> Option<bool> { |
125 | self.inner.get().map(OnceBool::from_usize) | |
126 | } | |
127 | ||
128 | /// Sets the contents of this cell to `value`. | |
129 | /// | |
130 | /// Returns `Ok(())` if the cell was empty and `Err(())` if it was | |
131 | /// full. | |
6a06907d | 132 | #[inline] |
5869c6ff XL |
133 | pub fn set(&self, value: bool) -> Result<(), ()> { |
134 | self.inner.set(OnceBool::to_usize(value)) | |
135 | } | |
136 | ||
137 | /// Gets the contents of the cell, initializing it with `f` if the cell was | |
138 | /// empty. | |
139 | /// | |
140 | /// If several threads concurrently run `get_or_init`, more than one `f` can | |
141 | /// be called. However, all threads will return the same value, produced by | |
142 | /// some `f`. | |
143 | pub fn get_or_init<F>(&self, f: F) -> bool | |
144 | where | |
145 | F: FnOnce() -> bool, | |
146 | { | |
147 | OnceBool::from_usize(self.inner.get_or_init(|| OnceBool::to_usize(f()))) | |
148 | } | |
149 | ||
150 | /// Gets the contents of the cell, initializing it with `f` if | |
151 | /// the cell was empty. If the cell was empty and `f` failed, an | |
152 | /// error is returned. | |
153 | /// | |
154 | /// If several threads concurrently run `get_or_init`, more than one `f` can | |
155 | /// be called. However, all threads will return the same value, produced by | |
156 | /// some `f`. | |
157 | pub fn get_or_try_init<F, E>(&self, f: F) -> Result<bool, E> | |
158 | where | |
159 | F: FnOnce() -> Result<bool, E>, | |
160 | { | |
161 | self.inner.get_or_try_init(|| f().map(OnceBool::to_usize)).map(OnceBool::from_usize) | |
162 | } | |
163 | ||
6a06907d | 164 | #[inline] |
5869c6ff XL |
165 | fn from_usize(value: NonZeroUsize) -> bool { |
166 | value.get() == 1 | |
167 | } | |
6a06907d | 168 | #[inline] |
5869c6ff XL |
169 | fn to_usize(value: bool) -> NonZeroUsize { |
170 | unsafe { NonZeroUsize::new_unchecked(if value { 1 } else { 2 }) } | |
171 | } | |
172 | } | |
173 | ||
174 | #[cfg(feature = "alloc")] | |
175 | pub use self::once_box::OnceBox; | |
6a06907d | 176 | |
5869c6ff XL |
177 | #[cfg(feature = "alloc")] |
178 | mod once_box { | |
a2a8927a XL |
179 | use super::atomic::{AtomicPtr, Ordering}; |
180 | use core::{marker::PhantomData, ptr}; | |
5869c6ff | 181 | |
6a06907d XL |
182 | use alloc::boxed::Box; |
183 | ||
5869c6ff | 184 | /// A thread-safe cell which can be written to only once. |
5869c6ff XL |
185 | pub struct OnceBox<T> { |
186 | inner: AtomicPtr<T>, | |
187 | ghost: PhantomData<Option<Box<T>>>, | |
188 | } | |
189 | ||
a2a8927a XL |
190 | impl<T> core::fmt::Debug for OnceBox<T> { |
191 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | |
192 | write!(f, "OnceBox({:?})", self.inner.load(Ordering::Relaxed)) | |
193 | } | |
194 | } | |
195 | ||
6a06907d XL |
196 | impl<T> Default for OnceBox<T> { |
197 | fn default() -> Self { | |
198 | Self::new() | |
199 | } | |
200 | } | |
201 | ||
5869c6ff XL |
202 | impl<T> Drop for OnceBox<T> { |
203 | fn drop(&mut self) { | |
204 | let ptr = *self.inner.get_mut(); | |
205 | if !ptr.is_null() { | |
206 | drop(unsafe { Box::from_raw(ptr) }) | |
207 | } | |
208 | } | |
209 | } | |
210 | ||
211 | impl<T> OnceBox<T> { | |
212 | /// Creates a new empty cell. | |
213 | pub const fn new() -> OnceBox<T> { | |
214 | OnceBox { inner: AtomicPtr::new(ptr::null_mut()), ghost: PhantomData } | |
215 | } | |
216 | ||
217 | /// Gets a reference to the underlying value. | |
218 | pub fn get(&self) -> Option<&T> { | |
219 | let ptr = self.inner.load(Ordering::Acquire); | |
220 | if ptr.is_null() { | |
221 | return None; | |
222 | } | |
223 | Some(unsafe { &*ptr }) | |
224 | } | |
225 | ||
226 | /// Sets the contents of this cell to `value`. | |
227 | /// | |
228 | /// Returns `Ok(())` if the cell was empty and `Err(value)` if it was | |
229 | /// full. | |
230 | pub fn set(&self, value: Box<T>) -> Result<(), Box<T>> { | |
231 | let ptr = Box::into_raw(value); | |
6a06907d XL |
232 | let exchange = self.inner.compare_exchange( |
233 | ptr::null_mut(), | |
234 | ptr, | |
235 | Ordering::AcqRel, | |
236 | Ordering::Acquire, | |
237 | ); | |
238 | if let Err(_) = exchange { | |
5869c6ff XL |
239 | let value = unsafe { Box::from_raw(ptr) }; |
240 | return Err(value); | |
241 | } | |
242 | Ok(()) | |
243 | } | |
244 | ||
245 | /// Gets the contents of the cell, initializing it with `f` if the cell was | |
246 | /// empty. | |
247 | /// | |
248 | /// If several threads concurrently run `get_or_init`, more than one `f` can | |
249 | /// be called. However, all threads will return the same value, produced by | |
250 | /// some `f`. | |
251 | pub fn get_or_init<F>(&self, f: F) -> &T | |
252 | where | |
253 | F: FnOnce() -> Box<T>, | |
254 | { | |
255 | enum Void {} | |
256 | match self.get_or_try_init(|| Ok::<Box<T>, Void>(f())) { | |
257 | Ok(val) => val, | |
258 | Err(void) => match void {}, | |
259 | } | |
260 | } | |
261 | ||
262 | /// Gets the contents of the cell, initializing it with `f` if | |
263 | /// the cell was empty. If the cell was empty and `f` failed, an | |
264 | /// error is returned. | |
265 | /// | |
266 | /// If several threads concurrently run `get_or_init`, more than one `f` can | |
267 | /// be called. However, all threads will return the same value, produced by | |
268 | /// some `f`. | |
269 | pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E> | |
270 | where | |
271 | F: FnOnce() -> Result<Box<T>, E>, | |
272 | { | |
273 | let mut ptr = self.inner.load(Ordering::Acquire); | |
274 | ||
275 | if ptr.is_null() { | |
276 | let val = f()?; | |
277 | ptr = Box::into_raw(val); | |
6a06907d XL |
278 | let exchange = self.inner.compare_exchange( |
279 | ptr::null_mut(), | |
280 | ptr, | |
281 | Ordering::AcqRel, | |
282 | Ordering::Acquire, | |
283 | ); | |
284 | if let Err(old) = exchange { | |
5869c6ff | 285 | drop(unsafe { Box::from_raw(ptr) }); |
6a06907d | 286 | ptr = old; |
5869c6ff XL |
287 | } |
288 | }; | |
289 | Ok(unsafe { &*ptr }) | |
290 | } | |
291 | } | |
292 | ||
6a06907d XL |
293 | unsafe impl<T: Sync + Send> Sync for OnceBox<T> {} |
294 | ||
5869c6ff XL |
295 | /// ```compile_fail |
296 | /// struct S(*mut ()); | |
297 | /// unsafe impl Sync for S {} | |
298 | /// | |
299 | /// fn share<T: Sync>(_: &T) {} | |
300 | /// share(&once_cell::race::OnceBox::<S>::new()); | |
301 | /// ``` | |
6a06907d | 302 | fn _dummy() {} |
5869c6ff | 303 | } |