use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Condvar, Mutex, MutexGuard, PoisonError};
use std::time::Duration;
pub(crate) const STAGE_LIMIT: Duration = Duration::from_secs(10);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct Progress {
pub(crate) live: usize,
pub(crate) started: usize,
pub(crate) disposed: usize,
}
struct HookState {
panics: usize,
closed: bool,
live: usize,
started: usize,
disposed: usize,
}
impl HookState {
fn progress(&self) -> Progress {
Progress {
live: self.live,
started: self.started,
disposed: self.disposed,
}
}
}
pub(crate) struct Hooks {
state: Mutex<HookState>,
changed: Condvar,
peak: AtomicUsize,
}
pub(crate) struct Running<'a>(&'a Hooks);
impl Drop for Running<'_> {
fn drop(&mut self) {
self.0.state().live -= 1;
self.0.changed.notify_all();
}
}
impl Hooks {
pub(super) const fn new() -> Self {
Self {
state: Mutex::new(HookState {
panics: 0,
closed: false,
live: 0,
started: 0,
disposed: 0,
}),
changed: Condvar::new(),
peak: AtomicUsize::new(0),
}
}
fn state(&self) -> MutexGuard<'_, HookState> {
self.state.lock().unwrap_or_else(PoisonError::into_inner)
}
pub(super) fn enter(&self) -> Running<'_> {
let mut state = self.state();
if state.panics > 0 {
state.panics -= 1;
drop(state);
panic!("injected blocking-pool job panic");
}
state.live += 1;
state.started += 1;
self.peak.fetch_max(state.live, Ordering::SeqCst);
self.changed.notify_all();
while state.closed {
state = self
.changed
.wait(state)
.unwrap_or_else(PoisonError::into_inner);
}
Running(self)
}
pub(super) fn disposed(&self) {
self.state().disposed += 1;
self.changed.notify_all();
}
pub(crate) fn inject_panics(&self, count: usize) {
self.state().panics = count;
}
pub(crate) fn peak(&self) -> usize {
self.peak.load(Ordering::SeqCst)
}
pub(crate) fn set_gate_closed(&self, closed: bool) {
self.state().closed = closed;
self.changed.notify_all();
}
pub(crate) fn progress(&self) -> Progress {
self.state().progress()
}
pub(crate) fn wait_until(&self, reached: impl Fn(Progress) -> bool) -> Progress {
let (state, _) = self
.changed
.wait_timeout_while(self.state(), STAGE_LIMIT, |state| {
!reached(state.progress())
})
.unwrap_or_else(PoisonError::into_inner);
state.progress()
}
}
pub(crate) fn exclusive() -> MutexGuard<'static, ()> {
static EXCLUSIVE: Mutex<()> = Mutex::new(());
EXCLUSIVE.lock().unwrap_or_else(PoisonError::into_inner)
}