๐Ÿฆ€ Functional Rust

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

FeatureOCamlRust
Seeexample.mlexample.rs