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