use anyhow::{Result, bail};
use std::cell::RefCell;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
#[derive(Clone, Default)]
pub struct Token(Arc<AtomicBool>);
impl Token {
pub fn new() -> Self {
Self::default()
}
pub fn cancel(&self) {
self.0.store(true, Ordering::Relaxed);
}
pub fn is_cancelled(&self) -> bool {
self.0.load(Ordering::Relaxed)
}
}
thread_local! {
static CURRENT: RefCell<Option<Token>> = const { RefCell::new(None) };
}
pub fn with<T>(token: Token, work: impl FnOnce() -> T) -> T {
let previous = CURRENT.with(|current| current.replace(Some(token)));
let outcome = work();
CURRENT.with(|current| *current.borrow_mut() = previous);
outcome
}
pub fn cancelled() -> bool {
CURRENT.with(|current| {
current
.borrow()
.as_ref()
.is_some_and(Token::is_cancelled)
})
}
pub fn check() -> Result<()> {
if cancelled() {
bail!(
"cancelled at the client's request. If a render had already been \
submitted it may still complete at the provider, and will still be \
billed."
);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_thread_with_no_token_is_never_cancelled() {
assert!(!cancelled());
assert!(check().is_ok());
}
#[test]
fn a_token_cancels_the_work_holding_it() {
let token = Token::new();
let handle = token.clone();
with(token, || {
assert!(check().is_ok());
handle.cancel();
assert!(cancelled());
let error = check().unwrap_err().to_string();
assert!(error.contains("billed"), "must warn about the charge: {error}");
});
}
#[test]
fn cancellation_crosses_threads() {
let token = Token::new();
let handle = token.clone();
let worker = std::thread::spawn(move || {
with(token, || {
for _ in 0..1000 {
if check().is_err() {
return true;
}
std::thread::sleep(std::time::Duration::from_millis(1));
}
false
})
});
std::thread::sleep(std::time::Duration::from_millis(20));
handle.cancel();
assert!(worker.join().unwrap(), "the worker never saw the cancellation");
}
#[test]
fn the_token_does_not_outlive_the_work_it_was_installed_for() {
let token = Token::new();
token.cancel();
with(token, || assert!(cancelled()));
assert!(!cancelled(), "a cancelled token leaked past its request");
}
}