Giter VIP home page Giter VIP logo

async-timer's People

Contributors

doumanash avatar lann avatar lockbox avatar mizz1e avatar udoprog avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

async-timer's Issues

Spurious wakeups with timerfd + Interval

I'm seeing a suspicious number of calls to poll when using the timerfd implementation combined with Interval. In particular, try running

$ cargo t --features tokio_on,stream foobar -- --nocapture

With the following test:

#[cfg(feature = "stream")]
#[tokio::test(threaded_scheduler)]
async fn foobar() {
    use futures_util::stream::StreamExt;

    let mut interval = X(Interval::platform_new(time::Duration::from_millis(2)));

    while let Some(_) = interval.next().await {
        eprintln!("{:?} yield", time::SystemTime::now());
    }
}

use core::future::Future;
use core::{task};
use core::pin::Pin;
use async_timer::oneshot::Oneshot;
struct X<T: Oneshot>(Interval<T>);

#[cfg(feature = "stream")]
impl<T: Oneshot> futures_core::stream::Stream for X<T> {
    type Item = ();

    #[inline]
    fn poll_next(mut self: Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll<Option<Self::Item>> {
        eprintln!("{:?} poll_next", time::SystemTime::now());
        futures_core::Stream::poll_next(Pin::new(&mut self.0), ctx)
    }
}

You'll end up with output like the following:

SystemTime { tv_sec: 1579742737, tv_nsec: 400813292 } yield
SystemTime { tv_sec: 1579742737, tv_nsec: 400838213 } poll_next
SystemTime { tv_sec: 1579742737, tv_nsec: 400874684 } poll_next
SystemTime { tv_sec: 1579742737, tv_nsec: 400905277 } poll_next
SystemTime { tv_sec: 1579742737, tv_nsec: 402863063 } poll_next
SystemTime { tv_sec: 1579742737, tv_nsec: 402892521 } yield

Okay, so a value is yielded at 400813292. Before it yields, the interval restarts the timer. The future is polled again shortly thereafter (~25µs, at 400838213) following the call to next().await. That call polls the timer, finds that it has not expired, and returns Pending. Presumably the next time poll_next should be woken up is ~2ms later (as the Duration passed to Interval::new suggests) when the timer expires. Instead, the future is polled again at 400874684, ~40µs later, and returns Pending again. This happens again at 400905277, another ~30µs later. Only after that third poll_next does the timer appear to "kick in", and the next wakeup to call poll_next does not occur until 402863063 (a delay of ~2ms). At that point the interval stream yields Some and the patterns starts over again.

These spurious wakeups can be quite costly when an Interval is wrapped in a larger Future, since that larger Future's poll method is what is called, and it might also have to check on a bunch of other sub-Futures, since it doesn't know which one was notified.

I'm not sure what is causing this. It does not appear when using the POSIX timer (i.e., if you leave out tokio_on from the --features), so it's got to have something to do with timerfd specifically.

1.0.0: Final rewrite

What needs to be done:

  • async fn friendly API (i.e. to not require context) this means that user should be able use any non-Future APIs without a need to provide context. As side effect it is possible we'll get stale waker, but this is probably unlikely to be a problem.
    Though there still should be API that allows to reset waker
  • Callback API in addition to Future. While it is not difficult to create callback API for user, the crate should provide it out of the box. Ideally internal waker should store callback fn or Waker

Make Interval implement Stream

It's currently pretty inconvenient to use Interval, as you need to keep re-assigning the Interval on completion. It'd be nicer if Interval were a stream that yielded an empty type each time the timer expired. Or perhaps both APIs could be supported?

tokio policy

Need to decide how to handle breaking changes of tokio

With merge of tokio-rs/tokio#2903 we can switch to tokio 0.3 for kevent and tiemrfd timers
But still need to consider if we want to support tokio 0.2

UPD: Waiting to fix bug tokio-rs/tokio#3072

Once decided we need stable 1.0.0

lots of errors for aarch64-linux-android

got this while compiling for aarch64-linux-android

error[E0425]: cannot find function `timerfd_create` in crate `libc`
  --> /data/data/com.termux/files/home/.cargo/registry/src/github.com-1ecc6299db9ec823/async-timer-0.6.1/src/oneshot/timer_fd.rs:17:33
   |
17 |         let fd = unsafe { libc::timerfd_create(libc::CLOCK_MONOTONIC, libc::TFD_NONBLOCK) };
   |                                 ^^^^^^^^^^^^^^ help: a constant with a similar name exists: `SYS_timerfd_create`

error[E0425]: cannot find value `TFD_NONBLOCK` in crate `libc`
  --> /data/data/com.termux/files/home/.cargo/registry/src/github.com-1ecc6299db9ec823/async-timer-0.6.1/src/oneshot/timer_fd.rs:17:77
   |
17 |         let fd = unsafe { libc::timerfd_create(libc::CLOCK_MONOTONIC, libc::TFD_NONBLOCK) };
   |                                                                             ^^^^^^^^^^^^ help: a constant with a similar name exists: `EFD_NONBLOCK`

error[E0412]: cannot find type `itimerspec` in crate `libc`
  --> /data/data/com.termux/files/home/.cargo/registry/src/github.com-1ecc6299db9ec823/async-timer-0.6.1/src/oneshot/timer_fd.rs:23:32
   |
23 |     fn set(&self, timer: libc::itimerspec) {
   |                                ^^^^^^^^^^
help: a struct with a similar name exists
   |
23 |     fn set(&self, timer: libc::timespec) {
   |                                ^^^^^^^^
help: possible candidate is found in another module, you can import it into scope
   |
6  | use crate::oneshot::posix::ffi::itimerspec;
   |

error[E0425]: cannot find function `timerfd_settime` in crate `libc`
  --> /data/data/com.termux/files/home/.cargo/registry/src/github.com-1ecc6299db9ec823/async-timer-0.6.1/src/oneshot/timer_fd.rs:24:34
   |
24 |         let ret = unsafe { libc::timerfd_settime(self.0, 0, &timer, ptr::null_mut()) };
   |                                  ^^^^^^^^^^^^^^^ help: a constant with a similar name exists: `SYS_timerfd_settime`

error[E0422]: cannot find struct, variant or union type `itimerspec` in crate `libc`
  --> /data/data/com.termux/files/home/.cargo/registry/src/github.com-1ecc6299db9ec823/async-timer-0.6.1/src/oneshot/timer_fd.rs:74:27
   |
74 |     let new_value = libc::itimerspec {
   |                           ^^^^^^^^^^
help: a struct with a similar name exists
   |
74 |     let new_value = libc::timespec {
   |                           ^^^^^^^^
help: possible candidate is found in another module, you can import it into scope
   |
6  | use crate::oneshot::posix::ffi::itimerspec;
   |

lol?

thread 'tokio-runtime-worker' panicked at 'Assertion 'fd != -1' failed. OS error 24: Too many open files'

Hi.

I'm trying to create fast variable pwm signal with tokio and got this error.
I suppose that problem is in too many reassignments of interval variable.

Full error code:

thread 'tokio-runtime-worker' panicked at 'Assertion 'fd != -1' failed. OS error 24: Too many open files', /home/mikoma/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-timer-1.0.0-beta.9/src/timer/async_tokio1.rs:51:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: JoinError::Panic(Id(18), ...)', src/main.rs:59:14

And my code example:

use tokio::time::{sleep, Duration, Instant};
use tokio::sync::{Mutex};
use std::sync::Arc;
use async_timer::Interval;

#[tokio::main]
async fn main() {
    let timeout = Arc::new(Mutex::new(500));
    let slope = Arc::new(Mutex::new(1000));
    let stop = Arc::new(Mutex::new(false));
    let text1 = Arc::new(Mutex::new("1"));
    tokio::spawn({
        let stop_clone = Arc::clone(&stop);
        async move {
            sleep(Duration::from_millis(2000)).await;
            *stop_clone.lock().await = true;
        }
    });
    tokio::spawn({
        let timeout_clone = Arc::clone(&timeout);
        let slope_clone = Arc::clone(&slope);
        let stop_clone = Arc::clone(&stop);
        let text1_clone = Arc::clone(&text1);
        async move {
            let mut timeout = *slope_clone.lock().await;
            loop {
                let mut interval = Interval::platform_new(Duration::from_micros(timeout));
                if *stop_clone.lock().await == false {
                    if timeout > *timeout_clone.lock().await {
                        println!("{:?}", text1_clone.lock().await);
                        interval.wait().await;
                        println!("2");
                        interval.wait().await;
                        println!("{:?}", timeout);
                        timeout = timeout - 100;
                    } else {
                        println!("{:?}", text1_clone.lock().await);
                        let now = Instant::now();
                        interval.wait().await;
                        println!("{}|{}", now.elapsed().as_micros(), timeout);
                        println!("2");
                        interval.wait().await;
                        println!("{:?}", timeout);
                    }
                } else {
                    if timeout < *slope_clone.lock().await {
                        println!("{:?}", text1_clone.lock().await);
                        interval.wait().await;
                        println!("2");
                        interval.wait().await;
                        println!("{:?}", timeout);
                        timeout = timeout + 100;
                    } else {
                        break;
                    }
                }
            }
        }
    }).await.unwrap();
}

The behavior of Interval

It seems like the default behavior of Interval in this crate (which is more precise than Tokio's default) is to not account for the time between .await calls. This results in Tokio's Interval being more precise on average with it achieving 8.329971ms which is very close to 8.333ms despite the average deviation tick to tick being higher (this crate got 8.737788ms).

Is it possible for there to at least be an option to account for the time between .await calls?

Tested with 1.0.0-beta.12 and 0.7.4

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.