pub struct DebouncedTimeout<T> { /* private fields */ }Expand description
A Stop wrapper that debounces the Instant::now() call.
The first two checks read the clock, and the interval between them sets
N; after that, check() reads the clock every N calls, where N is chosen
so clock reads happen approximately once per
target_interval, and is at
most 64 (see Deadline precision).
Adaptation behavior:
- If calls slow down (longer between checks), increases check frequency at the next clock read, to avoid missing the deadline.
- If calls speed up (shorter between checks), gradually decreases check frequency to avoid over-checking.
clear_calibrationstarts over from two checks, for code that knows its checks are about to change pace, such as at the start of a slower stage.
§Deadline precision
It reads the clock about once per target interval (default 100μs), and
at least every 64 checks. While checks arrive at a steady rate it stops at
most about one target interval late, or one check late when checks come
further apart than that. It learns that checks have slowed
down only at its next clock read, so after a sudden slowdown (a library
moving on to a slower stage, say) it can stop up to 64 of the slower
checks late: 64 ms if they then come once a millisecond. WithTimeout
stops at most one check late. Where the code holding the timeout knows
the pace is about to change, clear_calibration
makes it notice within two checks. If you need sub-100μs precision, either
lower the target with
with_target_interval or use
WithTimeout directly.
§When to Use
Prefer DebouncedTimeout over WithTimeout when
the Stop implementation crosses an API boundary — the caller adds the
deadline, but the library controls check frequency. This avoids coupling
timeout overhead to the library’s internal loop structure.
In tight loops (sub-microsecond between checks), the savings are ~10x. In codec workloads (~4KB between checks), both types are equivalent because the real work dwarfs the clock read.
§Example
use almost_enough::{StopSource, Stop};
use almost_enough::time::DebouncedTimeout;
use std::time::Duration;
let source = StopSource::new();
let stop = DebouncedTimeout::new(source.as_ref(), Duration::from_millis(100));
// Fast loop — most check() calls skip the clock read
let mut i = 0u64;
while !stop.should_stop() {
i += 1;
if i > 1_000_000 { break; }
}Implementations§
Source§impl<T: Stop> DebouncedTimeout<T>
impl<T: Stop> DebouncedTimeout<T>
Sourcepub fn new(inner: T, duration: Duration) -> Self
pub fn new(inner: T, duration: Duration) -> Self
Create a new debounced timeout with the default target interval (100μs).
The deadline is calculated as Instant::now() + duration.
Durations longer than ~584 years are clamped to u64::MAX nanoseconds.
Sourcepub fn with_deadline(inner: T, deadline: Instant) -> Self
pub fn with_deadline(inner: T, deadline: Instant) -> Self
Create a debounced timeout with an absolute deadline.
If the deadline is in the past, the first clock read will trigger
StopReason::TimedOut.
Sourcepub fn with_target_interval(self, interval: Duration) -> Self
pub fn with_target_interval(self, interval: Duration) -> Self
Set the target interval between clock reads.
Smaller values check the clock more often (more responsive but more overhead). Larger values check less often (less overhead but may overshoot the deadline by up to this amount while checks arrive steadily). The clock is read at least every 64 checks regardless.
Default: 100μs (0.1ms).
Sourcepub fn remaining(&self) -> Duration
pub fn remaining(&self) -> Duration
Get the remaining time until deadline.
Returns Duration::ZERO if the deadline has passed.
Sourcepub fn into_inner(self) -> T
pub fn into_inner(self) -> T
Unwrap and return the inner stop.
Sourcepub fn checks_per_clock_read(&self) -> u32
pub fn checks_per_clock_read(&self) -> u32
Current number of check() calls between clock reads.
Starts at 1 (every call), is set from the interval between the first two checks, and then adapts as the call rate is measured, up to 64. Useful for diagnostics and testing.
Sourcepub fn clear_calibration(&self)
pub fn clear_calibration(&self)
Forget the measured check rate: the next two checks read the clock, and the interval between them sets how often it is read after that.
Call it where checks are about to change pace, such as when a library moves on to a slower stage. Without it, a slowdown is noticed at the next scheduled clock read, up to 64 of the slower checks later.
Source§impl<T: Stop> DebouncedTimeout<T>
impl<T: Stop> DebouncedTimeout<T>
Sourcepub fn tighten(self, duration: Duration) -> Self
pub fn tighten(self, duration: Duration) -> Self
Add another timeout, taking the tighter of the two deadlines.
Resets calibration state since the new deadline may require a different check frequency.
Sourcepub fn tighten_deadline(self, deadline: Instant) -> Self
pub fn tighten_deadline(self, deadline: Instant) -> Self
Add another deadline, taking the earlier of the two.
Resets calibration state since the new deadline may require a different check frequency.
Trait Implementations§
Source§impl<T: Debug> Debug for DebouncedTimeout<T>
impl<T: Debug> Debug for DebouncedTimeout<T>
Source§impl<T: Stop> Stop for DebouncedTimeout<T>
impl<T: Stop> Stop for DebouncedTimeout<T>
Auto Trait Implementations§
impl<T> !Freeze for DebouncedTimeout<T>
impl<T> RefUnwindSafe for DebouncedTimeout<T>where
T: RefUnwindSafe,
impl<T> Send for DebouncedTimeout<T>where
T: Send,
impl<T> Sync for DebouncedTimeout<T>where
T: Sync,
impl<T> Unpin for DebouncedTimeout<T>where
T: Unpin,
impl<T> UnsafeUnpin for DebouncedTimeout<T>where
T: UnsafeUnpin,
impl<T> UnwindSafe for DebouncedTimeout<T>where
T: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<T> CloneStop for T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> DebouncedTimeoutExt for Twhere
T: Stop,
impl<T> DebouncedTimeoutExt for Twhere
T: Stop,
Source§fn with_debounced_timeout(self, duration: Duration) -> DebouncedTimeout<Self>
fn with_debounced_timeout(self, duration: Duration) -> DebouncedTimeout<Self>
Source§fn with_debounced_deadline(self, deadline: Instant) -> DebouncedTimeout<Self>
fn with_debounced_deadline(self, deadline: Instant) -> DebouncedTimeout<Self>
Source§impl<T> StopExt for Twhere
T: Stop,
impl<T> StopExt for Twhere
T: Stop,
Source§fn or<S: Stop>(self, other: S) -> OrStop<Self, S>
fn or<S: Stop>(self, other: S) -> OrStop<Self, S>
Source§fn into_boxed(self) -> BoxedStopwhere
Self: 'static,
fn into_boxed(self) -> BoxedStopwhere
Self: 'static,
use into_token(), whose StopToken checks the same way and is Clone
BoxedStop. Deprecated: use
into_token(), whose StopToken checks the
same way and is Clone.