xref: /drstd/src/std/sys/unsupported/once.rs (revision 9670759b785600bf6315e4173e46a602f16add7a)
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