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