• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use core::cell::UnsafeCell;
2 use core::fmt;
3 use core::hint::spin_loop;
4 use core::sync::atomic::{AtomicUsize, Ordering};
5 
6 /// A synchronization primitive which can be used to run a one-time global
7 /// initialization. Unlike its std equivalent, this is generalized so that the
8 /// closure returns a value and it is stored. Once therefore acts something like
9 /// a future, too.
10 pub struct Once<T> {
11     state: AtomicUsize,
12     data: UnsafeCell<Option<T>>, // TODO remove option and use mem::uninitialized
13 }
14 
15 impl<T: fmt::Debug> fmt::Debug for Once<T> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result16     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17         match self.r#try() {
18             Some(s) => write!(f, "Once {{ data: ")
19                 .and_then(|()| s.fmt(f))
20                 .and_then(|()| write!(f, "}}")),
21             None => write!(f, "Once {{ <uninitialized> }}"),
22         }
23     }
24 }
25 
26 // Same unsafe impls as `std::sync::RwLock`, because this also allows for
27 // concurrent reads.
28 unsafe impl<T: Send + Sync> Sync for Once<T> {}
29 unsafe impl<T: Send> Send for Once<T> {}
30 
31 // Four states that a Once can be in, encoded into the lower bits of `state` in
32 // the Once structure.
33 const INCOMPLETE: usize = 0x0;
34 const RUNNING: usize = 0x1;
35 const COMPLETE: usize = 0x2;
36 const PANICKED: usize = 0x3;
37 
38 use core::hint::unreachable_unchecked as unreachable;
39 
40 impl<T> Once<T> {
41     /// Initialization constant of `Once`.
42     pub const INIT: Self = Once {
43         state: AtomicUsize::new(INCOMPLETE),
44         data: UnsafeCell::new(None),
45     };
46 
47     /// Creates a new `Once` value.
new() -> Once<T>48     pub const fn new() -> Once<T> {
49         Self::INIT
50     }
51 
force_get<'a>(&'a self) -> &'a T52     fn force_get<'a>(&'a self) -> &'a T {
53         match unsafe { &*self.data.get() }.as_ref() {
54             None => unsafe { unreachable() },
55             Some(p) => p,
56         }
57     }
58 
59     /// Performs an initialization routine once and only once. The given closure
60     /// will be executed if this is the first time `call_once` has been called,
61     /// and otherwise the routine will *not* be invoked.
62     ///
63     /// This method will block the calling thread if another initialization
64     /// routine is currently running.
65     ///
66     /// When this function returns, it is guaranteed that some initialization
67     /// has run and completed (it may not be the closure specified). The
68     /// returned pointer will point to the result from the closure that was
69     /// run.
call_once<'a, F>(&'a self, builder: F) -> &'a T where F: FnOnce() -> T,70     pub fn call_once<'a, F>(&'a self, builder: F) -> &'a T
71     where
72         F: FnOnce() -> T,
73     {
74         let mut status = self.state.load(Ordering::SeqCst);
75 
76         if status == INCOMPLETE {
77             status = match self.state.compare_exchange(
78                 INCOMPLETE,
79                 RUNNING,
80                 Ordering::SeqCst,
81                 Ordering::SeqCst,
82             ) {
83                 Ok(status) => {
84                     debug_assert_eq!(
85                         status, INCOMPLETE,
86                         "if compare_exchange succeeded, previous status must be incomplete",
87                     );
88                     // We init
89                     // We use a guard (Finish) to catch panics caused by builder
90                     let mut finish = Finish {
91                         state: &self.state,
92                         panicked: true,
93                     };
94                     unsafe { *self.data.get() = Some(builder()) };
95                     finish.panicked = false;
96 
97                     self.state.store(COMPLETE, Ordering::SeqCst);
98 
99                     // This next line is strictly an optimization
100                     return self.force_get();
101                 }
102                 Err(status) => status,
103             }
104         }
105 
106         loop {
107             match status {
108                 INCOMPLETE => unreachable!(),
109                 RUNNING => {
110                     // We spin
111                     spin_loop();
112                     status = self.state.load(Ordering::SeqCst)
113                 }
114                 PANICKED => panic!("Once has panicked"),
115                 COMPLETE => return self.force_get(),
116                 _ => unsafe { unreachable() },
117             }
118         }
119     }
120 
121     /// Returns a pointer iff the `Once` was previously initialized
122     pub fn r#try<'a>(&'a self) -> Option<&'a T> {
123         match self.state.load(Ordering::SeqCst) {
124             COMPLETE => Some(self.force_get()),
125             _ => None,
126         }
127     }
128 
129     /// Like try, but will spin if the `Once` is in the process of being
130     /// initialized
wait<'a>(&'a self) -> Option<&'a T>131     pub fn wait<'a>(&'a self) -> Option<&'a T> {
132         loop {
133             match self.state.load(Ordering::SeqCst) {
134                 INCOMPLETE => return None,
135 
136                 RUNNING => {
137                     spin_loop() // We spin
138                 }
139                 COMPLETE => return Some(self.force_get()),
140                 PANICKED => panic!("Once has panicked"),
141                 _ => unsafe { unreachable() },
142             }
143         }
144     }
145 }
146 
147 struct Finish<'a> {
148     state: &'a AtomicUsize,
149     panicked: bool,
150 }
151 
152 impl<'a> Drop for Finish<'a> {
drop(&mut self)153     fn drop(&mut self) {
154         if self.panicked {
155             self.state.store(PANICKED, Ordering::SeqCst);
156         }
157     }
158 }
159