doumanash / async-timer Goto Github PK
View Code? Open in Web Editor NEWTimer facilities for Rust's async story
License: Boost Software License 1.0
Timer facilities for Rust's async story
License: Boost Software License 1.0
We need to wait for inclusion and stabilization of Stream in std.
Ideally we do not want depend on futures
crate at all
Tracking issue: rust-lang/rust#79024
time::Instant::now()
is not available on wasm32-unknown-unknown
so after #21 using Interval
panics, even though it worked fine before
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
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
This line https://github.com/DoumanAsh/async-timer/blob/master/src/oneshot/timer_fd.rs#L90 fails on 32bit target
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();
}
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.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
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?
Some tests show that timer never resolves on very small values with Dispatch Source
API
Should be investigated
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?
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-Future
s, 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.
need to update the tokio version to avoid some of the issues here.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.