spin/
once.rs

1use core::cell::UnsafeCell;
2use core::sync::atomic::{AtomicUsize, Ordering, spin_loop_hint as cpu_relax};
3use core::fmt;
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/// a 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    /// run.
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 F: FnOnce() -> T
100    {
101        let mut status = self.state.load(Ordering::SeqCst);
102
103        if status == INCOMPLETE {
104            status = self.state.compare_and_swap(INCOMPLETE,
105                                                 RUNNING,
106                                                 Ordering::SeqCst);
107            if status == INCOMPLETE { // We init
108                // We use a guard (Finish) to catch panics caused by builder
109                let mut finish = Finish { state: &self.state, panicked: true };
110                unsafe { *self.data.get() = Some(builder()) };
111                finish.panicked = false;
112
113                status = COMPLETE;
114                self.state.store(status, Ordering::SeqCst);
115
116                // This next line is strictly an optimization
117                return self.force_get();
118            }
119        }
120
121        loop {
122            match status {
123                INCOMPLETE => unreachable!(),
124                RUNNING => { // We spin
125                    cpu_relax();
126                    status = self.state.load(Ordering::SeqCst)
127                },
128                PANICKED => panic!("Once has panicked"),
129                COMPLETE => return self.force_get(),
130                _ => unsafe { unreachable() },
131            }
132        }
133    }
134
135    /// Returns a pointer iff the `Once` was previously initialized
136    pub fn try<'a>(&'a self) -> Option<&'a T> {
137        match self.state.load(Ordering::SeqCst) {
138            COMPLETE => Some(self.force_get()),
139            _        => None,
140        }
141    }
142
143    /// Like try, but will spin if the `Once` is in the process of being
144    /// initialized
145    pub fn wait<'a>(&'a self) -> Option<&'a T> {
146        loop {
147            match self.state.load(Ordering::SeqCst) {
148                INCOMPLETE => return None,
149                RUNNING    => cpu_relax(), // We spin
150                COMPLETE   => return Some(self.force_get()),
151                PANICKED   => panic!("Once has panicked"),
152                _ => unsafe { unreachable() },
153            }
154        }
155    }
156}
157
158struct Finish<'a> {
159    state: &'a AtomicUsize,
160    panicked: bool,
161}
162
163impl<'a> Drop for Finish<'a> {
164    fn drop(&mut self) {
165        if self.panicked {
166            self.state.store(PANICKED, Ordering::SeqCst);
167        }
168    }
169}
170
171#[cfg(test)]
172mod tests {
173    use std::prelude::v1::*;
174
175    use std::sync::mpsc::channel;
176    use std::thread;
177    use super::Once;
178
179    #[test]
180    fn smoke_once() {
181        static O: Once<()> = Once::new();
182        let mut a = 0;
183        O.call_once(|| a += 1);
184        assert_eq!(a, 1);
185        O.call_once(|| a += 1);
186        assert_eq!(a, 1);
187    }
188
189    #[test]
190    fn smoke_once_value() {
191        static O: Once<usize> = Once::new();
192        let a = O.call_once(|| 1);
193        assert_eq!(*a, 1);
194        let b = O.call_once(|| 2);
195        assert_eq!(*b, 1);
196    }
197
198    #[test]
199    fn stampede_once() {
200        static O: Once<()> = Once::new();
201        static mut RUN: bool = false;
202
203        let (tx, rx) = channel();
204        for _ in 0..10 {
205            let tx = tx.clone();
206            thread::spawn(move|| {
207                for _ in 0..4 { thread::yield_now() }
208                unsafe {
209                    O.call_once(|| {
210                        assert!(!RUN);
211                        RUN = true;
212                    });
213                    assert!(RUN);
214                }
215                tx.send(()).unwrap();
216            });
217        }
218
219        unsafe {
220            O.call_once(|| {
221                assert!(!RUN);
222                RUN = true;
223            });
224            assert!(RUN);
225        }
226
227        for _ in 0..10 {
228            rx.recv().unwrap();
229        }
230    }
231
232    #[test]
233    fn try() {
234        static INIT: Once<usize> = Once::new();
235
236        assert!(INIT.try().is_none());
237        INIT.call_once(|| 2);
238        assert_eq!(INIT.try().map(|r| *r), Some(2));
239    }
240
241    #[test]
242    fn try_no_wait() {
243        static INIT: Once<usize> = Once::new();
244
245        assert!(INIT.try().is_none());
246        thread::spawn(move|| {
247            INIT.call_once(|| loop { });
248        });
249        assert!(INIT.try().is_none());
250    }
251
252
253    #[test]
254    fn wait() {
255        static INIT: Once<usize> = Once::new();
256
257        assert!(INIT.wait().is_none());
258        INIT.call_once(|| 3);
259        assert_eq!(INIT.wait().map(|r| *r), Some(3));
260    }
261
262    #[test]
263    fn panic() {
264        use ::std::panic;
265
266        static INIT: Once<()> = Once::new();
267
268        // poison the once
269        let t = panic::catch_unwind(|| {
270            INIT.call_once(|| panic!());
271        });
272        assert!(t.is_err());
273
274        // poisoning propagates
275        let t = panic::catch_unwind(|| {
276            INIT.call_once(|| {});
277        });
278        assert!(t.is_err());
279    }
280
281    #[test]
282    fn init_constant() {
283        static O: Once<()> = Once::INIT;
284        let mut a = 0;
285        O.call_once(|| a += 1);
286        assert_eq!(a, 1);
287        O.call_once(|| a += 1);
288        assert_eq!(a, 1);
289    }
290}