use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
use std::sync::{Arc, Mutex, MutexGuard};
const SUPPORT_UNKNOWN: u8 = 0;
const SUPPORT_COOPERATIVE: u8 = 1;
const SUPPORT_PREEMPTIVE: u8 = 2;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum InterruptMode {
#[default]
Unbound,
Cooperative,
Preemptive,
}
impl InterruptMode {
pub const fn is_preemptive(self) -> bool {
matches!(self, Self::Preemptive)
}
}
#[derive(Debug, Default)]
struct InterruptRequests {
persistent: bool,
scoped: usize,
}
#[derive(Clone, Debug)]
pub struct InterruptHandle {
flag: Arc<AtomicBool>,
support: Arc<AtomicU8>,
requests: Arc<Mutex<InterruptRequests>>,
}
impl Default for InterruptHandle {
fn default() -> Self {
Self {
flag: Arc::new(AtomicBool::new(false)),
support: Arc::new(AtomicU8::new(SUPPORT_UNKNOWN)),
requests: Arc::new(Mutex::new(InterruptRequests::default())),
}
}
}
impl InterruptHandle {
pub fn new() -> Self {
Self::default()
}
pub fn interrupt(&self) {
let mut requests = self.requests();
requests.persistent = true;
self.flag.store(true, Ordering::SeqCst);
}
pub fn clear(&self) {
let mut requests = self.requests();
requests.persistent = false;
self.flag.store(requests.scoped != 0, Ordering::SeqCst);
}
#[must_use = "dropping the guard immediately releases this interruption request"]
pub fn interrupt_scoped(&self) -> InterruptGuard {
let mut requests = self.requests();
requests.scoped = requests
.scoped
.checked_add(1)
.expect("too many concurrent interruption requests");
self.flag.store(true, Ordering::SeqCst);
drop(requests);
InterruptGuard {
flag: self.flag.clone(),
requests: self.requests.clone(),
active: true,
}
}
pub fn is_interrupted(&self) -> bool {
self.flag.load(Ordering::SeqCst)
}
pub fn mode(&self) -> InterruptMode {
match self.support.load(Ordering::SeqCst) {
SUPPORT_PREEMPTIVE => InterruptMode::Preemptive,
SUPPORT_COOPERATIVE => InterruptMode::Cooperative,
_ => InterruptMode::Unbound,
}
}
fn requests(&self) -> MutexGuard<'_, InterruptRequests> {
self.requests.lock().unwrap_or_else(|err| err.into_inner())
}
pub(crate) fn flag(&self) -> &Arc<AtomicBool> {
&self.flag
}
pub(crate) fn bind_engine(&self, preemption: bool) {
let value = if preemption {
SUPPORT_PREEMPTIVE
} else {
SUPPORT_COOPERATIVE
};
self.support.store(value, Ordering::SeqCst);
}
}
#[derive(Debug)]
pub struct InterruptGuard {
flag: Arc<AtomicBool>,
requests: Arc<Mutex<InterruptRequests>>,
active: bool,
}
impl InterruptGuard {
pub fn clear(mut self) {
self.release();
}
fn release(&mut self) {
if !self.active {
return;
}
let mut requests = self.requests.lock().unwrap_or_else(|err| err.into_inner());
debug_assert!(requests.scoped > 0);
requests.scoped = requests.scoped.saturating_sub(1);
self.flag.store(
requests.persistent || requests.scoped != 0,
Ordering::SeqCst,
);
self.active = false;
}
}
impl Drop for InterruptGuard {
fn drop(&mut self) {
self.release();
}
}