summaryrefslogtreecommitdiffstats
path: root/library/std/src/sys/unsupported/once.rs
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/sys/unsupported/once.rs')
-rw-r--r--library/std/src/sys/unsupported/once.rs89
1 files changed, 89 insertions, 0 deletions
diff --git a/library/std/src/sys/unsupported/once.rs b/library/std/src/sys/unsupported/once.rs
new file mode 100644
index 000000000..b4bb4975f
--- /dev/null
+++ b/library/std/src/sys/unsupported/once.rs
@@ -0,0 +1,89 @@
+use crate::cell::Cell;
+use crate::sync as public;
+
+pub struct Once {
+ state: Cell<State>,
+}
+
+pub struct OnceState {
+ poisoned: bool,
+ set_state_to: Cell<State>,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+enum State {
+ Incomplete,
+ Poisoned,
+ Running,
+ Complete,
+}
+
+struct CompletionGuard<'a> {
+ state: &'a Cell<State>,
+ set_state_on_drop_to: State,
+}
+
+impl<'a> Drop for CompletionGuard<'a> {
+ fn drop(&mut self) {
+ self.state.set(self.set_state_on_drop_to);
+ }
+}
+
+// Safety: threads are not supported on this platform.
+unsafe impl Sync for Once {}
+
+impl Once {
+ #[inline]
+ #[rustc_const_stable(feature = "const_once_new", since = "1.32.0")]
+ pub const fn new() -> Once {
+ Once { state: Cell::new(State::Incomplete) }
+ }
+
+ #[inline]
+ pub fn is_completed(&self) -> bool {
+ self.state.get() == State::Complete
+ }
+
+ #[cold]
+ #[track_caller]
+ pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState)) {
+ let state = self.state.get();
+ match state {
+ State::Poisoned if !ignore_poisoning => {
+ // Panic to propagate the poison.
+ panic!("Once instance has previously been poisoned");
+ }
+ State::Incomplete | State::Poisoned => {
+ self.state.set(State::Running);
+ // `guard` will set the new state on drop.
+ let mut guard =
+ CompletionGuard { state: &self.state, set_state_on_drop_to: State::Poisoned };
+ // Run the function, letting it know if we're poisoned or not.
+ let f_state = public::OnceState {
+ inner: OnceState {
+ poisoned: state == State::Poisoned,
+ set_state_to: Cell::new(State::Complete),
+ },
+ };
+ f(&f_state);
+ guard.set_state_on_drop_to = f_state.inner.set_state_to.get();
+ }
+ State::Running => {
+ panic!("one-time initialization may not be performed recursively");
+ }
+ State::Complete => {}
+ }
+ }
+}
+
+impl OnceState {
+ #[inline]
+ pub fn is_poisoned(&self) -> bool {
+ self.poisoned
+ }
+
+ #[inline]
+ pub fn poison(&self) {
+ self.set_state_to.set(State::Poisoned)
+ }
+}