Since `Once` will not have a non-poisoning variant, we remove it from the `poison` module. Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
114 lines
3.1 KiB
Rust
114 lines
3.1 KiB
Rust
use crate::cell::Cell;
|
|
use crate::sync as public;
|
|
use crate::sync::once::OnceExclusiveState;
|
|
|
|
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]
|
|
pub const fn new() -> Once {
|
|
Once { state: Cell::new(State::Incomplete) }
|
|
}
|
|
|
|
#[inline]
|
|
pub fn is_completed(&self) -> bool {
|
|
self.state.get() == State::Complete
|
|
}
|
|
|
|
#[inline]
|
|
pub(crate) fn state(&mut self) -> OnceExclusiveState {
|
|
match self.state.get() {
|
|
State::Incomplete => OnceExclusiveState::Incomplete,
|
|
State::Poisoned => OnceExclusiveState::Poisoned,
|
|
State::Complete => OnceExclusiveState::Complete,
|
|
_ => unreachable!("invalid Once state"),
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub(crate) fn set_state(&mut self, new_state: OnceExclusiveState) {
|
|
self.state.set(match new_state {
|
|
OnceExclusiveState::Incomplete => State::Incomplete,
|
|
OnceExclusiveState::Poisoned => State::Poisoned,
|
|
OnceExclusiveState::Complete => State::Complete,
|
|
});
|
|
}
|
|
|
|
#[cold]
|
|
#[track_caller]
|
|
pub fn wait(&self, _ignore_poisoning: bool) {
|
|
panic!("not implementable on this target");
|
|
}
|
|
|
|
#[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)
|
|
}
|
|
}
|