use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
static INTERRUPTED: AtomicBool = AtomicBool::new(false);
static CRITICAL_SECTION_DEPTH: AtomicUsize = AtomicUsize::new(0);
#[inline]
pub fn is_interrupted() -> bool {
INTERRUPTED.load(Ordering::Relaxed)
}
#[inline]
pub fn is_interrupted_outside_critical() -> bool {
if CRITICAL_SECTION_DEPTH.load(Ordering::Relaxed) > 0 {
false
} else {
INTERRUPTED.load(Ordering::Relaxed)
}
}
#[allow(dead_code)]
pub fn reset_interrupted() {
INTERRUPTED.store(false, Ordering::Relaxed);
}
#[inline]
pub fn enter_critical_section() {
CRITICAL_SECTION_DEPTH.fetch_add(1, Ordering::SeqCst);
}
#[inline]
pub fn exit_critical_section() {
CRITICAL_SECTION_DEPTH.fetch_sub(1, Ordering::SeqCst);
}
pub struct CriticalSectionGuard;
impl Default for CriticalSectionGuard {
fn default() -> Self {
Self::new()
}
}
impl CriticalSectionGuard {
pub fn new() -> Self {
enter_critical_section();
CriticalSectionGuard
}
}
impl Drop for CriticalSectionGuard {
fn drop(&mut self) {
exit_critical_section();
}
}
pub fn setup_signal_handler() -> Result<(), ctrlc::Error> {
ctrlc::set_handler(move || {
if !INTERRUPTED.swap(true, Ordering::SeqCst)
&& CRITICAL_SECTION_DEPTH.load(Ordering::Relaxed) > 0
{
eprintln!("\n⚠️ Interrupt received, finishing current write operation...");
}
})
}
#[derive(Debug, Clone)]
pub struct InterruptedError;
impl std::fmt::Display for InterruptedError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Operation interrupted by user")
}
}
impl std::error::Error for InterruptedError {}
#[inline]
pub fn check_interrupted() -> Result<(), InterruptedError> {
if is_interrupted() {
Err(InterruptedError)
} else {
Ok(())
}
}