Files
OneCAD/crates/kernel/src/watch.rs
T

45 lines
1.7 KiB
Rust

//! Progress and cooperative cancellation for long kernel work (reading a large STEP file) run
//! off the window thread. The caller installs a [`Watch`] around the work with [`watching`];
//! the work reports how far it is and stops at its next check once the watch says so. Work
//! that nobody watches runs as before.
use std::cell::RefCell;
use std::sync::Arc;
/// Someone waiting for long work: told how far it is, and able to stop it.
pub trait Watch: Send + Sync {
/// Stop at the next check.
fn cancelled(&self) -> bool;
/// `done` of `total` units of the current step (`stage`, such as "reading" or "converting").
fn progress(&self, stage: &'static str, done: usize, total: usize);
}
thread_local! {
static CURRENT: RefCell<Option<Arc<dyn Watch>>> = const { RefCell::new(None) };
}
/// Run `f` with `watch` seeing the kernel work it does on this thread.
pub fn watching<R>(watch: Arc<dyn Watch>, f: impl FnOnce() -> R) -> R {
let previous = CURRENT.with(|c| c.replace(Some(watch)));
struct Restore(Option<Option<Arc<dyn Watch>>>);
impl Drop for Restore {
fn drop(&mut self) {
if let Some(previous) = self.0.take() {
CURRENT.with(|c| *c.borrow_mut() = previous);
}
}
}
let _restore = Restore(Some(previous));
f()
}
/// The watch over this thread's work, to hand to the worker threads it starts.
pub(crate) fn current() -> Option<Arc<dyn Watch>> {
CURRENT.with(|c| c.borrow().clone())
}
/// The error long work returns when its watch cancelled it.
pub(crate) fn cancelled_error(what: &str) -> crate::KernelError {
crate::KernelError::Failed(format!("{what} cancelled"))
}