Add Timer::{reset_at, reset_after}

This commit is contained in:
Yin Guanhao 2020-05-29 12:49:04 +08:00
parent ee25d12cac
commit 94027529a4
3 changed files with 61 additions and 0 deletions

View File

@ -168,6 +168,19 @@ impl Reactor {
}
}
/// Reset a timer to a new Instant.
pub fn reset_timer(&self, when: Instant, id: usize, new: Instant) {
// Push a reset operation.
while self.timer_ops.push(TimerOp::Reset(when, id, new)).is_err() {
// Fire timers to drain the queue.
self.fire_timers();
}
// Notify if the new instant is sooner than the original.
if new < when {
self.timer_event.notify();
}
}
/// Attempts to lock the reactor.
pub fn try_lock(&self) -> Option<ReactorLock<'_>> {
self.events.try_lock().map(|events| {
@ -202,6 +215,11 @@ impl Reactor {
Ok(TimerOp::Remove(when, id)) => {
timers.remove(&(when, id));
}
Ok(TimerOp::Reset(when, id, new)) => {
if let Some(waker) = timers.remove(&(when, id)) {
timers.insert((new, id), waker);
}
}
Err(_) => break,
}
}
@ -312,6 +330,7 @@ impl ReactorLock<'_> {
enum TimerOp {
Insert(Instant, usize, Waker),
Remove(Instant, usize),
Reset(/* original */ Instant, usize, /* new */ Instant),
}
/// A registered source of I/O events.

View File

@ -106,6 +106,22 @@ impl Timer {
let id = None;
Timer { id, when }
}
/// Reset the timer to fire at the specified instant.
pub fn reset_at(&mut self, when: Instant) {
match self.id {
None => {}
Some(id) => {
Reactor::get().reset_timer(self.when, id, when);
self.when = when;
}
}
}
/// Reset timer to fire after the specified duration of time.
pub fn reset_after(&mut self, dur: Duration) {
self.reset_at(Instant::now() + dur);
}
}
impl Drop for Timer {

View File

@ -1,4 +1,6 @@
use futures::prelude::*;
use smol::{self, Timer};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
#[test]
@ -23,3 +25,27 @@ fn timer_after() {
assert!(before.elapsed() >= Duration::from_secs(1));
}
#[test]
fn timer_reset() {
smol::run(async move {
let start = Instant::now();
let t = Arc::new(Mutex::new(Timer::after(Duration::from_secs(10))));
let t1 = t.clone();
// Use another thread instead of smol::Task to avoid any interference
// with the runtime.
std::thread::spawn(move || {
std::thread::sleep(Duration::from_millis(500));
t1.lock().unwrap().reset_after(Duration::from_secs(1));
});
smol::Task::local(futures::future::poll_fn(move |cx| {
t.lock().unwrap().poll_unpin(cx)
}))
.await;
let e = start.elapsed();
assert!(e >= Duration::from_millis(1300));
assert!(e < Duration::from_millis(1700));
});
}