Expand description
Clock-agnostic deadlines and timeouts.
reliakit-timeout answers one question: has my time budget run out, and how
much is left? It does not read the clock, sleep, or spawn anything — you
capture a start instant and a budget, then pass now to the query methods.
That makes it usable from sync code, any async runtime, and no_std /
embedded contexts, with deterministic tests.
Time is a plain u64 in any monotonic unit you choose (milliseconds is
typical), matching reliakit-circuit and reliakit-ratelimit. All
arithmetic saturates, so no method panics — not on overflow, and not on a
clock that moves backwards.
Two small types:
Timeoutis a reusable budget that is not yet pinned to a timeline. Configure it once, then callTimeout::startper operation.Deadlineis a budget pinned to a start instant. Query it withremaining,is_expired,check, and friends.
§Example
use reliakit_timeout::{Deadline, Timeout};
// A 30s budget (here in milliseconds), pinned to the start of the operation.
let policy = Timeout::new(30_000);
let deadline = policy.start(1_000); // started at t = 1_000
assert_eq!(deadline.remaining(1_000), 30_000);
assert_eq!(deadline.remaining(21_000), 10_000);
assert!(!deadline.is_expired(30_999));
assert!(deadline.is_expired(31_000)); // expiry is inclusive
// Not yet expired -> Some(remaining); expired -> None.
assert_eq!(deadline.check(21_000), Some(10_000));
assert_eq!(deadline.check(40_000), None);§Composing with backoff
Use Deadline::clamp to keep a retry delay from running past the budget,
and Deadline::is_expired to stop retrying:
use reliakit_timeout::Deadline;
let deadline = Deadline::new(0, 1_000);
let proposed_backoff = 800; // ms the backoff policy wants to wait
let now = 500;
if deadline.is_expired(now) {
// give up
} else {
let wait = deadline.clamp(now, proposed_backoff); // min(800, 500 left) = 500
assert_eq!(wait, 500);
}