Add interval api. (#41)
* Add interval api. * Fix workflows. * Address review comments. * Don't tick immediately. * Increase allowed jitter for ci.
This commit is contained in:
parent
1f860a9525
commit
75316e2982
|
@ -16,14 +16,14 @@ jobs:
|
||||||
rust: [nightly, beta, stable, 1.39.0]
|
rust: [nightly, beta, stable, 1.39.0]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Set current week of the year in environnement
|
- name: Set current week of the year in environnement
|
||||||
if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macOS')
|
if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macOS')
|
||||||
run: echo "::set-env name=CURRENT_WEEK::$(date +%V)"
|
run: echo "CURRENT_WEEK=$(date +%V)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Set current week of the year in environnement
|
- name: Set current week of the year in environnement
|
||||||
if: startsWith(matrix.os, 'windows')
|
if: startsWith(matrix.os, 'windows')
|
||||||
run: echo "::set-env name=CURRENT_WEEK::$(Get-Date -UFormat %V)"
|
run: echo "CURRENT_WEEK=$(Get-Date -UFormat %V)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Install latest ${{ matrix.rust }}
|
- name: Install latest ${{ matrix.rust }}
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
|
|
|
@ -11,10 +11,10 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Set current week of the year in environnement
|
- name: Set current week of the year in environnement
|
||||||
run: echo "::set-env name=CURRENT_WEEK::$(date +%V)"
|
run: echo "CURRENT_WEEK=$(date +%V)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
|
|
|
@ -11,10 +11,10 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Set current week of the year in environnement
|
- name: Set current week of the year in environnement
|
||||||
run: echo "::set-env name=CURRENT_WEEK::$(date +%V)"
|
run: echo "CURRENT_WEEK=$(date +%V)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- uses: actions-rs/audit-check@v1
|
- uses: actions-rs/audit-check@v1
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
80
src/lib.rs
80
src/lib.rs
|
@ -85,6 +85,11 @@ mod reactor;
|
||||||
|
|
||||||
pub use driver::block_on;
|
pub use driver::block_on;
|
||||||
|
|
||||||
|
/// Use Duration::MAX once duration_constants are stabilized.
|
||||||
|
fn duration_max() -> Duration {
|
||||||
|
Duration::new(u64::MAX, 1_000_000_000 - 1)
|
||||||
|
}
|
||||||
|
|
||||||
/// A future that expires at a point in time.
|
/// A future that expires at a point in time.
|
||||||
///
|
///
|
||||||
/// Timers are futures that output the [`Instant`] at which they fired.
|
/// Timers are futures that output the [`Instant`] at which they fired.
|
||||||
|
@ -127,6 +132,9 @@ pub struct Timer {
|
||||||
|
|
||||||
/// When this timer fires.
|
/// When this timer fires.
|
||||||
when: Instant,
|
when: Instant,
|
||||||
|
|
||||||
|
/// The period.
|
||||||
|
period: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Timer {
|
impl Timer {
|
||||||
|
@ -161,10 +169,8 @@ impl Timer {
|
||||||
/// # });
|
/// # });
|
||||||
/// ```
|
/// ```
|
||||||
pub fn at(instant: Instant) -> Timer {
|
pub fn at(instant: Instant) -> Timer {
|
||||||
Timer {
|
// Use Duration::MAX once duration_constants are stabilized.
|
||||||
id_and_waker: None,
|
Timer::interval_at(instant, duration_max())
|
||||||
when: instant,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the timer to expire after the new duration of time.
|
/// Sets the timer to expire after the new duration of time.
|
||||||
|
@ -222,6 +228,47 @@ impl Timer {
|
||||||
*id = Reactor::get().insert_timer(self.when, waker);
|
*id = Reactor::get().insert_timer(self.when, waker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a timer that ticks every period.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use async_io::Timer;
|
||||||
|
/// use futures_lite::StreamExt;
|
||||||
|
/// use std::time::{Duration, Instant};
|
||||||
|
///
|
||||||
|
/// # futures_lite::future::block_on(async {
|
||||||
|
/// let period = Duration::from_secs(1);
|
||||||
|
/// Timer::interval(period).next().await;
|
||||||
|
/// # });
|
||||||
|
/// ```
|
||||||
|
pub fn interval(period: Duration) -> Timer {
|
||||||
|
Timer::interval_at(Instant::now() + period, period)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a timer that ticks every period, starting at `start`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use async_io::Timer;
|
||||||
|
/// use futures_lite::StreamExt;
|
||||||
|
/// use std::time::{Duration, Instant};
|
||||||
|
///
|
||||||
|
/// # futures_lite::future::block_on(async {
|
||||||
|
/// let now = Instant::now();
|
||||||
|
/// let period = Duration::from_secs(1);
|
||||||
|
/// Timer::interval_at(now, period).next().await;
|
||||||
|
/// # });
|
||||||
|
/// ```
|
||||||
|
pub fn interval_at(start: Instant, period: Duration) -> Timer {
|
||||||
|
Timer {
|
||||||
|
id_and_waker: None,
|
||||||
|
when: start,
|
||||||
|
period: period,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Timer {
|
impl Drop for Timer {
|
||||||
|
@ -236,14 +283,33 @@ impl Drop for Timer {
|
||||||
impl Future for Timer {
|
impl Future for Timer {
|
||||||
type Output = Instant;
|
type Output = Instant;
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
match self.poll_next(cx) {
|
||||||
|
Poll::Ready(Some(when)) => Poll::Ready(when),
|
||||||
|
Poll::Pending => Poll::Pending,
|
||||||
|
Poll::Ready(None) => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stream for Timer {
|
||||||
|
type Item = Instant;
|
||||||
|
|
||||||
|
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
// Check if the timer has already fired.
|
// Check if the timer has already fired.
|
||||||
if Instant::now() >= self.when {
|
if Instant::now() >= self.when {
|
||||||
if let Some((id, _)) = self.id_and_waker.take() {
|
if let Some((id, _)) = self.id_and_waker.take() {
|
||||||
// Deregister the timer from the reactor.
|
// Deregister the timer from the reactor.
|
||||||
Reactor::get().remove_timer(self.when, id);
|
Reactor::get().remove_timer(self.when, id);
|
||||||
}
|
}
|
||||||
Poll::Ready(self.when)
|
let when = self.when;
|
||||||
|
if let Some(next) = when.checked_add(self.period) {
|
||||||
|
self.when = next;
|
||||||
|
// Register the timer in the reactor.
|
||||||
|
let id = Reactor::get().insert_timer(self.when, cx.waker());
|
||||||
|
self.id_and_waker = Some((id, cx.waker().clone()));
|
||||||
|
}
|
||||||
|
return Poll::Ready(Some(when));
|
||||||
} else {
|
} else {
|
||||||
match &self.id_and_waker {
|
match &self.id_and_waker {
|
||||||
None => {
|
None => {
|
||||||
|
@ -261,8 +327,8 @@ impl Future for Timer {
|
||||||
}
|
}
|
||||||
Some(_) => {}
|
Some(_) => {}
|
||||||
}
|
}
|
||||||
Poll::Pending
|
|
||||||
}
|
}
|
||||||
|
Poll::Pending
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use async_io::Timer;
|
use async_io::Timer;
|
||||||
use futures_lite::{future, FutureExt};
|
use futures_lite::{future, FutureExt, StreamExt};
|
||||||
|
|
||||||
fn spawn<T: Send + 'static>(
|
fn spawn<T: Send + 'static>(
|
||||||
f: impl Future<Output = T> + Send + 'static,
|
f: impl Future<Output = T> + Send + 'static,
|
||||||
|
@ -30,6 +30,22 @@ fn smoke() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn interval() {
|
||||||
|
future::block_on(async {
|
||||||
|
let period = Duration::from_secs(1);
|
||||||
|
let jitter = Duration::from_millis(500);
|
||||||
|
let start = Instant::now();
|
||||||
|
let mut timer = Timer::interval(period);
|
||||||
|
timer.next().await;
|
||||||
|
let elapsed = start.elapsed();
|
||||||
|
assert!(elapsed >= period && elapsed - period < jitter);
|
||||||
|
timer.next().await;
|
||||||
|
let elapsed = start.elapsed();
|
||||||
|
assert!(elapsed >= period * 2 && elapsed - period * 2 < jitter);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn poll_across_tasks() {
|
fn poll_across_tasks() {
|
||||||
future::block_on(async {
|
future::block_on(async {
|
||||||
|
|
Loading…
Reference in New Issue