343: Cancellation Token
Difficulty: Advanced Category: Concurrency Concept: Arc<AtomicBool> as cancellation flag across threads Key Insight: Shared atomic boolean provides cooperative cancellation โ threads check the flag and exit gracefully// 343: Cancellation Token
// Arc<AtomicBool> for cooperative cancellation across threads
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
// Approach 1: Simple cancellation token
fn make_token() -> Arc<AtomicBool> {
Arc::new(AtomicBool::new(false))
}
fn cancel(token: &AtomicBool) {
token.store(true, Ordering::Relaxed);
}
fn is_cancelled(token: &AtomicBool) -> bool {
token.load(Ordering::Relaxed)
}
// Approach 2: Worker that respects cancellation
fn worker(token: Arc<AtomicBool>, name: String) -> String {
let mut count = 0u64;
while !is_cancelled(&token) && count < 1_000_000 {
count += 1;
}
format!("{} did {} iterations", name, count)
}
// Approach 3: Multi-worker with shared token
fn run_workers(n: usize) -> Vec<String> {
let token = make_token();
let handles: Vec<_> = (0..n)
.map(|i| {
let t = token.clone();
let name = format!("worker-{}", i);
thread::spawn(move || worker(t, name))
})
.collect();
thread::sleep(Duration::from_millis(1));
cancel(&token);
handles.into_iter().map(|h| h.join().unwrap()).collect()
}
fn main() {
let results = run_workers(3);
for r in &results {
println!("{}", r);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_token() {
let token = make_token();
assert!(!is_cancelled(&token));
cancel(&token);
assert!(is_cancelled(&token));
}
#[test]
fn test_worker_cancellation() {
let token = make_token();
let t = token.clone();
let handle = thread::spawn(move || worker(t, "test".into()));
thread::sleep(Duration::from_millis(1));
cancel(&token);
let result = handle.join().unwrap();
assert!(result.starts_with("test"));
}
#[test]
fn test_multi_workers() {
let results = run_workers(3);
assert_eq!(results.len(), 3);
for r in &results {
assert!(r.contains("worker-"));
}
}
}
(* 343: Cancellation Token *)
(* OCaml: use Atomic or ref for cooperative cancellation *)
(* Approach 1: Simple ref-based cancellation *)
let make_token () = ref false
let cancel token = token := true
let is_cancelled token = !token
let worker token name =
let count = ref 0 in
while not (is_cancelled token) && !count < 100 do
incr count
done;
Printf.sprintf "%s did %d iterations" name !count
(* Approach 2: Atomic for thread safety *)
let make_atomic_token () = Atomic.make false
let atomic_cancel token = Atomic.set token true
let atomic_is_cancelled token = Atomic.get token
(* Approach 3: Multi-worker cancellation *)
let run_workers n =
let token = make_token () in
let results = Array.make n "" in
let threads = Array.init n (fun i ->
Thread.create (fun () ->
results.(i) <- worker token (Printf.sprintf "worker-%d" i)
) ()
) in
Unix.sleepf 0.001;
cancel token;
Array.iter Thread.join threads;
Array.to_list results
(* Tests *)
let () =
let token = make_token () in
assert (not (is_cancelled token));
cancel token;
assert (is_cancelled token);
let result = worker (make_token ()) "test" in
assert (String.length result > 0);
Printf.printf "โ All tests passed\n"
๐ Detailed Comparison
Core Insight
Shared atomic boolean provides cooperative cancellation โ threads check the flag and exit gracefully
OCaml Approach
- See example.ml for implementation
Rust Approach
- See example.rs for implementation
Comparison Table
| Feature | OCaml | Rust |
|---|---|---|
| See | example.ml | example.rs |