├── .cirrus.yml ├── .gitignore ├── .travis.yml ├── Cargo.toml ├── README.md ├── src ├── delay.rs ├── interval.rs ├── lib.rs └── sys │ ├── kqueue.rs │ ├── mod.rs │ └── timerfd.rs └── tests ├── delay.rs └── interval.rs /.cirrus.yml: -------------------------------------------------------------------------------- 1 | task: 2 | name: stable-x86_64-unknown-freebsd 3 | freebsd_instance: 4 | matrix: 5 | - image: freebsd-12-0-release-amd64 6 | - image: freebsd-11-2-release-amd64 7 | env: 8 | RUST_BACKTRACE: 1 9 | setup_script: 10 | - pkg install -y curl git 11 | - curl https://sh.rustup.rs -sSf --output rustup.sh 12 | - sh rustup.sh -y 13 | - . $HOME/.cargo/env 14 | test_script: 15 | - . $HOME/.cargo/env 16 | - cargo test --no-fail-fast --verbose --all 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: cargo 3 | rust: 4 | - stable 5 | - beta 6 | - nightly 7 | 8 | # always test things that aren't pushes (like PRs) 9 | # never test tags or pushes to non-master branches (wait for PR) 10 | # https://github.com/travis-ci/travis-ci/issues/2200#issuecomment-441395545) 11 | if: type != push OR (tag IS blank AND branch = master) 12 | 13 | # an entry in stage=test will be generated for each rust/os combination. 14 | # each entry will run these commands. 15 | script: 16 | - cargo test 17 | jobs: 18 | allow_failures: 19 | - rust: nightly 20 | - os: windows 21 | include: 22 | - &check 23 | stage: check # do a pre-screen to make sure this is even worth testing 24 | script: cargo check --all-targets 25 | rust: stable 26 | os: linux 27 | - <<: *check # also test oldest known-good stable 28 | rust: 1.31.1 29 | - stage: test # then run the tests 30 | rust: stable 31 | os: osx 32 | - rust: stable 33 | os: windows 34 | - &linux 35 | stage: test 36 | rust: stable 37 | os: linux 38 | - <<: *linux 39 | rust: beta 40 | - <<: *linux 41 | rust: nightly 42 | - stage: lint # we lint on beta to future-proof 43 | name: "Rust: beta, rustfmt" 44 | rust: beta 45 | os: linux 46 | script: 47 | - rustup component add rustfmt-preview 48 | - cargo fmt -v -- --check 49 | - name: "Rust: nightly, rustfmt" # and on nightly with allow_fail 50 | rust: nightly 51 | os: linux 52 | script: 53 | - rustup component add rustfmt-preview 54 | - cargo fmt -v -- --check 55 | - name: "Rust: beta, clippy" 56 | rust: beta 57 | os: linux 58 | script: 59 | - rustup component add clippy-preview 60 | - touch ./src/lib.rs && cargo clippy -- -D warnings 61 | - name: "Rust: nightly, clippy" 62 | rust: nightly 63 | os: linux 64 | script: 65 | - rustup component add clippy-preview 66 | - touch ./src/lib.rs && cargo clippy -- -D warnings 67 | stages: 68 | - check 69 | - test 70 | - lint 71 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tokio-os-timer" 3 | version = "0.1.8" 4 | authors = ["Jon Gjengset "] 5 | edition = "2018" 6 | license = "MIT" 7 | homepage = "https://github.com/jonhoo/tokio-os-timer" 8 | repository = "https://github.com/jonhoo/tokio-os-timer" 9 | description = "Timer facilities for Tokio based on OS-level primitives." 10 | categories = ["asynchronous", "date-and-time"] 11 | readme = "README.md" 12 | 13 | [badges] 14 | travis-ci = { repository = "jonhoo/tokio-os-timer" } 15 | cirrus-ci = { repository = "jonhoo/tokio-os-timer" } 16 | maintenance = { status = "deprecated" } 17 | 18 | [dependencies] 19 | libc = "0.2" 20 | mio = "0.6" 21 | tokio-reactor = "0.1" 22 | futures = "0.1" 23 | nix = "0.14.0" 24 | 25 | [dev-dependencies] 26 | tokio-mock-task = "0.1.0" 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Crates.io](https://img.shields.io/crates/v/tokio-os-timer.svg)](https://crates.io/crates/tokio-os-timer) 2 | [![Documentation](https://docs.rs/tokio-os-timer/badge.svg)](https://docs.rs/tokio-os-timer/) 3 | [![Travis Build Status](https://travis-ci.com/jonhoo/tokio-os-timer.svg?branch=master)](https://travis-ci.com/jonhoo/tokio-os-timer) 4 | [![Cirrus CI Build Status](https://api.cirrus-ci.com/github/jonhoo/tokio-os-timer.svg)](https://cirrus-ci.com/github/jonhoo/tokio-os-timer) 5 | 6 | **This crate is deprecated. Please use 7 | [`async-timer`](https://github.com/DoumanAsh/async-timer) with the 8 | `tokio_on` feature enabled instead.** 9 | 10 | This crate provides timers for use with tokio that rely on OS mechanisms 11 | for timer management rather than a separate timing mechanism like 12 | [`tokio-timer`]. This comes at somewhat increased overhead if you have 13 | many timers, but allows the timers to have any granularity supported by 14 | your operating system where `tokio-timer` can only support timers with a 15 | granularity of 1ms. In particular, the system timers usually support 16 | whatever granularity the underlying hardware supports (see 17 | "High-resolution timers" in [`time(7)`]), which on my laptop is 1ns! 18 | Realistically, you won't be able to make your timers higher resolution 19 | than how long system calls on your system take, which is usually on the 20 | order of hundreds of nanoseconds. 21 | 22 | ## Platform support 23 | 24 | The current implementation uses [`timerfd_create(2)`] on Linux, and 25 | [`kqueue(2)` timers] on macOS and BSDs. Windows support is sadly 26 | unlikely to appear 27 | ([#9](https://github.com/jonhoo/tokio-os-timer/issues/9)). 28 | 29 | [`tokio-timer`]: https://docs.rs/tokio-timer/ 30 | [`timerfd_create(2)`]: https://linux.die.net/man/2/timerfd_settime 31 | [`kqueue(2)` timers]: https://man.openbsd.org/kqueue.2 32 | [`time(7)`]: https://linux.die.net/man/7/time 33 | -------------------------------------------------------------------------------- /src/delay.rs: -------------------------------------------------------------------------------- 1 | use crate::sys::{TimeSpec, Timer}; 2 | use futures::{try_ready, Async, Future, Poll}; 3 | use std::io; 4 | use std::time::Duration; 5 | 6 | /// A future that completes a specified amount of time from its creation. 7 | /// 8 | /// Instances of `Delay` perform no work and complete with `()` once the specified duration has been passed. 9 | pub struct Delay { 10 | e: Option, 11 | } 12 | 13 | impl Delay { 14 | /// Create a new `Delay` instance that elapses at now + `delay`. 15 | #[deprecated(since = "0.1.8", note = "Please use the async-timer crate")] 16 | pub fn new(delay: Duration) -> io::Result { 17 | if delay.as_secs() == 0 && delay.subsec_nanos() == 0 { 18 | // this would be interpreted as "inactive timer" by timerfd_settime 19 | return Ok(Self { e: None }); 20 | } 21 | 22 | let mut timer = Timer::new()?; 23 | 24 | // arm the timer 25 | timer.set(TimeSpec::Timeout(delay))?; 26 | 27 | Ok(Self { e: Some(timer) }) 28 | } 29 | } 30 | 31 | impl Future for Delay { 32 | type Item = (); 33 | type Error = io::Error; 34 | fn poll(&mut self) -> Poll { 35 | if let Some(ref mut e) = self.e { 36 | try_ready!(e.poll()); 37 | } 38 | Ok(Async::Ready(())) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/interval.rs: -------------------------------------------------------------------------------- 1 | use crate::sys::{TimeSpec, Timer}; 2 | use futures::{try_ready, Async, Poll, Stream}; 3 | use std::io; 4 | use std::time::Duration; 5 | 6 | /// A stream that yields once every time a fixed amount of time elapses. 7 | /// 8 | /// Instances of `Interval` perform no work. 9 | pub struct Interval { 10 | e: Option, 11 | } 12 | 13 | impl Interval { 14 | /// Create a new `Interval` instance that yields at now + `interval`, and every subsequent 15 | /// `interval`. 16 | #[deprecated(since = "0.1.8", note = "Please use the async-timer crate")] 17 | pub fn new(interval: Duration) -> io::Result { 18 | if interval.as_secs() == 0 && interval.subsec_nanos() == 0 { 19 | // this would be interpreted as "inactive timer" by timerfd_settime 20 | return Ok(Self { e: None }); 21 | } 22 | 23 | let mut timer = Timer::new()?; 24 | 25 | // arm the timer 26 | timer.set(TimeSpec::Interval(interval))?; 27 | 28 | Ok(Self { e: Some(timer) }) 29 | } 30 | } 31 | 32 | impl Stream for Interval { 33 | type Item = (); 34 | type Error = io::Error; 35 | fn poll(&mut self) -> Poll, Self::Error> { 36 | if let Some(ref mut e) = self.e { 37 | try_ready!(e.poll()); 38 | } 39 | Ok(Async::Ready(Some(()))) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides timers for use with tokio that rely on OS mechanisms for timer management 2 | //! rather than a separate timing mechanism like [`tokio-timer`]. This comes at somewhat increased 3 | //! overhead if you have many timers, but allows the timers to have any granularity supported by 4 | //! your operating system where `tokio-timer` can only support timers with a granularity of 1ms. 5 | //! In particular, the system timers usually support whatever granularity the underlying hardware 6 | //! supports (see "High-resolution timers" in [`time(7)`]), which on my laptop is 1ns! 7 | //! Realistically, you won't be able to make your timers higher resolution than how long system 8 | //! calls on your system take, which is usually on the order of hundreds of nanoseconds. 9 | //! 10 | //! The current implementation uses [`timerfd_create(2)`] on Linux, and [`kqueue(2)` timers] on 11 | //! macOS and BSDs. 12 | //! 13 | //! [`tokio-timer`]: https://docs.rs/tokio-timer/ 14 | //! [`timerfd_create(2)`]: https://linux.die.net/man/2/timerfd_settime 15 | //! [`kqueue(2)` timers]: https://man.openbsd.org/kqueue.2 16 | //! [`time(7)`]: https://linux.die.net/man/7/time 17 | 18 | #![deny(missing_docs)] 19 | 20 | mod delay; 21 | pub use self::delay::Delay; 22 | 23 | mod interval; 24 | pub use self::interval::Interval; 25 | 26 | mod sys; 27 | -------------------------------------------------------------------------------- /src/sys/kqueue.rs: -------------------------------------------------------------------------------- 1 | use super::TimeSpec; 2 | use mio::{unix::EventedFd, Poll, PollOpt, Ready, Token}; 3 | use nix::sys::event::*; 4 | use std::io; 5 | use std::os::unix::io::RawFd; 6 | 7 | pub(crate) struct Timer(RawFd); 8 | 9 | impl Timer { 10 | pub(crate) fn new() -> io::Result { 11 | let kq = kqueue().map_err(|e| e.as_errno().unwrap())?; 12 | Ok(Timer(kq)) 13 | } 14 | 15 | pub(crate) fn set(&mut self, timer: TimeSpec) -> io::Result<()> { 16 | let mut flags = EventFlag::EV_ADD | EventFlag::EV_ENABLE; 17 | if let TimeSpec::Timeout(..) = timer { 18 | flags |= EventFlag::EV_ONESHOT; 19 | } 20 | 21 | let time = match timer { 22 | TimeSpec::Timeout(d) | TimeSpec::Interval(d) => d, 23 | }; 24 | 25 | // We need to decide what time unit we want... 26 | // We want the smallest unit that we can use without overflow, so: 27 | let mut unit = FilterFlag::NOTE_NSECONDS; 28 | let mut time = time.as_nanos(); 29 | if time > isize::max_value() as u128 { 30 | unit = FilterFlag::NOTE_USECONDS; 31 | time /= 1_000; 32 | } 33 | if time > isize::max_value() as u128 { 34 | unit = FilterFlag::empty(); // default is milliseconds 35 | time /= 1_000; 36 | } 37 | if time > isize::max_value() as u128 { 38 | unit = FilterFlag::NOTE_SECONDS; 39 | time /= 1_000; 40 | } 41 | let time = time as isize; 42 | 43 | kevent( 44 | self.0, 45 | &[KEvent::new( 46 | 1, 47 | EventFilter::EVFILT_TIMER, 48 | flags, 49 | unit, 50 | time, 51 | 0, 52 | )], 53 | &mut [], 54 | 0, 55 | ) 56 | .map_err(|e| e.as_errno().unwrap())?; 57 | 58 | Ok(()) 59 | } 60 | 61 | pub(crate) fn check(&mut self) -> io::Result<()> { 62 | let mut ev = [KEvent::new( 63 | 0, 64 | EventFilter::EVFILT_TIMER, 65 | EventFlag::empty(), 66 | FilterFlag::empty(), 67 | 0, 68 | 0, 69 | )]; 70 | match kevent(self.0, &[], &mut ev[..], 0).map_err(|e| e.as_errno().unwrap())? { 71 | 1 => { 72 | // timer fired! 73 | assert_eq!(ev[0].ident(), 1); 74 | Ok(()) 75 | } 76 | 0 => { 77 | // timer has not fired? 78 | Err(io::Error::new( 79 | io::ErrorKind::WouldBlock, 80 | "no timer kevents", 81 | )) 82 | } 83 | n => unreachable!("somehow got {} events when waiting for at most 1", n), 84 | } 85 | } 86 | } 87 | 88 | impl mio::Evented for Timer { 89 | fn register( 90 | &self, 91 | poll: &Poll, 92 | token: Token, 93 | interest: Ready, 94 | opts: PollOpt, 95 | ) -> io::Result<()> { 96 | let interest = if interest.contains(Ready::readable()) { 97 | Ready::readable() 98 | } else { 99 | Ready::empty() 100 | }; 101 | EventedFd(&self.0).register(poll, token, interest, opts) 102 | } 103 | 104 | fn reregister( 105 | &self, 106 | poll: &Poll, 107 | token: Token, 108 | interest: Ready, 109 | opts: PollOpt, 110 | ) -> io::Result<()> { 111 | let interest = if interest.contains(Ready::readable()) { 112 | Ready::readable() 113 | } else { 114 | Ready::empty() 115 | }; 116 | EventedFd(&self.0).reregister(poll, token, interest, opts) 117 | } 118 | 119 | fn deregister(&self, poll: &Poll) -> io::Result<()> { 120 | EventedFd(&self.0).deregister(poll) 121 | } 122 | } 123 | 124 | impl Drop for Timer { 125 | fn drop(&mut self) { 126 | let _ = nix::unistd::close(self.0); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/sys/mod.rs: -------------------------------------------------------------------------------- 1 | use futures::{try_ready, Async, Poll}; 2 | use std::io; 3 | use std::time::Duration; 4 | use tokio_reactor::Registration; 5 | 6 | #[cfg(any( 7 | target_os = "bitrig", 8 | target_os = "dragonfly", 9 | target_os = "freebsd", 10 | target_os = "ios", 11 | target_os = "macos", 12 | target_os = "netbsd", 13 | target_os = "openbsd" 14 | ))] 15 | mod kqueue; 16 | 17 | #[cfg(any( 18 | target_os = "bitrig", 19 | target_os = "dragonfly", 20 | target_os = "freebsd", 21 | target_os = "ios", 22 | target_os = "macos", 23 | target_os = "netbsd", 24 | target_os = "openbsd" 25 | ))] 26 | use self::kqueue::Timer as SysTimer; 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android", target_os = "solaris"))] 29 | mod timerfd; 30 | 31 | #[cfg(any(target_os = "linux", target_os = "android", target_os = "solaris"))] 32 | use self::timerfd::Timer as SysTimer; 33 | 34 | pub(crate) enum TimeSpec { 35 | Timeout(Duration), 36 | Interval(Duration), 37 | } 38 | 39 | pub(crate) struct Timer { 40 | r: Registration, 41 | t: SysTimer, 42 | } 43 | 44 | impl Timer { 45 | pub(crate) fn new() -> io::Result { 46 | let t = Timer { 47 | r: Registration::new(), 48 | t: SysTimer::new()?, 49 | }; 50 | t.r.register(&t.t)?; 51 | Ok(t) 52 | } 53 | 54 | pub(crate) fn set(&mut self, timer: TimeSpec) -> io::Result<()> { 55 | self.t.set(timer) 56 | } 57 | 58 | pub(crate) fn poll(&mut self) -> Poll<(), io::Error> { 59 | let r = try_ready!(self.r.poll_read_ready()); 60 | if !r.is_readable() { 61 | return Ok(Async::NotReady); 62 | } 63 | 64 | // make sure to also check the timer in case of spurious wakeups 65 | match self.t.check() { 66 | Ok(_) => Ok(Async::Ready(())), 67 | Err(err) => { 68 | if err.kind() == io::ErrorKind::WouldBlock { 69 | return Ok(Async::NotReady); 70 | } 71 | Err(err) 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/sys/timerfd.rs: -------------------------------------------------------------------------------- 1 | use super::TimeSpec; 2 | use mio::{unix::EventedFd, Poll, PollOpt, Ready, Token}; 3 | use std::io; 4 | use std::os::unix::io::RawFd; 5 | 6 | pub(crate) struct Timer(RawFd); 7 | 8 | impl Timer { 9 | pub(crate) fn new() -> io::Result { 10 | let tfd = unsafe { libc::timerfd_create(libc::CLOCK_MONOTONIC, libc::TFD_NONBLOCK) }; 11 | if tfd == -1 { 12 | Err(io::Error::last_os_error()) 13 | } else { 14 | Ok(Timer(tfd)) 15 | } 16 | } 17 | 18 | pub(crate) fn set(&mut self, timer: TimeSpec) -> io::Result<()> { 19 | let timer = match timer { 20 | TimeSpec::Timeout(delay) => libc::itimerspec { 21 | it_interval: libc::timespec { 22 | tv_sec: 0, 23 | tv_nsec: 0, 24 | }, 25 | it_value: libc::timespec { 26 | tv_sec: delay.as_secs() as i64, 27 | tv_nsec: i64::from(delay.subsec_nanos()), 28 | }, 29 | }, 30 | TimeSpec::Interval(interval) => libc::itimerspec { 31 | // first expiry 32 | it_value: libc::timespec { 33 | tv_sec: interval.as_secs() as i64, 34 | tv_nsec: i64::from(interval.subsec_nanos()), 35 | }, 36 | // subsequent expiry intervals 37 | it_interval: libc::timespec { 38 | tv_sec: interval.as_secs() as i64, 39 | tv_nsec: i64::from(interval.subsec_nanos()), 40 | }, 41 | }, 42 | }; 43 | 44 | let ret = unsafe { libc::timerfd_settime(self.0, 0, &timer, std::ptr::null_mut()) }; 45 | if ret == -1 { 46 | Err(io::Error::last_os_error()) 47 | } else { 48 | Ok(()) 49 | } 50 | } 51 | 52 | pub(crate) fn check(&mut self) -> io::Result<()> { 53 | let mut buf = [0; 8]; 54 | let ret = unsafe { libc::read(self.0, buf.as_mut().as_mut_ptr() as *mut _, 8) }; 55 | if ret == -1 { 56 | Err(io::Error::last_os_error()) 57 | } else { 58 | Ok(()) 59 | } 60 | } 61 | } 62 | 63 | impl mio::Evented for Timer { 64 | fn register( 65 | &self, 66 | poll: &Poll, 67 | token: Token, 68 | interest: Ready, 69 | opts: PollOpt, 70 | ) -> io::Result<()> { 71 | EventedFd(&self.0).register(poll, token, interest, opts) 72 | } 73 | 74 | fn reregister( 75 | &self, 76 | poll: &Poll, 77 | token: Token, 78 | interest: Ready, 79 | opts: PollOpt, 80 | ) -> io::Result<()> { 81 | EventedFd(&self.0).reregister(poll, token, interest, opts) 82 | } 83 | 84 | fn deregister(&self, poll: &Poll) -> io::Result<()> { 85 | EventedFd(&self.0).deregister(poll) 86 | } 87 | } 88 | 89 | impl Drop for Timer { 90 | fn drop(&mut self) { 91 | let _ = nix::unistd::close(self.0); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /tests/delay.rs: -------------------------------------------------------------------------------- 1 | // heavily copied from https://github.com/tokio-rs/tokio/blob/master/tokio-timer/tests/delay.rs 2 | 3 | use futures::Future; 4 | use std::time::Duration; 5 | use tokio_os_timer::Delay; 6 | 7 | macro_rules! assert_ready { 8 | ($f:expr) => {{ 9 | use ::futures::Async::*; 10 | 11 | match $f.poll().unwrap() { 12 | Ready(v) => v, 13 | NotReady => panic!("NotReady"), 14 | } 15 | }}; 16 | ($f:expr, $($msg:expr),+) => {{ 17 | use ::futures::Async::*; 18 | 19 | match $f.poll().unwrap() { 20 | Ready(v) => v, 21 | NotReady => { 22 | let msg = format!($($msg),+); 23 | panic!("NotReady; {}", msg) 24 | } 25 | } 26 | }} 27 | } 28 | 29 | macro_rules! assert_not_ready { 30 | ($f:expr) => {{ 31 | let res = $f.poll().unwrap(); 32 | assert!(!res.is_ready(), "actual={:?}", res) 33 | }}; 34 | ($f:expr, $($msg:expr),+) => {{ 35 | let res = $f.poll().unwrap(); 36 | if res.is_ready() { 37 | let msg = format!($($msg),+); 38 | panic!("actual={:?}; {}", res, msg); 39 | } 40 | }}; 41 | } 42 | #[test] 43 | fn immediate() { 44 | let mut mock = tokio_mock_task::MockTask::new(); 45 | let mut delay = Delay::new(Duration::new(0, 0)).unwrap(); 46 | mock.enter(|| assert_ready!(delay)); 47 | } 48 | 49 | #[test] 50 | fn delayed() { 51 | let mut mock = tokio_mock_task::MockTask::new(); 52 | let delay = Duration::from_millis(500); 53 | let mut t = Delay::new(delay).unwrap(); 54 | mock.enter(|| assert_not_ready!(t)); 55 | // sleep until a time when delay still hasn't passed 56 | std::thread::sleep(Duration::from_millis(250)); 57 | mock.enter(|| assert_not_ready!(t)); 58 | // sleep until delay _has_ passed 59 | std::thread::sleep(Duration::from_millis(500)); 60 | mock.enter(|| assert_ready!(t)); 61 | } 62 | -------------------------------------------------------------------------------- /tests/interval.rs: -------------------------------------------------------------------------------- 1 | // heavily copied from https://github.com/tokio-rs/tokio/blob/master/tokio-timer/tests/delay.rs 2 | 3 | use futures::Stream; 4 | use std::time::Duration; 5 | use tokio_os_timer::Interval; 6 | 7 | macro_rules! assert_ready { 8 | ($f:expr) => {{ 9 | use ::futures::Async::*; 10 | 11 | match $f.poll().unwrap() { 12 | Ready(v) => v, 13 | NotReady => panic!("NotReady"), 14 | } 15 | }}; 16 | ($f:expr, $($msg:expr),+) => {{ 17 | use ::futures::Async::*; 18 | 19 | match $f.poll().unwrap() { 20 | Ready(v) => v, 21 | NotReady => { 22 | let msg = format!($($msg),+); 23 | panic!("NotReady; {}", msg) 24 | } 25 | } 26 | }} 27 | } 28 | 29 | macro_rules! assert_not_ready { 30 | ($f:expr) => {{ 31 | let res = $f.poll().unwrap(); 32 | assert!(!res.is_ready(), "actual={:?}", res) 33 | }}; 34 | ($f:expr, $($msg:expr),+) => {{ 35 | let res = $f.poll().unwrap(); 36 | if res.is_ready() { 37 | let msg = format!($($msg),+); 38 | panic!("actual={:?}; {}", res, msg); 39 | } 40 | }}; 41 | } 42 | #[test] 43 | fn immediate() { 44 | let mut mock = tokio_mock_task::MockTask::new(); 45 | let mut interval = Interval::new(Duration::new(0, 0)).unwrap(); 46 | assert!(mock.enter(|| assert_ready!(interval)).is_some()); 47 | assert!(mock.enter(|| assert_ready!(interval)).is_some()); 48 | } 49 | 50 | #[test] 51 | fn delayed() { 52 | let mut mock = tokio_mock_task::MockTask::new(); 53 | let interval = Duration::from_millis(500); 54 | let mut t = Interval::new(interval).unwrap(); 55 | mock.enter(|| assert_not_ready!(t)); 56 | // sleep until a time when timer still hasn't expired 57 | std::thread::sleep(Duration::from_millis(250)); 58 | mock.enter(|| assert_not_ready!(t)); 59 | // sleep until interval has passed 60 | std::thread::sleep(Duration::from_millis(500)); 61 | assert!(mock.enter(|| assert_ready!(t)).is_some()); 62 | mock.enter(|| assert_not_ready!(t)); 63 | // sleep until a time when timer still hasn't expired 64 | std::thread::sleep(Duration::from_millis(100)); 65 | mock.enter(|| assert_not_ready!(t)); 66 | // sleep until interval has passed again 67 | std::thread::sleep(Duration::from_millis(300)); 68 | assert!(mock.enter(|| assert_ready!(t)).is_some()); 69 | mock.enter(|| assert_not_ready!(t)); 70 | } 71 | --------------------------------------------------------------------------------