1use core::cell::UnsafeCell;
2use core::fmt;
3use core::sync::atomic::{spin_loop_hint as cpu_relax, AtomicUsize, Ordering};
4
5pub struct Once<T> {
22 state: AtomicUsize,
23 data: UnsafeCell<Option<T>>, }
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
37unsafe impl<T: Send + Sync> Sync for Once<T> {}
40unsafe impl<T: Send> Send for Once<T> {}
41
42const 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 pub const INIT: Self = Once {
54 state: AtomicUsize::new(INCOMPLETE),
55 data: UnsafeCell::new(None),
56 };
57
58 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 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 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 return self.force_get();
123 }
124 }
125
126 loop {
127 match status {
128 INCOMPLETE => unreachable!(),
129 RUNNING => {
130 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 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 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(), 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 let t = panic::catch_unwind(|| {
277 INIT.call_once(|| panic!());
278 });
279 assert!(t.is_err());
280
281 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}