find::task — cancellation
task provides cooperative cancellation for the blocking search
operations. It is the mechanism behind every timeout_ms option in the
find module — and it is public so you can build your own long-running work
with the same guarantees, or cancel a search from another thread.
pub struct CancelToken { /* clone, cheap */ }
pub struct AbortToken { /* clone, cheap */ }
pub enum AbortReason { Timeout, Signal, User }
pub struct CancelledError { pub reason: AbortReason }
The two sides
| Token | Who holds it | What it does |
|---|---|---|
CancelToken |
the running operation | checks heartbeat() between chunks of work; reads the deadline |
AbortToken |
the external caller | requests cancellation via abort(reason) |
A single CancelToken::new(timeout_ms) creates both: hand the CancelToken
to the search (the functions do this internally) and keep the AbortToken
(or clone the CancelToken) for cancellation from elsewhere.
The API
pub fn new(timeout_ms: Option<u32>) -> CancelToken // None = no deadline
pub fn heartbeat(&self) -> Result<(), String> // Err(reason.to_string()) when cancelled
pub fn heartbeat_reason(&self) -> Result<(), AbortReason> // typed variant
pub async fn wait(&self) -> AbortReason // await cancellation or timeout
pub fn aborted(&self) -> bool // non-blocking check
pub fn abort_token(&self) -> AbortToken
impl AbortToken {
pub fn abort(&self, reason: AbortReason) // request cancellation
}
How the search engines use it
CancelToken::new(options.timeout_ms)is created at the top ofglob/grep.- The parallel walkers call
heartbeat_reason()every 128 entries and stop the walk on cancellation. - Timeout is reported as
AbortReason::Timeout; the walkers keep the partial results and return them withtimed_out: true(see the timeout contract). - Non-timeout aborts (
User,Signal) always surface as errors — there is no partial-salvage for explicit cancellation. - A deadline that already elapsed before the first
heartbeat()is a hard error: nothing was collected, so there is nothing to salvage.
Writing your own cancellable work
use cosh_sdk::find::{task::{CancelToken, AbortReason}, glob, GlobOptions};
// A long-running loop that respects a deadline:
fn my_loop(ct: &CancelToken) -> Result<(), String> {
loop {
ct.heartbeat()?; // Err("Timeout") once the deadline passes
// … one chunk of work …
}
}
// Cancel a running glob from another thread:
let ct = CancelToken::new(Some(30_000));
let abort = ct.abort_token();
std::thread::spawn(move || {
std::thread::sleep(std::time::Duration::from_millis(100));
abort.abort(AbortReason::User); // glob now fails with "User"
});
let result = glob(GlobOptions { /* … */ timeout_ms: Some(30_000), ..Default::default() });
AbortReason and CancelledError both Display as "Timeout", "Signal",
or "User" — the exact strings the engines surface in their Err values.
Back to the module overview.