use std::fmt;
use std::io::{self, Read};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
#[derive(Debug, Clone, Default)]
pub struct CancelToken {
flag: Arc<AtomicBool>,
}
impl CancelToken {
pub fn new() -> Self {
Self::default()
}
pub fn from_flag(flag: Arc<AtomicBool>) -> Self {
Self { flag }
}
pub fn flag(&self) -> &Arc<AtomicBool> {
&self.flag
}
pub fn cancel(&self) {
self.flag.store(true, Ordering::SeqCst);
}
pub fn is_cancelled(&self) -> bool {
self.flag.load(Ordering::SeqCst)
}
pub fn as_atomic(&self) -> &AtomicBool {
&self.flag
}
}
#[derive(Debug)]
pub(crate) struct CancelledIo;
impl fmt::Display for CancelledIo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("request cancelled")
}
}
impl std::error::Error for CancelledIo {}
pub(crate) fn cancelled_io() -> io::Error {
io::Error::other(CancelledIo)
}
pub fn is_cancelled_io(err: &io::Error) -> bool {
err.get_ref().is_some_and(|inner| inner.is::<CancelledIo>())
}
pub(crate) fn is_set(cancel: Option<&AtomicBool>) -> bool {
cancel.is_some_and(|flag| flag.load(Ordering::SeqCst))
}
pub(crate) fn sleep_cancellable(total: Duration, cancel: Option<&AtomicBool>) -> bool {
let until = Instant::now() + total;
loop {
if is_set(cancel) {
return false;
}
let now = Instant::now();
if now >= until {
return true;
}
std::thread::sleep((until - now).min(Duration::from_millis(50)));
}
}
pub(crate) struct CheckedReader<'a> {
pub(crate) inner: &'a mut dyn Read,
pub(crate) cancel: Option<&'a AtomicBool>,
}
impl Read for CheckedReader<'_> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if is_set(self.cancel) {
return Err(cancelled_io());
}
self.inner.read(buf)
}
}