1 use crate::std::cell::Cell; 2 use crate::std::sync as public; 3 use crate::std::sync::once::ExclusiveState; 4 5 pub struct Once { 6 state: Cell<State>, 7 } 8 9 pub struct OnceState { 10 poisoned: bool, 11 set_state_to: Cell<State>, 12 } 13 14 #[derive(Clone, Copy, PartialEq, Eq)] 15 enum State { 16 Incomplete, 17 Poisoned, 18 Running, 19 Complete, 20 } 21 22 struct CompletionGuard<'a> { 23 state: &'a Cell<State>, 24 set_state_on_drop_to: State, 25 } 26 27 impl<'a> Drop for CompletionGuard<'a> { drop(&mut self)28 fn drop(&mut self) { 29 self.state.set(self.set_state_on_drop_to); 30 } 31 } 32 33 // Safety: threads are not supported on this platform. 34 unsafe impl Sync for Once {} 35 36 impl Once { 37 #[inline] new() -> Once38 pub const fn new() -> Once { 39 Once { 40 state: Cell::new(State::Incomplete), 41 } 42 } 43 44 #[inline] is_completed(&self) -> bool45 pub fn is_completed(&self) -> bool { 46 self.state.get() == State::Complete 47 } 48 49 #[inline] state(&mut self) -> ExclusiveState50 pub(crate) fn state(&mut self) -> ExclusiveState { 51 match self.state.get() { 52 State::Incomplete => ExclusiveState::Incomplete, 53 State::Poisoned => ExclusiveState::Poisoned, 54 State::Complete => ExclusiveState::Complete, 55 _ => unreachable!("invalid Once state"), 56 } 57 } 58 59 #[cold] 60 #[track_caller] call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState))61 pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState)) { 62 let state = self.state.get(); 63 match state { 64 State::Poisoned if !ignore_poisoning => { 65 // Panic to propagate the poison. 66 panic!("Once instance has previously been poisoned"); 67 } 68 State::Incomplete | State::Poisoned => { 69 self.state.set(State::Running); 70 // `guard` will set the new state on drop. 71 let mut guard = CompletionGuard { 72 state: &self.state, 73 set_state_on_drop_to: State::Poisoned, 74 }; 75 // Run the function, letting it know if we're poisoned or not. 76 let f_state = public::OnceState { 77 inner: OnceState { 78 poisoned: state == State::Poisoned, 79 set_state_to: Cell::new(State::Complete), 80 }, 81 }; 82 f(&f_state); 83 guard.set_state_on_drop_to = f_state.inner.set_state_to.get(); 84 } 85 State::Running => { 86 panic!("one-time initialization may not be performed recursively"); 87 } 88 State::Complete => {} 89 } 90 } 91 } 92 93 impl OnceState { 94 #[inline] is_poisoned(&self) -> bool95 pub fn is_poisoned(&self) -> bool { 96 self.poisoned 97 } 98 99 #[inline] poison(&self)100 pub fn poison(&self) { 101 self.set_state_to.set(State::Poisoned) 102 } 103 } 104