qadapt_spin/
once.rs

1use core::cell::UnsafeCell;
2use core::fmt;
3use core::sync::atomic::{spin_loop_hint as cpu_relax, AtomicUsize, Ordering};
4
5/// A synchronization primitive which can be used to run a one-time global
6/// initialization. Unlike its std equivalent, this is generalized so that The
7/// closure returns a value and it is stored. Once therefore acts something like
8/// 1a future, too.
9///
10/// # Examples
11///
12/// ```
13/// use spin;
14///
15/// static START: spin::Once<()> = spin::Once::new();
16///
17/// START.call_once(|| {
18///     // run initialization here
19/// });
20/// ```
21pub struct Once<T> {
22    state: AtomicUsize,
23    data: UnsafeCell<Option<T>>, // TODO remove option and use mem::uninitialized
24}
25
26impl<T: fmt::Debug> fmt::Debug for Once<T> {
27    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28        match self.try() {
29            Some(s) => write!(f, "Once {{ data: ")
30                .and_then(|()| s.fmt(f))
31                .and_then(|()| write!(f, "}}")),
32            None => write!(f, "Once {{ <uninitialized> }}"),
33        }
34    }
35}
36
37// Same unsafe impls as `std::sync::RwLock`, because this also allows for
38// concurrent reads.
39unsafe impl<T: Send + Sync> Sync for Once<T> {}
40unsafe impl<T: Send> Send for Once<T> {}
41
42// Four states that a Once can be in, encoded into the lower bits of `state` in
43// the Once structure.
44const INCOMPLETE: usize = 0x0;
45const RUNNING: usize = 0x1;
46const COMPLETE: usize = 0x2;
47const PANICKED: usize = 0x3;
48
49use core::hint::unreachable_unchecked as unreachable;
50
51impl<T> Once<T> {
52    /// Initialization constant of `Once`.
53    pub const INIT: Self = Once {
54        state: AtomicUsize::new(INCOMPLETE),
55        data: UnsafeCell::new(None),
56    };
57
58    /// Creates a new `Once` value.
59    pub const fn new() -> Once<T> {
60        Self::INIT
61    }
62
63    fn force_get<'a>(&'a self) -> &'a T {
64        match unsafe { &*self.data.get() }.as_ref() {
65            None => unsafe { unreachable() },
66            Some(p) => p,
67        }
68    }
69
70    /// Performs an initialization routine once and only once. The given closure
71    /// will be executed if this is the first time `call_once` has been called,
72    /// and otherwise the routine will *not* be invoked.
73    ///
74    /// This method will block the calling thread if another initialization
75    /// routine is currently running.
76    ///
77    /// When this function returns, it is guaranteed that some initialization
78    /// has run and completed (it may not be the closure specified). The
79    /// returned pointer will point to the result from the closure that was
80    /// ran.
81    ///
82    /// # Examples
83    ///
84    /// ```
85    /// use spin;
86    ///
87    /// static INIT: spin::Once<usize> = spin::Once::new();
88    ///
89    /// fn get_cached_val() -> usize {
90    ///     *INIT.call_once(expensive_computation)
91    /// }
92    ///
93    /// fn expensive_computation() -> usize {
94    ///     // ...
95    /// # 2
96    /// }
97    /// ```
98    pub fn call_once<'a, F>(&'a self, builder: F) -> &'a T
99    where
100        F: FnOnce() -> T,
101    {
102        let mut status = self.state.load(Ordering::SeqCst);
103
104        if status == INCOMPLETE {
105            status = self
106                .state
107                .compare_and_swap(INCOMPLETE, RUNNING, Ordering::SeqCst);
108            if status == INCOMPLETE {
109                // We init
110                // We use a guard (Finish) to catch panics caused by builder
111                let mut finish = Finish {
112                    state: &self.state,
113                    panicked: true,
114                };
115                unsafe { *self.data.get() = Some(builder()) };
116                finish.panicked = false;
117
118                status = COMPLETE;
119                self.state.store(status, Ordering::SeqCst);
120
121                // This next line is strictly an optomization
122                return self.force_get();
123            }
124        }
125
126        loop {
127            match status {
128                INCOMPLETE => unreachable!(),
129                RUNNING => {
130                    // We spin
131                    cpu_relax();
132                    status = self.state.load(Ordering::SeqCst)
133                }
134                PANICKED => panic!("Once has panicked"),
135                COMPLETE => return self.force_get(),
136                _ => unsafe { unreachable() },
137            }
138        }
139    }
140
141    /// Returns a pointer iff the `Once` was previously initialized
142    pub fn try<'a>(&'a self) -> Option<&'a T> {
143        match self.state.load(Ordering::SeqCst) {
144            COMPLETE => Some(self.force_get()),
145            _ => None,
146        }
147    }
148
149    /// Like try, but will spin if the `Once` is in the process of being
150    /// initialized
151    pub fn wait<'a>(&'a self) -> Option<&'a T> {
152        loop {
153            match self.state.load(Ordering::SeqCst) {
154                INCOMPLETE => return None,
155                RUNNING => cpu_relax(), // We spin
156                COMPLETE => return Some(self.force_get()),
157                PANICKED => panic!("Once has panicked"),
158                _ => unsafe { unreachable() },
159            }
160        }
161    }
162}
163
164struct Finish<'a> {
165    state: &'a AtomicUsize,
166    panicked: bool,
167}
168
169impl<'a> Drop for Finish<'a> {
170    fn drop(&mut self) {
171        if self.panicked {
172            self.state.store(PANICKED, Ordering::SeqCst);
173        }
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use std::prelude::v1::*;
180
181    use super::Once;
182    use std::sync::mpsc::channel;
183    use std::thread;
184
185    #[test]
186    fn smoke_once() {
187        static O: Once<()> = Once::new();
188        let mut a = 0;
189        O.call_once(|| a += 1);
190        assert_eq!(a, 1);
191        O.call_once(|| a += 1);
192        assert_eq!(a, 1);
193    }
194
195    #[test]
196    fn smoke_once_value() {
197        static O: Once<usize> = Once::new();
198        let a = O.call_once(|| 1);
199        assert_eq!(*a, 1);
200        let b = O.call_once(|| 2);
201        assert_eq!(*b, 1);
202    }
203
204    #[test]
205    fn stampede_once() {
206        static O: Once<()> = Once::new();
207        static mut RUN: bool = false;
208
209        let (tx, rx) = channel();
210        for _ in 0..10 {
211            let tx = tx.clone();
212            thread::spawn(move || {
213                for _ in 0..4 {
214                    thread::yield_now()
215                }
216                unsafe {
217                    O.call_once(|| {
218                        assert!(!RUN);
219                        RUN = true;
220                    });
221                    assert!(RUN);
222                }
223                tx.send(()).unwrap();
224            });
225        }
226
227        unsafe {
228            O.call_once(|| {
229                assert!(!RUN);
230                RUN = true;
231            });
232            assert!(RUN);
233        }
234
235        for _ in 0..10 {
236            rx.recv().unwrap();
237        }
238    }
239
240    #[test]
241    fn try() {
242        static INIT: Once<usize> = Once::new();
243
244        assert!(INIT.try().is_none());
245        INIT.call_once(|| 2);
246        assert_eq!(INIT.try().map(|r| *r), Some(2));
247    }
248
249    #[test]
250    fn try_no_wait() {
251        static INIT: Once<usize> = Once::new();
252
253        assert!(INIT.try().is_none());
254        thread::spawn(move || {
255            INIT.call_once(|| loop {});
256        });
257        assert!(INIT.try().is_none());
258    }
259
260    #[test]
261    fn wait() {
262        static INIT: Once<usize> = Once::new();
263
264        assert!(INIT.wait().is_none());
265        INIT.call_once(|| 3);
266        assert_eq!(INIT.wait().map(|r| *r), Some(3));
267    }
268
269    #[test]
270    fn panic() {
271        use std::panic;
272
273        static INIT: Once<()> = Once::new();
274
275        // poison the once
276        let t = panic::catch_unwind(|| {
277            INIT.call_once(|| panic!());
278        });
279        assert!(t.is_err());
280
281        // poisoning propagates
282        let t = panic::catch_unwind(|| {
283            INIT.call_once(|| {});
284        });
285        assert!(t.is_err());
286    }
287
288    #[test]
289    fn init_constant() {
290        static O: Once<()> = Once::INIT;
291        let mut a = 0;
292        O.call_once(|| a += 1);
293        assert_eq!(a, 1);
294        O.call_once(|| a += 1);
295        assert_eq!(a, 1);
296    }
297}