Skip to main content

lock_free_static/
once_cell.rs

1use core::{
2    cell::UnsafeCell,
3    mem::{ManuallyDrop, MaybeUninit, forget},
4    panic::{RefUnwindSafe, UnwindSafe},
5    ptr,
6    sync::atomic::{AtomicBool, Ordering},
7};
8
9struct Defer<F: FnOnce()> {
10    f: ManuallyDrop<F>,
11}
12impl<F: FnOnce()> Defer<F> {
13    pub fn new(f: F) -> Self {
14        Self {
15            f: ManuallyDrop::new(f),
16        }
17    }
18}
19impl<F: FnOnce()> Drop for Defer<F> {
20    fn drop(&mut self) {
21        (unsafe { ManuallyDrop::take(&mut self.f) })();
22    }
23}
24
25/// Lock-free thread-safe cell which can be written to only once.
26pub struct OnceCell<T> {
27    slot: UnsafeCell<MaybeUninit<T>>,
28    lock: AtomicBool,
29    init: AtomicBool,
30}
31
32unsafe impl<T: Send> Send for OnceCell<T> {}
33unsafe impl<T: Send + Sync> Sync for OnceCell<T> {}
34
35impl<T: UnwindSafe> UnwindSafe for OnceCell<T> {}
36impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for OnceCell<T> {}
37
38impl<T> Default for OnceCell<T> {
39    fn default() -> Self {
40        Self::new()
41    }
42}
43
44impl<T> OnceCell<T> {
45    /// Creates a new empty cell.
46    pub const fn new() -> Self {
47        Self {
48            slot: UnsafeCell::new(MaybeUninit::uninit()),
49            lock: AtomicBool::new(false),
50            init: AtomicBool::new(false),
51        }
52    }
53
54    /// Sets the contents of this cell to `value`.
55    ///
56    /// Returns `Ok(())` if the cell’s value was set by this call.
57    pub fn set(&self, value: T) -> Result<(), T> {
58        if self.lock.swap(true, Ordering::AcqRel) {
59            Err(value)
60        } else {
61            let slot = unsafe { &mut *self.slot.get() };
62            *slot = MaybeUninit::new(value);
63            self.init.store(true, Ordering::Release);
64            Ok(())
65        }
66    }
67
68    /// Sets the contents of this cell to value returned by `ctor` call.
69    ///
70    /// The `ctor` is called only if the cell’s value is going set by this call. Otherwice `ctor` returned in `Err(..)`.
71    ///
72    /// # Panics
73    ///
74    /// If `ctor` panics, the panic is propagated to the caller, and the cell remains uninitialized.
75    pub fn set_with<F: FnOnce() -> T>(&self, ctor: F) -> Result<(), F> {
76        if self.lock.swap(true, Ordering::AcqRel) {
77            Err(ctor)
78        } else {
79            let unlock = Defer::new(|| self.lock.store(false, Ordering::Release));
80            let value = ctor();
81            forget(unlock);
82
83            let slot = unsafe { &mut *self.slot.get() };
84            *slot = MaybeUninit::new(value);
85            self.init.store(true, Ordering::Release);
86            Ok(())
87        }
88    }
89
90    /// Gets the pointer to the underlying value.
91    ///
92    /// Returns `None` if the cell is empty.
93    pub fn get_ptr(&self) -> Option<*mut T> {
94        if self.init.load(Ordering::Relaxed) {
95            Some(self.slot.get() as *mut T)
96        } else {
97            None
98        }
99    }
100
101    /// Gets the reference to the underlying value.
102    ///
103    /// Returns `None` if the cell is empty, or being initialized.
104    pub fn get(&self) -> Option<&T> {
105        self.get_ptr().map(|p| unsafe { &*p })
106    }
107
108    /// Gets the mutable reference to the underlying value.
109    ///
110    /// Returns `None` if the cell is empty.
111    pub fn get_mut(&mut self) -> Option<&mut T> {
112        self.get_ptr().map(|p| unsafe { &mut *p })
113    }
114
115    /// Takes the value out of this cell, moving it back to an uninitialized state.
116    ///
117    /// Has no effect and returns `None` if the cell hasn’t been initialized.
118    pub fn take(&mut self) -> Option<T> {
119        if self.init.swap(false, Ordering::Relaxed) {
120            self.lock.store(false, Ordering::Relaxed);
121            Some(unsafe { ptr::read(self.slot.get()).assume_init() })
122        } else {
123            None
124        }
125    }
126
127    /// Consumes the cell, returning the wrapped value.
128    ///
129    /// Returns `None` if the cell was empty.
130    pub fn into_inner(mut self) -> Option<T> {
131        self.take()
132    }
133}
134
135impl<T> Drop for OnceCell<T> {
136    fn drop(&mut self) {
137        drop(self.take());
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    use super::OnceCell;
144
145    #[test]
146    fn get() {
147        let mut cell = OnceCell::<i32>::new();
148        assert!(cell.get().is_none());
149
150        cell.set(123).unwrap();
151        assert_eq!(cell.set(321), Err(321));
152        assert_eq!(*cell.get().unwrap(), 123);
153
154        {
155            let value_mut = cell.get_mut().unwrap();
156            assert_eq!(*value_mut, 123);
157            *value_mut = 321;
158            assert_eq!(*value_mut, 321);
159        }
160        assert_eq!(*cell.get().unwrap(), 321);
161    }
162
163    #[test]
164    fn take() {
165        let mut cell = OnceCell::<i32>::new();
166        assert!(cell.get().is_none());
167
168        cell.set(123).unwrap();
169        assert_eq!(cell.set(321), Err(321));
170        assert_eq!(*cell.get().unwrap(), 123);
171
172        assert_eq!(cell.take().unwrap(), 123);
173        assert!(cell.get().is_none());
174        assert!(cell.take().is_none());
175
176        cell.set(321).unwrap();
177        assert_eq!(*cell.get().unwrap(), 321);
178        assert_eq!(cell.into_inner().unwrap(), 321);
179    }
180
181    #[test]
182    fn set_with() {
183        let cell = OnceCell::<i32>::new();
184
185        assert!(cell.set_with(|| 123).is_ok());
186        assert_eq!(*cell.get().unwrap(), 123);
187        assert!(cell.set_with(|| 321).is_err());
188        assert_eq!(*cell.get().unwrap(), 123);
189    }
190
191    #[test]
192    fn set_with_panic() {
193        extern crate std;
194        use std::panic::catch_unwind;
195
196        let cell = OnceCell::<i32>::new();
197
198        assert_eq!(
199            *catch_unwind(|| cell.set_with(|| panic!("abc")))
200                .err()
201                .unwrap()
202                .downcast::<&'static str>()
203                .unwrap(),
204            "abc"
205        );
206        assert!(cell.get().is_none());
207
208        cell.set(321).unwrap();
209        assert_eq!(*cell.get().unwrap(), 321);
210    }
211
212    static CELL: OnceCell<i32> = OnceCell::new();
213
214    #[test]
215    fn static_() {
216        assert!(CELL.get().is_none());
217
218        CELL.set(123).unwrap();
219        assert_eq!(CELL.set(321), Err(321));
220        assert_eq!(*CELL.get().unwrap(), 123);
221    }
222}