Tokio-free token-bucket admission control with loom-checkable atomics.
[Throttle] is a fixed-capacity bucket that refills at a
configured rate. Callers ask for n tokens with
[Throttle::try_acquire] (non-blocking) or
[Throttle::acquire_blocking] (sync, sleeps the calling
thread until the bucket has enough). The bucket caps the
long-run rate at refill_per_sec tokens per second while
still allowing short bursts up to capacity tokens.
This crate intentionally has no async runtime dependency. The
dynomite crate wraps [Throttle] in a tokio-aware adapter
that uses tokio::time::sleep instead of
[std::thread::sleep] in the wait loop. The two layers share
the same algorithm and the same invariants.
Loom support
Under RUSTFLAGS='--cfg loom' the atomics and mutex used by
[Throttle] are sourced from the [loom] crate's
shadow-std modules. This lets a model checker explore every
legal interleaving of the CAS loop in
[Throttle::try_acquire] and verify that no interleaving
over-grants tokens. The clock is abstracted behind the
[Clock] trait so loom tests can drive a deterministic
[ManualClock] rather than wall-clock time.
Examples
use throttle_core::Throttle;
let t: Throttle = Throttle::new(8, 4); // burst 8, sustain 4 tokens/sec
assert!(t.try_acquire(8)); // burst the whole bucket
assert!(!t.try_acquire(8)); // empty now, fast-fail
The example uses no_run because the actual atomics under
RUSTFLAGS='--cfg loom' require a loom::model() context.
Behavioural correctness is exercised by the in-crate unit
tests and the integration tests under tests/.