Skip to main content

Crate almost_enough

Crate almost_enough 

Source
Expand description

§almost-enough

Cooperative cancellation for Rust. Cancel long-running operations from another thread.

§Which Crate?

  • Application code: Use almost-enough (this crate) - has all implementations
  • Library code: Depend on enough (minimal) and accept impl Stop

§Complete Example

use almost_enough::{Stopper, Stop};
use std::thread;
use std::time::Duration;

// 1. Create a stopper
let stop = Stopper::new();

// 2. Clone and pass to worker thread
let worker_stop = stop.clone();
let handle = thread::spawn(move || {
    for i in 0..1000 {
        // 3. Check periodically - exit early if cancelled
        if worker_stop.should_stop() {
            return Err("cancelled");
        }
        // ... do work ...
    }
    Ok("completed")
});

// 4. Cancel from main thread (or signal handler, timeout, etc.)
thread::sleep(Duration::from_millis(1));
stop.cancel();

// Worker exits early
let result = handle.join().unwrap();
// result is either Ok("completed") or Err("cancelled")

§Quick Reference

let stop = Stopper::new();
stop.cancel();              // Trigger cancellation
stop.should_stop();         // Returns true if cancelled
stop.check()?;              // Returns Err(StopReason) if cancelled

§Type Overview

TypeFeatureUse Case
UnstoppablecoreZero-cost “never stop”
StopSource / StopRefcoreStack-based, borrowed, zero-alloc
FnStopcoreWrap any closure
OrStopcoreCombine multiple stops
StopperallocDefault choice - Arc-based, clone to share
SyncStopperallocLike Stopper with Acquire/Release ordering
ChildStopperallocHierarchical parent-child cancellation
StopTokenallocType-erased dynamic dispatch - Arc-based, Clone
BoxedStopallocType-erased (prefer StopToken)
WithTimeoutstdAdd deadline to any Stop

§StopExt Extension Trait

The StopExt trait adds combinator methods to any Stop implementation:

use almost_enough::{StopSource, Stop, StopExt};

let timeout = StopSource::new();
let cancel = StopSource::new();

// Combine: stop if either stops
let combined = timeout.as_ref().or(cancel.as_ref());
assert!(!combined.should_stop());

cancel.cancel();
assert!(combined.should_stop());

§Type Erasure with StopToken

StopToken wraps Arc<dyn Stop> — it’s Clone (cheap Arc increment), type-erased, and can be sent across threads. Stopper and SyncStopper convert to StopToken at zero cost via From/Into (the existing Arc is reused, no double-wrapping).

Use CloneStop (a trait alias for Stop + Clone + 'static) to accept any clonable stop, then erase with into_token() at the boundary:

use almost_enough::{CloneStop, StopToken, Stopper, Stop, StopExt};

fn outer(stop: impl CloneStop) {
    // Erase the concrete type — StopToken is Clone
    let stop: StopToken = stop.into_token();
    inner(&stop);
}

fn inner(stop: &StopToken) {
    let stop2 = stop.clone(); // cheap Arc increment
    // Only one version of this function exists
    while !stop.should_stop() {
        break;
    }
}

let stop = Stopper::new();
outer(stop);

§Hierarchical Cancellation with .child()

Create child stops that inherit cancellation from their parent:

use almost_enough::{Stopper, Stop, StopExt};

let parent = Stopper::new();
let child = parent.child();

// Child cancellation doesn't affect parent
child.cancel();
assert!(!parent.should_stop());

// But parent cancellation propagates to children
let child2 = parent.child();
parent.cancel();
assert!(child2.should_stop());

§Stop Guards (RAII Cancellation)

Automatically stop on scope exit unless explicitly disarmed:

use almost_enough::{Stopper, StopDropRoll};

fn do_work(source: &Stopper) -> Result<(), &'static str> {
    let guard = source.stop_on_drop();

    // If we return early or panic, source is stopped
    risky_operation()?;

    // Success! Don't stop.
    guard.disarm();
    Ok(())
}

fn risky_operation() -> Result<(), &'static str> {
    Ok(())
}

let source = Stopper::new();
do_work(&source).unwrap();

§Feature Flags

  • std (default) - Full functionality including timeouts
  • alloc - Arc-based types, into_boxed(), child(), StopDropRoll
  • None - Core trait and stack-based types only

Re-exports§

pub use time::TimeoutExt;
pub use time::WithTimeout;

Modules§

time
Timeout support for cancellation.

Structs§

BoxedStop
A heap-allocated Stop implementation.
CancelGuard
A guard that cancels a source when dropped, unless disarmed.
ChildStopper
A cancellation primitive with tree-structured parent-child relationships.
FnStop
A Stop implementation backed by a closure.
OrStop
Combines two Stop implementations.
StopRef
A borrowed reference to a StopSource.
StopSource
A stack-based cancellation source.
StopToken
A shared-ownership Stop implementation with cheap Clone.
Stopper
A cancellation primitive with unified clone semantics.
SyncStopper
A cancellation primitive with Release/Acquire memory ordering.
Unstoppable
A Stop implementation that never stops (no cooperative cancellation).

Enums§

StopReason
Why an operation was stopped.

Traits§

Cancellable
Trait for types that can be stopped/cancelled.
CloneStop
Trait alias for stop tokens that can be cloned and sent across threads.
Stop
Cooperative cancellation check.
StopDropRoll
Extension trait for creating CancelGuards.
StopExt
Extension trait providing ergonomic combinators for Stop implementations.

Type Aliases§

NeverDeprecated
Type alias for backwards compatibility.