Expose fine-grained time remaining

Fixes #6
master
Jon Gjengset 2018-02-22 13:35:31 -05:00
parent 5afae607ab
commit 4b3316a19e
No known key found for this signature in database
GPG Key ID: D64AC9D67176DC71
1 changed files with 30 additions and 33 deletions

View File

@ -150,45 +150,36 @@ impl<T: Eq + Clone + Hash> TimerHeap<T> {
self.active.remove(&key).is_some() self.active.remove(&key).is_some()
} }
/// Return the amount of time remaining (in ms) for the earliest expiring timer /// Return the amount of time remaining for the earliest expiring timer.
/// Return `None` if there are no timers in the heap /// Return `None` if there are no timers in the heap.
pub fn time_remaining(&self) -> Option<u64> { pub fn time_remaining(&self) -> Option<Duration> {
self._time_remaining(Instant::now()) self._time_remaining(Instant::now())
} }
/// A deterministically testable version of `time_remaining()` /// A deterministically testable version of `time_remaining()`
fn _time_remaining(&self, now: Instant) -> Option<u64> { fn _time_remaining(&self, now: Instant) -> Option<Duration> {
self.timers.iter().find(|e| { self.timers
self.active.get(&e.key) == Some(&e.counter) .iter()
}).map(|e| { .find(|e| self.active.get(&e.key) == Some(&e.counter))
if now > e.expires_at { .map(|e| {
return 0; if now > e.expires_at {
} return Duration::new(0, 0);
let duration = e.expires_at - now; }
// We add a millisecond if there is a fractional ms milliseconds in e.expires_at - now
// duration.subsec_nanos() / 1000000 so that we never fire early. })
let nanos = duration.subsec_nanos() as u64;
// TODO: This can almost certainly be done faster
let subsec_ms = nanos / 1000000;
let mut remaining = duration.as_secs()*1000 + subsec_ms;
if subsec_ms * 1000000 < nanos {
remaining += 1;
}
remaining
})
} }
/// Return the earliest timeout based on a user timeout and the least remaining time in the /// Return the earliest timeout based on a user timeout and the least remaining time in the
/// next timer to fire. /// next timer to fire.
pub fn earliest_timeout(&self, user_timeout_ms: usize) -> usize { pub fn earliest_timeout(&self, user_timeout: Duration) -> Duration {
if let Some(remaining) = self.time_remaining() { if let Some(remaining) = self.time_remaining() {
if user_timeout_ms < remaining as usize { if user_timeout < remaining {
user_timeout_ms user_timeout
} else { } else {
remaining as usize remaining
} }
} else { } else {
user_timeout_ms user_timeout
} }
} }
@ -272,14 +263,20 @@ mod tests {
let mut heap = TimerHeap::new(); let mut heap = TimerHeap::new();
let now = Instant::now(); let now = Instant::now();
let duration = Duration::from_millis(500); let duration = Duration::from_millis(500);
heap._insert(1u64, duration, TimerType::Oneshot, now).unwrap(); heap._insert(1u64, duration, TimerType::Oneshot, now)
assert_matches!(heap._time_remaining(now), Some(500)); .unwrap();
assert_matches!(heap._time_remaining(now + duration), Some(0)); assert_eq!(heap._time_remaining(now), Some(Duration::from_millis(500)));
assert_matches!(heap._time_remaining(now + duration + Duration::from_millis(100)), assert_eq!(
Some(0)); heap._time_remaining(now + duration),
Some(Duration::new(0, 0))
);
assert_eq!(
heap._time_remaining(now + duration + Duration::from_millis(100)),
Some(Duration::new(0, 0))
);
assert_eq!(heap.remove(2), false); assert_eq!(heap.remove(2), false);
assert!(heap.remove(1)); assert!(heap.remove(1));
assert_matches!(heap._time_remaining(now), None); assert_eq!(heap._time_remaining(now), None);
} }
#[test] #[test]