├── .gitignore ├── src ├── c_wrapper │ └── posix.c ├── utils.rs ├── timer │ ├── dummy.rs │ ├── web.rs │ ├── win.rs │ ├── mod.rs │ ├── apple.rs │ ├── posix.rs │ └── async_tokio1.rs ├── lib.rs ├── interval.rs ├── timed.rs └── state.rs ├── tests ├── timed.rs ├── timer.rs └── interval.rs ├── Cargo.toml ├── LICENSE ├── .github └── workflows │ └── rust.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | tags 5 | -------------------------------------------------------------------------------- /src/c_wrapper/posix.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef void (*callback)(union sigval); 5 | 6 | timer_t posix_timer(callback cb, void* data) { 7 | timer_t id; 8 | struct sigevent sev = { 9 | .sigev_notify = SIGEV_THREAD, 10 | .sigev_notify_function = cb, 11 | }; 12 | 13 | sev.sigev_value.sival_ptr = data; 14 | 15 | if (timer_create(CLOCK_REALTIME, &sev, &id) == -1) { 16 | return 0; 17 | } else { 18 | return id; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/timed.rs: -------------------------------------------------------------------------------- 1 | use core::pin::Pin; 2 | use tokio_1 as tokio; 3 | 4 | use std::time; 5 | 6 | #[tokio::test] 7 | async fn test_timed() { 8 | let mut future = async_timer::new_timer(time::Duration::from_secs(4)); 9 | let future = Pin::new(&mut future); 10 | let work = async_timer::timed(future, time::Duration::from_secs(3)); 11 | 12 | let before = time::SystemTime::now(); 13 | 14 | let expired = work.await.unwrap_err(); 15 | let work = expired.await; 16 | 17 | assert!(work.await.is_ok()); 18 | let after = time::SystemTime::now(); 19 | let diff = after.duration_since(before).unwrap(); 20 | 21 | assert!(diff.as_millis() >= 3_500 && diff.as_millis() <= 4_500); 22 | } 23 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | #[doc(hidden)] 2 | #[macro_export] 3 | #[cfg(not(debug_assertions))] 4 | macro_rules! unreach { 5 | () => ({ 6 | unsafe { 7 | core::hint::unreachable_unchecked(); 8 | } 9 | }) 10 | } 11 | 12 | #[doc(hidden)] 13 | #[macro_export] 14 | #[cfg(debug_assertions)] 15 | macro_rules! unreach { 16 | () => ({ 17 | unreachable!() 18 | }) 19 | } 20 | 21 | #[allow(unused_macros)] 22 | ///Assertion macro, which panics with last OS error 23 | macro_rules! os_assert { 24 | ($cond:expr) => ({ 25 | if !($cond) { 26 | panic!("Assertion '{}' failed. {}", stringify!($cond), error_code::ErrorCode::last_system()); 27 | } 28 | }) 29 | } 30 | 31 | #[allow(unused_macros)] 32 | #[doc(hidden)] 33 | macro_rules! assert_time { 34 | ($time:expr) => ({ 35 | debug_assert!(!($time.as_secs() == 0 && $time.subsec_nanos() == 0), "Zero timeout makes no sense"); 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /tests/timer.rs: -------------------------------------------------------------------------------- 1 | use async_timer::timer::{Timer, Platform, SyncTimer, new_sync_timer}; 2 | use tokio_1 as tokio; 3 | 4 | use std::time; 5 | 6 | #[tokio::test] 7 | async fn test_async_timer() { 8 | let work = Platform::new(time::Duration::from_secs(2)); 9 | assert!(!work.is_ticking()); 10 | assert!(!work.is_expired()); 11 | 12 | let before = time::SystemTime::now(); 13 | work.await; 14 | let after = time::SystemTime::now(); 15 | let diff = after.duration_since(before).unwrap(); 16 | 17 | assert!(diff.as_millis() >= 1_500 && diff.as_millis() <= 2_500); 18 | } 19 | 20 | #[test] 21 | fn test_cancel_timer() { 22 | let mut work = new_sync_timer(time::Duration::from_secs(500000)); 23 | 24 | assert!(!work.is_ticking()); 25 | assert!(!work.is_expired()); 26 | 27 | assert!(!work.tick()); 28 | 29 | assert!(work.is_ticking()); 30 | assert!(!work.is_expired()); 31 | 32 | work.cancel(); 33 | 34 | assert!(!work.is_ticking()); 35 | assert!(work.is_expired()); 36 | } 37 | -------------------------------------------------------------------------------- /src/timer/dummy.rs: -------------------------------------------------------------------------------- 1 | //! Dummy Timer 2 | 3 | use core::{task, time}; 4 | use core::future::Future; 5 | use core::pin::Pin; 6 | 7 | ///Dummy Timer with implementation that panics 8 | pub struct DummyTimer; 9 | 10 | impl DummyTimer { 11 | ///Creates new instance 12 | pub const fn new(_: time::Duration) -> Self { 13 | Self 14 | } 15 | } 16 | 17 | impl super::Timer for DummyTimer { 18 | fn new(_: time::Duration) -> Self { 19 | unimplemented!(); 20 | } 21 | 22 | fn is_ticking(&self) -> bool { 23 | false 24 | } 25 | 26 | fn is_expired(&self) -> bool { 27 | false 28 | } 29 | 30 | fn cancel(&mut self) { 31 | unimplemented!(); 32 | } 33 | 34 | fn restart(&mut self, _: time::Duration) { 35 | unimplemented!(); 36 | } 37 | 38 | fn restart_ctx(&mut self, _: time::Duration, _: &task::Waker) { 39 | unimplemented!(); 40 | } 41 | } 42 | 43 | impl super::SyncTimer for DummyTimer { 44 | fn init R>(&mut self, _: F) -> R { 45 | unimplemented!(); 46 | } 47 | } 48 | 49 | impl Future for DummyTimer { 50 | type Output = (); 51 | 52 | fn poll(self: Pin<&mut Self>, _: &mut task::Context) -> task::Poll { 53 | unimplemented!(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async-timer" 3 | version = "1.0.0-beta.15" 4 | authors = ["Douman "] 5 | edition = "2018" 6 | description = "Timers for Rust async story" 7 | readme = "README.md" 8 | repository = "https://github.com/DoumanAsh/async-timer" 9 | license = "BSL-1.0" 10 | keywords = ["timer", "async", "wasm"] 11 | categories = ["asynchronous", "wasm", "no-std"] 12 | include = [ 13 | "**/*.rs", 14 | "**/*.c", 15 | "Cargo.toml", 16 | "README.md" 17 | ] 18 | 19 | [features] 20 | default = [] 21 | # Enables std usage 22 | std = ["error-code/std"] 23 | # Enables C API wrapper for platform code. 24 | c_wrapper = ["cc"] 25 | # Enables usage of tokio 1.0 26 | tokio1 = ["tokio_1", "std"] 27 | 28 | [dependencies] 29 | error-code = "3" 30 | 31 | [target.'cfg(any(target_os = "macos", target_os = "ios", windows, unix))'.dependencies] 32 | libc = { version = "0.2.60", default-features = false } 33 | tokio_1 = { package = "tokio", version = "1.35", default-features = false, optional = true, features = ["net"] } 34 | 35 | [target.'cfg(any(target_arch = "wasm32"))'.dependencies] 36 | wasm-bindgen = "0.2" 37 | web-time = "1.1" 38 | 39 | [dev-dependencies] 40 | tokio_1 = { package = "tokio", version = "1.35", default-features = false, features = ["macros", "rt"] } 41 | 42 | [build-dependencies.cc] 43 | version = "1" 44 | optional = true 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - '**' 10 | 11 | jobs: 12 | build: 13 | 14 | runs-on: ${{ matrix.os }} 15 | 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | os: [macos-latest, windows-latest, ubuntu-latest] 20 | 21 | steps: 22 | - uses: actions/checkout@v1 23 | 24 | - name: Install Rust Unix 25 | if: runner.os != 'Windows' 26 | run: | 27 | if rustup --version >/dev/null 2>&1; then 28 | rustup update 29 | else 30 | curl https://sh.rustup.rs -sSf | sh -s -- -y --profile minimal --default-toolchain stable 31 | echo ::add-path::$HOME/.cargo/bin 32 | fi 33 | - name: Install Rust Windows 34 | if: runner.os == 'Windows' 35 | run: | 36 | if (Get-Command "rustup" -ErrorAction SilentlyContinue) { 37 | rustup update 38 | } else { 39 | Invoke-WebRequest https://static.rust-lang.org/rustup/dist/i686-pc-windows-gnu/rustup-init.exe -OutFile rustup-init.exe 40 | ./rustup-init.exe -y --profile minimal --default-toolchain stable 41 | echo ::add-path::%USERPROFILE%\.cargo\bin 42 | } 43 | 44 | - name: Rust version 45 | run: | 46 | cargo --version 47 | rustc --version 48 | 49 | - name: Check WASM 50 | run: | 51 | rustup target add wasm32-unknown-unknown 52 | cargo check --target wasm32-unknown-unknown 53 | 54 | - name: Test with tokio 1.0 55 | if: runner.os != 'Windows' 56 | run: cargo test --features tokio1 --release 57 | 58 | - name: Test 59 | run: cargo test --all --features std 60 | 61 | - name: Test with C wrapper 62 | if: runner.os == 'Linux' 63 | run: cargo test --all --features c_wrapper 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # async-timer 2 | 3 | ![Rust](https://github.com/DoumanAsh/async-timer/workflows/Rust/badge.svg?branch=master) 4 | [![Crates.io](https://img.shields.io/crates/v/async-timer.svg)](https://crates.io/crates/async-timer) 5 | [![Documentation](https://docs.rs/async-timer/badge.svg)](https://docs.rs/crate/async-timer/) 6 | [![dependency status](https://deps.rs/crate/async-timer/1.0.0-beta.13/status.svg)](https://deps.rs/crate/async-timer) 7 | 8 | Timer facilities for Rust's async story 9 | 10 | ## Accuracy 11 | 12 | Regular timers that do not rely on async event loop tend to be on par with user space timers 13 | like in `tokio`. 14 | If that's not suitable for you you should enable event loop based timers which in most cases 15 | give you the most accurate timers possible on unix platforms (See features.) 16 | 17 | ## Features 18 | 19 | - `tokio1` - Enables event loop based timers using tokio, providing higher resolution timers on unix platforms. 20 | - `c_wrapper` - Uses C shim to create bindings to platform API, which may be more reliable than `libc`. 21 | - `std` - Enables usage of std types (e.g. Error) 22 | 23 | ## Examples 24 | 25 | ### Timed 26 | 27 | ```rust 28 | async fn job() { 29 | } 30 | 31 | async fn do_job() { 32 | let work = unsafe { 33 | async_timer::Timed::platform_new_unchecked(job(), core::time::Duration::from_secs(1)) 34 | }; 35 | 36 | match work.await { 37 | Ok(_) => println!("I'm done!"), 38 | //You can retry by polling `expired` 39 | Err(expired) => println!("Job expired: {}", expired), 40 | } 41 | } 42 | ``` 43 | 44 | ### Interval 45 | 46 | ```rust 47 | async fn job() { 48 | } 49 | 50 | async fn do_a_while() { 51 | let mut times: u8 = 0; 52 | let mut interval = async_timer::Interval::platform_new(core::time::Duration::from_secs(1)); 53 | 54 | while times < 5 { 55 | job().await; 56 | interval.wait().await; 57 | times += 1; 58 | } 59 | } 60 | ``` 61 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Async timer lib 2 | //! 3 | //! ## Accuracy 4 | //! 5 | //! Regular timers that do not rely on async event loop tend to be on par with user space timers 6 | //! like in `tokio`. 7 | //! If that's not suitable for you you should enable event loop based timers which in most cases 8 | //! give you the most accurate timers possible on unix platforms (See features.) 9 | //! 10 | //! ## Timers 11 | //! 12 | //! - [Timer](timer/trait.Timer.html) interface to one-shot [Platform Timer](timer/type.Platform.html), may require event loop. 13 | //! - [SyncTimer](timer/trait.SyncTimer.html) interface to one-shot [Platform Timer](timer/type.SyncPlatform.html), does not require event loop. 14 | //! 15 | //! ## Primitives 16 | //! 17 | //! - [Timed](struct.Timed.html) - A wrapper over future that allows to limit time for the future to resolve. 18 | //! - [Interval](struct.Interval.html) - Periodic timer, that on each completition returns itself to poll once again with the same interval. 19 | //! 20 | //! ## Features 21 | //! 22 | //! - `tokio1` - Enables event loop based timers using tokio, providing higher resolution timers on unix platforms. 23 | //! - `c_wrapper` - Uses C shim to create bindings to platform API, which may be more reliable than `libc`. 24 | //! - `std` - Enables usage of std types (e.g. Error) 25 | #![warn(missing_docs)] 26 | 27 | #![cfg_attr(feature = "cargo-clippy", allow(clippy::style, clippy::needless_lifetimes))] 28 | 29 | extern crate alloc; 30 | #[cfg(feature = "std")] 31 | extern crate std; 32 | 33 | use core::time; 34 | use core::pin::Pin; 35 | use core::future::Future; 36 | 37 | #[macro_use] 38 | mod utils; 39 | pub mod state; 40 | pub mod timer; 41 | mod timed; 42 | mod interval; 43 | 44 | pub use state::Callback; 45 | pub use timer::{SyncTimer, Timer, new_sync_timer, new_timer}; 46 | pub use timed::{Timed, Expired}; 47 | pub use interval::Interval; 48 | 49 | #[inline(always)] 50 | ///Creates timed future with default Platform timer. 51 | pub fn timed<'a, F: Future>(fut: Pin<&'a mut F>, timeout: time::Duration) -> Timed<'a, F, timer::Platform> { 52 | Timed::platform_new(fut, timeout) 53 | } 54 | 55 | #[inline(always)] 56 | ///Creates interval with default Platform timer. 57 | pub fn interval(interval: time::Duration) -> Interval { 58 | Interval::platform_new(interval) 59 | } 60 | -------------------------------------------------------------------------------- /tests/interval.rs: -------------------------------------------------------------------------------- 1 | use async_timer::Interval; 2 | use tokio_1 as tokio; 3 | 4 | use std::time; 5 | 6 | #[tokio::test] 7 | async fn test_interval() { 8 | let mut interval = Interval::platform_new(time::Duration::from_secs(1)); 9 | 10 | let before = time::SystemTime::now(); 11 | interval.wait().await; 12 | let after = time::SystemTime::now(); 13 | let diff = after.duration_since(before).unwrap(); 14 | 15 | assert!(diff.as_millis() >= 750 && diff.as_millis() <= 1_250); 16 | 17 | let before = time::SystemTime::now(); 18 | interval.wait().await; 19 | let after = time::SystemTime::now(); 20 | let diff = after.duration_since(before).unwrap(); 21 | 22 | assert!(diff.as_millis() >= 750 && diff.as_millis() <= 1_250); 23 | } 24 | 25 | async fn test_interval_average(num_runs: usize, interval: time::Duration) { 26 | const ACCURACY: time::Duration = time::Duration::from_nanos(133333); 27 | 28 | let mut times = Vec::with_capacity(num_runs); 29 | 30 | println!("interval={:?}", interval); 31 | let mut timer = async_timer::interval(interval); 32 | 33 | for _ in 0..num_runs { 34 | let start = std::time::Instant::now(); 35 | 36 | timer.wait().await; 37 | 38 | // simulate some work, this doesn't have to be consistent 39 | // the timer is the only thing that needs to be consistent 40 | std::thread::sleep(time::Duration::from_millis(1)); 41 | 42 | // log time, the timer should be waiting less 43 | // to account for the time it took to do the work 44 | times.push(start.elapsed()); 45 | } 46 | 47 | let total: time::Duration = times.iter().sum(); 48 | let average = total / num_runs as u32; 49 | 50 | let min = interval - ACCURACY; 51 | let max = interval + ACCURACY; 52 | println!("Check {:?} <= average={:?} <= {:?}", min, average, max); 53 | //Margin of error should be within interval-0.1ms..=interval+0.1ms 54 | assert!(min <= average); 55 | assert!(average <= max); 56 | } 57 | 58 | //Windows timers are shite for small duration 59 | //kevent() also behaves badly for some reason 60 | //only linux's timerfd is reliable 61 | #[tokio::test] 62 | #[cfg(feature = "tokio1")] 63 | #[cfg(target_os = "linux")] 64 | async fn test_average_of_small_interval() { 65 | test_interval_average(6000, time::Duration::from_secs_f32(1. / 120.)).await; 66 | } 67 | 68 | #[tokio::test] 69 | #[cfg(feature = "tokio1")] 70 | #[cfg(target_os = "linux")] 71 | async fn test_average_of_mid_interval() { 72 | test_interval_average(60, time::Duration::from_secs_f32(135. / 120.)).await; 73 | } 74 | -------------------------------------------------------------------------------- /src/interval.rs: -------------------------------------------------------------------------------- 1 | //!Interval module 2 | 3 | #[cfg(not(target_arch = "wasm32"))] 4 | use std::time; 5 | #[cfg(target_arch = "wasm32")] 6 | use web_time as time; 7 | use core::task; 8 | use core::future::Future; 9 | use core::pin::Pin; 10 | 11 | use crate::timer::Timer; 12 | use crate::timer::Platform as PlatformTimer; 13 | 14 | ///Periodic Timer 15 | /// 16 | ///On each completion, underlying timer is restarted and therefore `Future` can be polled once 17 | ///more. 18 | /// 19 | ///## Usage 20 | /// 21 | ///```rust, no_run 22 | ///async fn job() { 23 | ///} 24 | /// 25 | ///async fn do_a_while() { 26 | /// let mut times: u8 = 0; 27 | /// let mut interval = async_timer::Interval::platform_new(core::time::Duration::from_secs(1)); 28 | /// 29 | /// while times < 5 { 30 | /// job().await; 31 | /// interval.wait().await; 32 | /// times += 1; 33 | /// } 34 | ///} 35 | ///``` 36 | #[must_use = "Interval does nothing unless polled"] 37 | pub struct Interval { 38 | timer: T, 39 | ///Timer interval, change to this value will be reflected on next restart of timer. 40 | pub interval: time::Duration, 41 | finish_at: time::Instant, 42 | } 43 | 44 | impl Interval { 45 | #[inline(always)] 46 | ///Creates new instance using platform timer 47 | pub fn platform_new(interval: time::Duration) -> Self { 48 | Interval::::new(interval) 49 | } 50 | } 51 | 52 | impl Interval { 53 | ///Creates new instance with specified timer type. 54 | pub fn new(interval: time::Duration) -> Self { 55 | Self { 56 | timer: T::new(interval), 57 | finish_at: time::Instant::now() + interval, 58 | interval, 59 | } 60 | } 61 | 62 | #[inline(always)] 63 | ///Stops interval 64 | pub fn cancel(&mut self) { 65 | self.timer.cancel() 66 | } 67 | 68 | ///Restarts interval 69 | pub fn restart(&mut self) { 70 | let now = time::Instant::now(); 71 | 72 | let interval = match now.checked_duration_since(self.finish_at) { 73 | Some(delayed) => self.interval - time::Duration::from_nanos((delayed.as_nanos() % self.interval.as_nanos()) as _), 74 | None => self.interval 75 | }; 76 | self.timer.restart(interval); 77 | } 78 | 79 | #[inline(always)] 80 | ///Returns future for next expiration. 81 | pub fn wait<'a>(&'a mut self) -> impl Future + 'a { 82 | self 83 | } 84 | } 85 | 86 | impl Future for &'_ mut Interval { 87 | type Output = (); 88 | 89 | fn poll(mut self: Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll { 90 | match Future::poll(Pin::new(&mut self.timer), ctx) { 91 | task::Poll::Ready(()) => { 92 | self.restart(); 93 | task::Poll::Ready(()) 94 | }, 95 | task::Poll::Pending => task::Poll::Pending, 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/timed.rs: -------------------------------------------------------------------------------- 1 | //! Timed future 2 | 3 | use core::future::Future; 4 | use core::{fmt, task, time}; 5 | use core::pin::Pin; 6 | 7 | use crate::timer::Timer; 8 | use crate::timer::Platform as PlatformTimer; 9 | 10 | struct State<'a, F, T> { 11 | timer: T, 12 | timeout: time::Duration, 13 | fut: Pin<&'a mut F> 14 | } 15 | 16 | #[must_use = "Timed does nothing unless polled"] 17 | ///Limiter on time to wait for underlying `Future` 18 | /// 19 | ///# Usage 20 | /// 21 | ///```rust, no_run 22 | ///async fn job() { 23 | ///} 24 | /// 25 | ///async fn do_job() { 26 | /// let mut job = job(); 27 | /// let job = unsafe { 28 | /// core::pin::Pin::new_unchecked(&mut job) 29 | /// }; 30 | /// let work = unsafe { 31 | /// async_timer::Timed::platform_new(job, core::time::Duration::from_secs(1)) 32 | /// }; 33 | /// 34 | /// match work.await { 35 | /// Ok(_) => println!("I'm done!"), 36 | /// //You can retry by polling `expired` 37 | /// Err(expired) => println!("Job expired: {}", expired), 38 | /// } 39 | ///} 40 | ///``` 41 | pub struct Timed<'a, F, T=PlatformTimer> { 42 | state: Option>, 43 | } 44 | 45 | impl<'a, F: Future> Timed<'a, F> { 46 | #[inline] 47 | ///Creates new instance using [Timer](../oneshot/type.Timer.html) alias. 48 | pub fn platform_new(fut: Pin<&'a mut F>, timeout: time::Duration) -> Self { 49 | Self::new(fut, timeout) 50 | } 51 | } 52 | 53 | impl<'a, F: Future, T: Timer> Timed<'a, F, T> { 54 | ///Creates new instance with specified timeout 55 | /// 56 | ///Unsafe version of `new` that doesn't require `Unpin`. 57 | /// 58 | ///Requires to specify `Timer` type (e.g. `Timed::::new()`) 59 | pub fn new(fut: Pin<&'a mut F>, timeout: time::Duration) -> Self { 60 | Self { 61 | state: Some(State { 62 | timer: T::new(timeout), 63 | timeout, 64 | fut, 65 | }) 66 | } 67 | } 68 | } 69 | 70 | impl<'a, F: Future, T: Timer> Future for Timed<'a, F, T> { 71 | type Output = Result>; 72 | 73 | fn poll(self: Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll { 74 | let this = self.get_mut(); 75 | 76 | if let Some(state) = this.state.as_mut() { 77 | match Future::poll(state.fut.as_mut(), ctx) { 78 | task::Poll::Pending => (), 79 | task::Poll::Ready(result) => return task::Poll::Ready(Ok(result)), 80 | } 81 | 82 | match Future::poll(Pin::new(&mut state.timer), ctx) { 83 | task::Poll::Pending => (), 84 | task::Poll::Ready(_) => return task::Poll::Ready(Err(Expired(this.state.take()))), 85 | } 86 | } 87 | 88 | task::Poll::Pending 89 | } 90 | } 91 | 92 | #[must_use = "Expire should be handled as error or to restart Timed"] 93 | ///Error when [Timed](struct.Timed.html) expires 94 | /// 95 | ///Implements `Future` that can be used to restart `Timed` 96 | ///Note, that `Timer` starts execution immediately after resolving this Future. 97 | pub struct Expired<'a, F, T>(Option>); 98 | 99 | impl<'a, F: Future, T: Timer> Future for Expired<'a, F, T> { 100 | type Output = Timed<'a, F, T>; 101 | 102 | fn poll(self: Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll { 103 | let this = self.get_mut(); 104 | 105 | match this.0.take() { 106 | Some(mut state) => { 107 | state.timer.restart_ctx(state.timeout, ctx.waker()); 108 | 109 | task::Poll::Ready(Timed { 110 | state: Some(state) 111 | }) 112 | }, 113 | None => task::Poll::Pending, 114 | } 115 | } 116 | } 117 | 118 | #[cfg(feature = "std")] 119 | impl<'a, F, T: Timer> crate::std::error::Error for Expired<'a, F, T> {} 120 | 121 | impl<'a, F, T: Timer> fmt::Debug for Expired<'a, F, T> { 122 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 123 | write!(f, "{}", self) 124 | } 125 | } 126 | 127 | impl<'a, F, T: Timer> fmt::Display for Expired<'a, F, T> { 128 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 129 | match self.0.as_ref() { 130 | None => write!(f, "Future is being re-tried."), 131 | Some(state) => match state.timeout.as_secs() { 132 | 0 => write!(f, "Future expired in {} ms", state.timeout.as_millis()), 133 | secs => write!(f, "Future expired in {} seconds and {} ms", secs, state.timeout.subsec_millis()), 134 | }, 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/timer/web.rs: -------------------------------------------------------------------------------- 1 | //! Web based timer 2 | 3 | use core::{task, time}; 4 | use core::pin::Pin; 5 | use core::future::Future; 6 | 7 | use crate::state::TimerState; 8 | use crate::alloc::boxed::Box; 9 | 10 | #[wasm_bindgen::prelude::wasm_bindgen] 11 | extern "C" { 12 | fn setTimeout(closure: &wasm_bindgen::closure::Closure, time: u32) -> i32; 13 | fn clearTimeout(id: i32); 14 | } 15 | 16 | struct TimerHandle { 17 | timeout_id: i32, 18 | _closure: wasm_bindgen::closure::Closure, 19 | } 20 | 21 | impl TimerHandle { 22 | #[inline] 23 | fn clear(&mut self) { 24 | clearTimeout(self.timeout_id) 25 | } 26 | } 27 | 28 | impl Drop for TimerHandle { 29 | fn drop(&mut self) { 30 | self.clear(); 31 | } 32 | } 33 | 34 | fn timer_create(timeout: time::Duration, state: *const TimerState) -> TimerHandle { 35 | let timeout = timeout.as_millis() as u32; 36 | 37 | let closure = wasm_bindgen::closure::Closure::once(move || unsafe { 38 | (*state).wake(); 39 | }); 40 | let timeout_id = setTimeout(&closure, timeout); 41 | 42 | TimerHandle { 43 | timeout_id, 44 | _closure: closure, 45 | } 46 | } 47 | 48 | enum State { 49 | Init(time::Duration), 50 | Running(TimerHandle, *const TimerState), 51 | } 52 | 53 | unsafe impl Send for State {} 54 | unsafe impl Sync for State {} 55 | 56 | ///Web timer wrapper 57 | pub struct WebTimer { 58 | state: State, 59 | } 60 | 61 | impl WebTimer { 62 | #[inline] 63 | ///Creates new instance 64 | pub const fn new(time: time::Duration) -> Self { 65 | Self { 66 | state: State::Init(time), 67 | } 68 | } 69 | } 70 | 71 | impl super::Timer for WebTimer { 72 | #[inline(always)] 73 | fn new(timeout: time::Duration) -> Self { 74 | assert_time!(timeout); 75 | Self::new(timeout) 76 | } 77 | 78 | #[inline] 79 | fn is_ticking(&self) -> bool { 80 | match &self.state { 81 | State::Init(_) => false, 82 | State::Running(_, ref state) => unsafe { 83 | !(**state).is_done() 84 | }, 85 | } 86 | } 87 | 88 | #[inline] 89 | fn is_expired(&self) -> bool { 90 | match &self.state { 91 | State::Init(_) => false, 92 | State::Running(_, ref state) => unsafe { 93 | (**state).is_done() 94 | }, 95 | } 96 | } 97 | 98 | fn restart(&mut self, new_value: time::Duration) { 99 | assert_time!(new_value); 100 | 101 | match &mut self.state { 102 | State::Init(ref mut timeout) => { 103 | *timeout = new_value; 104 | }, 105 | State::Running(fd, ref state) => { 106 | unsafe { (**state).reset() }; 107 | *fd = timer_create(new_value, *state); 108 | } 109 | } 110 | } 111 | 112 | fn restart_ctx(&mut self, new_value: time::Duration, waker: &task::Waker) { 113 | assert_time!(new_value); 114 | 115 | match &mut self.state { 116 | State::Init(ref mut timeout) => { 117 | *timeout = new_value; 118 | }, 119 | State::Running(fd, ref state) => { 120 | unsafe { (**state).register(waker) }; 121 | unsafe { (**state).reset() }; 122 | *fd = timer_create(new_value, *state); 123 | } 124 | } 125 | } 126 | 127 | fn cancel(&mut self) { 128 | match self.state { 129 | State::Init(_) => (), 130 | State::Running(ref mut fd, state) => unsafe { 131 | (*state).cancel(); 132 | fd.clear() 133 | } 134 | } 135 | } 136 | } 137 | 138 | impl super::SyncTimer for WebTimer { 139 | fn init R>(&mut self, init: F) -> R { 140 | if let State::Init(timeout) = self.state { 141 | let state = TimerState::new(); 142 | init(&state); 143 | 144 | let state = Box::into_raw(Box::new(state)); 145 | let fd = timer_create(timeout, state); 146 | 147 | self.state = State::Running(fd, state) 148 | } 149 | 150 | match &self.state { 151 | State::Running(_, ref state) => init(unsafe { &**state }), 152 | State::Init(_) => unreach!(), 153 | } 154 | } 155 | } 156 | 157 | impl Future for WebTimer { 158 | type Output = (); 159 | 160 | #[inline] 161 | fn poll(self: Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll { 162 | crate::timer::poll_sync(self.get_mut(), ctx) 163 | } 164 | } 165 | 166 | impl Drop for WebTimer { 167 | fn drop(&mut self) { 168 | match self.state { 169 | State::Running(ref mut fd, state) => unsafe { 170 | (*state).cancel(); 171 | fd.clear(); 172 | let _ = Box::from_raw(state as *mut TimerState); 173 | }, 174 | _ => (), 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/timer/win.rs: -------------------------------------------------------------------------------- 1 | //! Windows API based timer 2 | 3 | use core::{task, time, ptr}; 4 | use core::pin::Pin; 5 | use core::future::Future; 6 | 7 | use crate::state::TimerState; 8 | use crate::alloc::boxed::Box; 9 | 10 | #[allow(non_snake_case, non_camel_case_types)] 11 | mod ffi { 12 | pub use core::ffi::c_void; 13 | use libc::{c_ulong, c_int}; 14 | 15 | #[repr(C)] 16 | #[derive(Copy, Clone)] 17 | pub struct FILETIME { 18 | pub dwLowDateTime: c_ulong, 19 | pub dwHighDateTime: c_ulong, 20 | } 21 | #[repr(C)] 22 | pub union ULONGTIME { 23 | pub full: i64, 24 | pub time: FILETIME, 25 | } 26 | 27 | pub type PTP_TIMER = *mut c_void; 28 | 29 | type PTP_TIMER_CALLBACK = Option; 30 | 31 | extern "system" { 32 | pub fn CloseThreadpoolTimer(pti: *mut c_void); 33 | pub fn CreateThreadpoolTimer(pfnti: PTP_TIMER_CALLBACK, pv: *mut c_void, pcbe: *mut c_void) -> *mut c_void; 34 | pub fn SetThreadpoolTimerEx(pti: *mut c_void, pftDueTime: *mut FILETIME, msPeriod: c_ulong, msWindowLength: c_ulong) -> c_int; 35 | pub fn WaitForThreadpoolTimerCallbacks(pti: PTP_TIMER, fCancelPendingCallbacks: c_int); 36 | } 37 | } 38 | 39 | unsafe extern "system" fn timer_callback(_: *mut ffi::c_void, data: *mut ffi::c_void, _: *mut ffi::c_void) { 40 | #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))] 41 | let state = data as *mut TimerState; 42 | 43 | (*state).wake(); 44 | } 45 | 46 | fn time_create(state: *mut TimerState) -> ffi::PTP_TIMER { 47 | let timer = unsafe { 48 | ffi::CreateThreadpoolTimer(Some(timer_callback), state as *mut ffi::c_void, ptr::null_mut()) 49 | }; 50 | os_assert!(!timer.is_null()); 51 | 52 | timer 53 | } 54 | 55 | fn set_timer_value(fd: ffi::PTP_TIMER, timeout: time::Duration) { 56 | let mut ticks = i64::from(timeout.subsec_nanos() / 100); 57 | ticks += (timeout.as_secs() * 10_000_000) as i64; 58 | let ticks = -ticks; 59 | 60 | unsafe { 61 | let mut time = ffi::ULONGTIME { 62 | full: ticks, 63 | }; 64 | ffi::SetThreadpoolTimerEx(fd, &mut time.time, 0, 0); 65 | } 66 | } 67 | 68 | enum State { 69 | Init(time::Duration), 70 | Running(ffi::PTP_TIMER, Box), 71 | } 72 | 73 | unsafe impl Send for State {} 74 | unsafe impl Sync for State {} 75 | 76 | ///Windows Native timer 77 | pub struct WinTimer { 78 | state: State, 79 | } 80 | 81 | impl WinTimer { 82 | #[inline] 83 | ///Creates new instance 84 | pub const fn new(time: time::Duration) -> Self { 85 | Self { 86 | state: State::Init(time), 87 | } 88 | } 89 | } 90 | 91 | impl super::Timer for WinTimer { 92 | #[inline(always)] 93 | fn new(timeout: time::Duration) -> Self { 94 | assert_time!(timeout); 95 | debug_assert!(timeout.as_millis() <= u32::max_value().into()); 96 | Self::new(timeout) 97 | } 98 | 99 | #[inline] 100 | fn is_ticking(&self) -> bool { 101 | match &self.state { 102 | State::Init(_) => false, 103 | State::Running(_, ref state) => !state.is_done(), 104 | } 105 | } 106 | 107 | #[inline] 108 | fn is_expired(&self) -> bool { 109 | match &self.state { 110 | State::Init(_) => false, 111 | State::Running(_, ref state) => state.is_done(), 112 | } 113 | } 114 | 115 | fn restart(&mut self, new_value: time::Duration) { 116 | assert_time!(new_value); 117 | debug_assert!(new_value.as_millis() <= u32::max_value().into()); 118 | 119 | match &mut self.state { 120 | State::Init(ref mut timeout) => { 121 | *timeout = new_value; 122 | }, 123 | State::Running(ref fd, ref state) => { 124 | state.reset(); 125 | set_timer_value(*fd, new_value); 126 | } 127 | } 128 | } 129 | 130 | fn restart_ctx(&mut self, new_value: time::Duration, waker: &task::Waker) { 131 | assert_time!(new_value); 132 | debug_assert!(new_value.as_millis() <= u32::max_value().into()); 133 | 134 | match &mut self.state { 135 | State::Init(ref mut timeout) => { 136 | *timeout = new_value; 137 | }, 138 | State::Running(ref fd, ref state) => { 139 | state.register(waker); 140 | state.reset(); 141 | set_timer_value(*fd, new_value); 142 | } 143 | } 144 | } 145 | 146 | fn cancel(&mut self) { 147 | match self.state { 148 | State::Init(_) => (), 149 | State::Running(fd, ref state) => unsafe { 150 | state.cancel(); 151 | ffi::SetThreadpoolTimerEx(fd, ptr::null_mut(), 0, 0); 152 | ffi::WaitForThreadpoolTimerCallbacks(fd, 1); 153 | } 154 | } 155 | } 156 | } 157 | 158 | impl super::SyncTimer for WinTimer { 159 | fn init R>(&mut self, init: F) -> R { 160 | if let State::Init(timeout) = self.state { 161 | let state = Box::into_raw(Box::new(TimerState::new())); 162 | let fd = time_create(state); 163 | 164 | let state = unsafe { Box::from_raw(state) }; 165 | 166 | init(&state); 167 | 168 | set_timer_value(fd, timeout); 169 | 170 | self.state = State::Running(fd, state) 171 | } 172 | 173 | match &self.state { 174 | State::Running(_, ref state) => init(&state), 175 | State::Init(_) => unreach!(), 176 | } 177 | } 178 | } 179 | 180 | impl Future for WinTimer { 181 | type Output = (); 182 | 183 | #[inline] 184 | fn poll(self: Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll { 185 | crate::timer::poll_sync(self.get_mut(), ctx) 186 | } 187 | } 188 | 189 | impl Drop for WinTimer { 190 | fn drop(&mut self) { 191 | match self.state { 192 | State::Init(_) => (), 193 | State::Running(fd, ref state) => unsafe { 194 | state.cancel(); 195 | ffi::SetThreadpoolTimerEx(fd, ptr::null_mut(), 0, 0); 196 | ffi::WaitForThreadpoolTimerCallbacks(fd, 1); 197 | ffi::CloseThreadpoolTimer(fd); 198 | } 199 | } 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/timer/mod.rs: -------------------------------------------------------------------------------- 1 | //!Raw Timer 2 | 3 | use core::{time, task}; 4 | use core::future::Future; 5 | 6 | use crate::state::TimerState; 7 | 8 | ///Timer 9 | /// 10 | ///## Common implementations: 11 | /// 12 | ///- Windows uses thread pooled timer 13 | ///- Apple systems uses dispatch source API 14 | ///- Posix compatible `timer_create`, available on major Posix-compliant systems. Depends on availability of `siginfo_t::si_value` method. 15 | ///- Wasm uses Web API `SetTimeout` 16 | ///- Dummy timer is used when no implementation is available. Panics when used. 17 | /// 18 | ///## Usage 19 | /// 20 | ///```no_run 21 | ///use async_timer::timer::{Timer, new_timer}; 22 | /// 23 | ///use core::time; 24 | ///use core::pin::Pin; 25 | /// 26 | ///async fn do_something() { 27 | /// let mut work = new_timer(time::Duration::from_secs(2)); 28 | /// assert!(!work.is_ticking()); //Timer starts only on initial poll 29 | /// assert!(!work.is_expired()); 30 | /// Pin::new(&mut work).await; //Remember await consumes future, and we'd prefer to avoid that in order to re-use timer 31 | /// assert!(work.is_expired()); 32 | /// 33 | ///} 34 | ///``` 35 | pub trait Timer: Send + Sync + Unpin + Future { 36 | ///Creates new instance 37 | fn new(timeout: time::Duration) -> Self; 38 | 39 | ///Returns whether timer is ongoing. 40 | /// 41 | ///Note that if it returns `false` it doesn't mean that `is_expired` will return `true` 42 | ///as initially timer may not be armed. 43 | fn is_ticking(&self) -> bool; 44 | 45 | ///Returns whether timer has expired. 46 | fn is_expired(&self) -> bool; 47 | 48 | ///Restarts timer with new timeout value. 49 | fn restart(&mut self, timeout: time::Duration); 50 | 51 | ///Restarts timer with new timeout value and waker. 52 | fn restart_ctx(&mut self, timeout: time::Duration, waker: &task::Waker); 53 | 54 | ///Cancels timer, if it is still ongoing. 55 | fn cancel(&mut self); 56 | } 57 | 58 | ///Describes timer interface that doesn't require async event loop. 59 | /// 60 | ///In most cases these timers are implemented by OS calling back a provided callback. 61 | /// 62 | ///As notification is not done via event loop, timer has to store callback in its own state. 63 | /// 64 | ///Whenever async timer relies on async event loop to handle notifications 65 | /// 66 | ///## Usage 67 | /// 68 | ///``` 69 | ///use async_timer::timer::{Timer, SyncTimer, new_sync_timer}; 70 | /// 71 | ///use core::sync::atomic::{AtomicBool, Ordering}; 72 | ///use core::time; 73 | /// 74 | ///use std::thread; 75 | /// 76 | ///static EXPIRED: AtomicBool = AtomicBool::new(false); 77 | ///fn on_expire() { 78 | /// EXPIRED.store(true, Ordering::Release); 79 | ///} 80 | /// 81 | ///let mut work = new_sync_timer(time::Duration::from_secs(1)); 82 | ///assert!(!work.is_ticking()); 83 | ///assert!(!work.is_expired()); 84 | /// 85 | ///work.init(|state| state.register(on_expire as fn())); 86 | ///work.tick(); 87 | /// 88 | ///assert!(work.is_ticking()); 89 | ///assert!(!work.is_expired()); 90 | ///thread::sleep(time::Duration::from_millis(1250)); //timer is not necessary expires immediately 91 | /// 92 | ///assert!(work.is_expired()); 93 | ///assert!(EXPIRED.load(Ordering::Acquire)); 94 | ///``` 95 | /// 96 | pub trait SyncTimer: Timer { 97 | ///Initializes timer state, performing initial arming and allowing to access `TimerState` 98 | ///during initialization 99 | /// 100 | ///The state can be used to register callback and check whether timer has notified user using 101 | ///configured callback 102 | /// 103 | ///If `Timer` is already armed, then `TimerState` is granted as it is. 104 | fn init R>(&mut self, init: F) -> R; 105 | 106 | ///Ticks timer. 107 | /// 108 | ///If timer is not started yet, starts returning false. 109 | ///Otherwise performs necessary actions, if any, to drive timer 110 | ///and returns whether it is expired. 111 | /// 112 | ///Default implementation initializes timer state, if necessary, and 113 | ///returns whether timer has expired or not. 114 | #[inline(always)] 115 | fn tick(&mut self) -> bool { 116 | self.init(|state| state.is_done()) 117 | } 118 | } 119 | 120 | #[inline(always)] 121 | fn poll_sync(timer: &mut T, ctx: &mut task::Context) -> task::Poll<()> { 122 | timer.init(|state| { 123 | state.register(ctx.waker()); 124 | match state.is_done() { 125 | true => task::Poll::Ready(()), 126 | false => task::Poll::Pending 127 | } 128 | }) 129 | } 130 | 131 | #[cfg(windows)] 132 | mod win; 133 | #[cfg(windows)] 134 | pub use win::WinTimer; 135 | #[cfg(windows)] 136 | ///Platform alias to Windows timer 137 | pub type Platform = win::WinTimer; 138 | #[cfg(windows)] 139 | ///Platform alias to Windows timer 140 | pub type SyncPlatform = win::WinTimer; 141 | 142 | #[cfg(all(feature = "tokio1", unix))] 143 | mod async_tokio1; 144 | #[cfg(all(feature = "tokio1", unix))] 145 | pub use async_tokio1::AsyncTimer; 146 | #[cfg(all(feature = "tokio1", unix))] 147 | ///Timer based on tokio's `AsyncFd` 148 | pub type Platform = AsyncTimer; 149 | 150 | #[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))] 151 | mod posix; 152 | #[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))] 153 | pub use posix::PosixTimer; 154 | #[cfg(all(not(feature = "tokio1"), not(any(target_os = "macos", target_os = "ios")), unix))] 155 | ///Platform alias to POSIX timer 156 | pub type Platform = posix::PosixTimer; 157 | #[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))] 158 | ///Platform alias to POSIX Timer 159 | pub type SyncPlatform = posix::PosixTimer; 160 | 161 | #[cfg(any(target_os = "macos", target_os = "ios"))] 162 | mod apple; 163 | #[cfg(any(target_os = "macos", target_os = "ios"))] 164 | pub use apple::AppleTimer; 165 | #[cfg(all(not(feature = "tokio1"), any(target_os = "macos", target_os = "ios")))] 166 | ///Platform alias to Apple Dispatch timer 167 | pub type Platform = apple::AppleTimer; 168 | #[cfg(any(target_os = "macos", target_os = "ios"))] 169 | ///Platform Alias to `kqueue` based Timer 170 | pub type SyncPlatform = apple::AppleTimer; 171 | 172 | #[cfg(target_arch = "wasm32")] 173 | mod web; 174 | #[cfg(target_arch = "wasm32")] 175 | pub use web::WebTimer; 176 | #[cfg(target_arch = "wasm32")] 177 | ///Platform alias to WASM Timer 178 | pub type Platform = web::WebTimer; 179 | #[cfg(target_arch = "wasm32")] 180 | ///Platform alias to WASM Timer 181 | pub type SyncPlatform = web::WebTimer; 182 | 183 | mod dummy; 184 | pub use dummy::DummyTimer; 185 | #[cfg(not(any(windows, target_arch = "wasm32", unix)))] 186 | ///Platform alias to Dummy Timer as no OS implementation is available. 187 | pub type Platform = dummy::DummyTimer; 188 | #[cfg(not(any(windows, target_arch = "wasm32", unix)))] 189 | ///Platform alias to Dummy Timer as no OS implementation is available. 190 | pub type SyncPlatform = dummy::DummyTimer; 191 | 192 | #[inline] 193 | ///Creates new timer, timer type depends on platform. 194 | pub const fn new_timer(timeout: time::Duration) -> Platform { 195 | Platform::new(timeout) 196 | } 197 | 198 | #[inline] 199 | ///Creates new timer, which always implements `SyncTimer` 200 | pub const fn new_sync_timer(timeout: time::Duration) -> SyncPlatform { 201 | SyncPlatform::new(timeout) 202 | } 203 | -------------------------------------------------------------------------------- /src/timer/apple.rs: -------------------------------------------------------------------------------- 1 | //! Dispatch Source based Timer 2 | 3 | use core::{ptr, task, time}; 4 | use core::pin::Pin; 5 | use core::future::Future; 6 | 7 | use crate::state::TimerState; 8 | use crate::alloc::boxed::Box; 9 | 10 | use libc::{c_long, c_ulong, c_void, uintptr_t}; 11 | 12 | #[allow(non_camel_case_types)] 13 | mod ffi { 14 | use super::*; 15 | 16 | pub type dispatch_object_t = *const c_void; 17 | pub type dispatch_queue_t = *const c_void; 18 | pub type dispatch_source_t = *const c_void; 19 | pub type dispatch_source_type_t = *const c_void; 20 | pub type dispatch_time_t = u64; 21 | 22 | pub const DISPATCH_TIME_FOREVER: dispatch_time_t = !0; 23 | //pub const DISPATCH_WALLTIME_NOW: dispatch_time_t = !1; 24 | pub const QOS_CLASS_DEFAULT: c_long = 0x15; 25 | 26 | extern "C" { 27 | pub static _dispatch_source_type_timer: c_long; 28 | 29 | pub fn dispatch_get_global_queue(identifier: c_long, flags: c_ulong) -> dispatch_queue_t; 30 | pub fn dispatch_source_create(type_: dispatch_source_type_t, handle: uintptr_t, mask: c_ulong, queue: dispatch_queue_t) -> dispatch_source_t; 31 | pub fn dispatch_source_set_timer(source: dispatch_source_t, start: dispatch_time_t, interval: u64, leeway: u64); 32 | pub fn dispatch_source_set_event_handler_f(source: dispatch_source_t, handler: unsafe extern "C" fn(*mut c_void)); 33 | pub fn dispatch_set_context(object: dispatch_object_t, context: *mut c_void); 34 | pub fn dispatch_resume(object: dispatch_object_t); 35 | pub fn dispatch_suspend(object: dispatch_object_t); 36 | pub fn dispatch_release(object: dispatch_object_t); 37 | pub fn dispatch_source_cancel(object: dispatch_object_t); 38 | pub fn dispatch_walltime(when: *const c_void, delta: i64) -> dispatch_time_t; 39 | } 40 | } 41 | 42 | //TODO: Investigate why sometimes it is called multiple times 43 | unsafe extern "C" fn timer_handler(context: *mut c_void) { 44 | let state = context as *mut TimerState; 45 | 46 | (*state).wake(); 47 | } 48 | 49 | struct TimerHandle { 50 | inner: ffi::dispatch_source_t, 51 | //Suspension count. Incremented suspend, and decremented on each resume 52 | s_count: u8, 53 | } 54 | 55 | impl Drop for TimerHandle { 56 | fn drop(&mut self) { 57 | unsafe { 58 | ffi::dispatch_source_cancel(self.inner); 59 | 60 | //It is error to release while source is suspended 61 | //So we decrement it 62 | self.resume(); 63 | 64 | ffi::dispatch_release(self.inner); 65 | } 66 | } 67 | } 68 | 69 | impl TimerHandle { 70 | fn new(state: *mut TimerState) -> Self { 71 | let inner = unsafe { 72 | let queue = ffi::dispatch_get_global_queue(ffi::QOS_CLASS_DEFAULT, 0); 73 | ffi::dispatch_source_create(&ffi::_dispatch_source_type_timer as *const _ as ffi::dispatch_source_type_t, 0, 0, queue) 74 | }; 75 | 76 | os_assert!(!inner.is_null()); 77 | 78 | unsafe { 79 | ffi::dispatch_source_set_event_handler_f(inner, timer_handler); 80 | ffi::dispatch_set_context(inner, state as *mut _); 81 | } 82 | 83 | Self { 84 | inner, 85 | //Starts as suspended 86 | s_count: 1, 87 | } 88 | } 89 | 90 | fn suspend(&mut self) { 91 | if self.s_count == 0 { 92 | unsafe { 93 | ffi::dispatch_suspend(self.inner); 94 | } 95 | 96 | self.s_count += 1; 97 | } 98 | } 99 | 100 | fn resume(&mut self) { 101 | while self.s_count > 0 { 102 | unsafe { 103 | ffi::dispatch_resume(self.inner) 104 | } 105 | 106 | self.s_count -= 1; 107 | } 108 | } 109 | 110 | fn set_delay(&mut self, timeout: time::Duration) { 111 | self.suspend(); 112 | 113 | unsafe { 114 | let start = ffi::dispatch_walltime(ptr::null(), timeout.as_nanos() as i64); 115 | ffi::dispatch_source_set_timer(self.inner, start, ffi::DISPATCH_TIME_FOREVER, 0); 116 | } 117 | 118 | self.resume(); 119 | } 120 | } 121 | 122 | unsafe impl Send for TimerHandle {} 123 | unsafe impl Sync for TimerHandle {} 124 | 125 | enum State { 126 | Init(time::Duration), 127 | Running(TimerHandle, Box), 128 | } 129 | 130 | ///Posix Timer 131 | /// 132 | ///Currently implemented only for `Linux` and `Android` as BSD systems 133 | ///proved to be a bit problematic 134 | pub struct AppleTimer { 135 | state: State, 136 | } 137 | 138 | impl AppleTimer { 139 | #[inline] 140 | ///Creates new instance 141 | pub const fn new(time: time::Duration) -> Self { 142 | Self { 143 | state: State::Init(time), 144 | } 145 | } 146 | } 147 | 148 | impl super::Timer for AppleTimer { 149 | #[inline(always)] 150 | fn new(timeout: time::Duration) -> Self { 151 | assert_time!(timeout); 152 | Self::new(timeout) 153 | } 154 | 155 | #[inline] 156 | fn is_ticking(&self) -> bool { 157 | match &self.state { 158 | State::Init(_) => false, 159 | State::Running(_, ref state) => !state.is_done(), 160 | } 161 | } 162 | 163 | #[inline] 164 | fn is_expired(&self) -> bool { 165 | match &self.state { 166 | State::Init(_) => false, 167 | State::Running(_, ref state) => state.is_done(), 168 | } 169 | } 170 | 171 | fn restart(&mut self, new_value: time::Duration) { 172 | assert_time!(new_value); 173 | 174 | match &mut self.state { 175 | State::Init(ref mut timeout) => { 176 | *timeout = new_value; 177 | }, 178 | State::Running(fd, ref mut state) => { 179 | state.reset(); 180 | fd.set_delay(new_value); 181 | } 182 | } 183 | } 184 | 185 | fn restart_ctx(&mut self, new_value: time::Duration, waker: &task::Waker) { 186 | assert_time!(new_value); 187 | 188 | match &mut self.state { 189 | State::Init(ref mut timeout) => { 190 | *timeout = new_value; 191 | }, 192 | State::Running(fd, ref mut state) => { 193 | state.register(waker); 194 | state.reset(); 195 | fd.set_delay(new_value); 196 | } 197 | } 198 | } 199 | 200 | fn cancel(&mut self) { 201 | match self.state { 202 | State::Init(_) => (), 203 | State::Running(ref mut fd, ref state) => { 204 | state.cancel(); 205 | fd.suspend(); 206 | } 207 | } 208 | } 209 | } 210 | 211 | impl super::SyncTimer for AppleTimer { 212 | fn init R>(&mut self, init: F) -> R { 213 | if let State::Init(timeout) = self.state { 214 | let state = Box::into_raw(Box::new(TimerState::new())); 215 | let mut fd = TimerHandle::new(state); 216 | 217 | let state = unsafe { Box::from_raw(state) }; 218 | init(&state); 219 | 220 | fd.set_delay(timeout); 221 | 222 | self.state = State::Running(fd, state) 223 | } 224 | 225 | match &self.state { 226 | State::Running(_, ref state) => init(state), 227 | State::Init(_) => unreach!(), 228 | } 229 | } 230 | } 231 | 232 | impl Future for AppleTimer { 233 | type Output = (); 234 | 235 | #[inline] 236 | fn poll(self: Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll { 237 | crate::timer::poll_sync(self.get_mut(), ctx) 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /src/timer/posix.rs: -------------------------------------------------------------------------------- 1 | //! Posix based timer 2 | 3 | use core::{mem, ptr, time, task}; 4 | use core::pin::Pin; 5 | use core::future::Future; 6 | 7 | use crate::state::TimerState; 8 | use crate::alloc::boxed::Box; 9 | 10 | mod ffi { 11 | use super::*; 12 | 13 | #[allow(non_camel_case_types)] 14 | pub type timer_t = usize; 15 | 16 | #[cfg(feature = "c_wrapper")] 17 | pub unsafe extern "C" fn timer_handler(value: libc::sigval) { 18 | let state = value.sival_ptr as *const TimerState; 19 | (*state).wake(); 20 | } 21 | 22 | #[cfg(not(feature = "c_wrapper"))] 23 | pub unsafe extern "C" fn timer_handler(_sig: libc::c_int, si: *mut libc::siginfo_t, _uc: *mut libc::c_void) { 24 | let state = (*si).si_value().sival_ptr as *const TimerState; 25 | (*state).wake(); 26 | } 27 | 28 | #[repr(C)] 29 | pub struct itimerspec { 30 | pub it_interval: libc::timespec, 31 | pub it_value: libc::timespec, 32 | } 33 | 34 | extern "C" { 35 | #[allow(unused)] 36 | pub fn timer_create(clockid: libc::clockid_t, sevp: *mut libc::sigevent, timerid: *mut timer_t) -> libc::c_int; 37 | pub fn timer_settime(timerid: timer_t, flags: libc::c_int, new_value: *const itimerspec, old_value: *mut itimerspec) -> libc::c_int; 38 | pub fn timer_delete(timerid: timer_t); 39 | } 40 | } 41 | 42 | #[cfg(not(feature = "c_wrapper"))] 43 | const TIMER_SIG: libc::c_int = 40; 44 | 45 | #[cfg(not(feature = "c_wrapper"))] 46 | fn init_sig() { 47 | let mut sa_mask = mem::MaybeUninit::::uninit(); 48 | unsafe { 49 | libc::sigemptyset(sa_mask.as_mut_ptr()); 50 | } 51 | 52 | let timer_sig = libc::sigaction { 53 | sa_flags: libc::SA_SIGINFO, 54 | sa_sigaction: ffi::timer_handler as usize, 55 | sa_mask: unsafe { sa_mask.assume_init() }, 56 | #[cfg(any(target_os = "linux", target_os = "android"))] 57 | sa_restorer: None, 58 | }; 59 | 60 | unsafe { 61 | os_assert!(libc::sigaction(TIMER_SIG, &timer_sig, ptr::null_mut()) != -1); 62 | } 63 | } 64 | 65 | #[cfg(feature = "c_wrapper")] 66 | fn time_create(state: *mut TimerState) -> ffi::timer_t { 67 | #[link(name = "posix_wrapper", kind = "static")] 68 | extern "C" { 69 | fn posix_timer(_: Option, _: *mut libc::c_void) -> ffi::timer_t; 70 | } 71 | 72 | let res = unsafe { 73 | posix_timer(Some(ffi::timer_handler), state as *mut libc::c_void) 74 | }; 75 | 76 | os_assert!(res != 0); 77 | res 78 | } 79 | 80 | #[cfg(not(feature = "c_wrapper"))] 81 | fn time_create(state: *mut TimerState) -> ffi::timer_t { 82 | let mut event: libc::sigevent = unsafe { mem::zeroed() }; 83 | 84 | event.sigev_value = libc::sigval { 85 | sival_ptr: state as *mut _, 86 | }; 87 | event.sigev_signo = TIMER_SIG; 88 | //NOTE: Timer handler is invoked by signal handler 89 | // Therefore all limitations are applied to your waker. 90 | // To be safe we could use thread, but in this case 91 | // we cannot really hope for it to be optimal... 92 | event.sigev_notify = libc::SIGEV_SIGNAL; 93 | 94 | let mut res = mem::MaybeUninit::::uninit(); 95 | 96 | unsafe { 97 | os_assert!(ffi::timer_create(libc::CLOCK_REALTIME, &mut event, res.as_mut_ptr()) == 0); 98 | res.assume_init() 99 | } 100 | } 101 | 102 | fn set_timer_value(fd: ffi::timer_t, timeout: time::Duration) { 103 | let it_value = libc::timespec { 104 | tv_sec: timeout.as_secs() as libc::time_t, 105 | #[cfg(not(any(target_os = "openbsd", target_os = "netbsd")))] 106 | tv_nsec: timeout.subsec_nanos() as libc::suseconds_t, 107 | #[cfg(any(target_os = "openbsd", target_os = "netbsd"))] 108 | tv_nsec: timeout.subsec_nanos() as libc::c_long, 109 | }; 110 | 111 | let new_value = ffi::itimerspec { 112 | it_interval: unsafe { mem::zeroed() }, 113 | it_value, 114 | }; 115 | 116 | unsafe { 117 | os_assert!(ffi::timer_settime(fd, 0, &new_value, ptr::null_mut()) == 0); 118 | } 119 | } 120 | 121 | enum State { 122 | Init(time::Duration), 123 | Running(ffi::timer_t, Box), 124 | } 125 | 126 | ///Posix Timer 127 | /// 128 | ///When using `c_wrapper` feature implementation uses C shim to create timers which call callbacks 129 | ///from a separate thread. 130 | /// 131 | ///Without it, callback is called from signal handler which limits usable operations within the 132 | ///callback. 133 | pub struct PosixTimer { 134 | state: State, 135 | } 136 | 137 | impl PosixTimer { 138 | #[inline] 139 | ///Creates new instance 140 | pub const fn new(time: time::Duration) -> Self { 141 | Self { 142 | state: State::Init(time), 143 | } 144 | } 145 | } 146 | 147 | impl super::Timer for PosixTimer { 148 | #[inline(always)] 149 | fn new(timeout: time::Duration) -> Self { 150 | assert_time!(timeout); 151 | Self::new(timeout) 152 | } 153 | 154 | 155 | #[inline] 156 | fn is_ticking(&self) -> bool { 157 | match &self.state { 158 | State::Init(_) => false, 159 | State::Running(_, ref state) => !state.is_done(), 160 | } 161 | } 162 | 163 | #[inline] 164 | fn is_expired(&self) -> bool { 165 | match &self.state { 166 | State::Init(_) => false, 167 | State::Running(_, ref state) => state.is_done(), 168 | } 169 | } 170 | 171 | fn restart(&mut self, new_value: time::Duration) { 172 | assert_time!(new_value); 173 | 174 | match &mut self.state { 175 | State::Init(ref mut timeout) => { 176 | *timeout = new_value; 177 | }, 178 | State::Running(fd, ref mut state) => { 179 | state.reset(); 180 | set_timer_value(*fd, new_value); 181 | } 182 | } 183 | } 184 | 185 | fn restart_ctx(&mut self, new_value: time::Duration, waker: &task::Waker) { 186 | assert_time!(new_value); 187 | 188 | match &mut self.state { 189 | State::Init(ref mut timeout) => { 190 | *timeout = new_value; 191 | }, 192 | State::Running(fd, ref mut state) => { 193 | state.register(waker); 194 | state.reset(); 195 | set_timer_value(*fd, new_value); 196 | } 197 | } 198 | } 199 | 200 | fn cancel(&mut self) { 201 | match self.state { 202 | State::Init(_) => (), 203 | State::Running(fd, ref state) => unsafe { 204 | state.cancel(); 205 | ffi::timer_settime(fd, 0, &mut mem::zeroed(), ptr::null_mut()); 206 | } 207 | } 208 | } 209 | } 210 | 211 | impl super::SyncTimer for PosixTimer { 212 | fn init R>(&mut self, init: F) -> R { 213 | #[cfg(not(feature = "c_wrapper"))] 214 | { 215 | extern crate std; 216 | static RUNTIME: std::sync::Once = std::sync::Once::new(); 217 | RUNTIME.call_once(init_sig); 218 | } 219 | 220 | if let State::Init(timeout) = self.state { 221 | let state = Box::into_raw(Box::new(TimerState::new())); 222 | let fd = time_create(state); 223 | 224 | let state = unsafe { Box::from_raw(state) }; 225 | init(&state); 226 | 227 | set_timer_value(fd, timeout); 228 | 229 | self.state = State::Running(fd, state) 230 | } 231 | 232 | match &self.state { 233 | State::Running(_, ref state) => init(state), 234 | State::Init(_) => unreach!(), 235 | } 236 | } 237 | } 238 | 239 | impl Future for PosixTimer { 240 | type Output = (); 241 | 242 | #[inline] 243 | fn poll(self: Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll { 244 | crate::timer::poll_sync(self.get_mut(), ctx) 245 | } 246 | } 247 | 248 | impl Drop for PosixTimer { 249 | fn drop(&mut self) { 250 | match self.state { 251 | State::Init(_) => (), 252 | State::Running(fd, _) => unsafe { 253 | ffi::timer_delete(fd); 254 | } 255 | } 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /src/state.rs: -------------------------------------------------------------------------------- 1 | //!State module 2 | 3 | use core::{ptr, task, hint, mem}; 4 | use core::cell::UnsafeCell; 5 | use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; 6 | 7 | #[cold] 8 | fn should_not_clone(_: *const()) -> task::RawWaker { 9 | panic!("Impossible Waker Clone"); 10 | } 11 | 12 | mod plain_fn { 13 | use core::{task, mem}; 14 | 15 | static VTABLE: task::RawWakerVTable = task::RawWakerVTable::new(super::should_not_clone, action, action, super::noop::action); 16 | 17 | unsafe fn action(callback: *const ()) { 18 | let func: fn() = mem::transmute(callback); 19 | func() 20 | } 21 | 22 | pub fn waker(data: fn()) -> task::Waker { 23 | unsafe { 24 | task::Waker::from_raw(task::RawWaker::new(data as *const (), &VTABLE)) 25 | } 26 | } 27 | } 28 | 29 | mod noop { 30 | use core::{ptr, task}; 31 | 32 | static VTABLE: task::RawWakerVTable = task::RawWakerVTable::new(super::should_not_clone, action, action, action); 33 | 34 | pub fn action(_: *const ()) { 35 | } 36 | 37 | #[inline(always)] 38 | pub fn waker() -> task::Waker { 39 | unsafe { 40 | task::Waker::from_raw(task::RawWaker::new(ptr::null(), &VTABLE)) 41 | } 42 | } 43 | } 44 | 45 | /// Idle state 46 | const WAITING: u8 = 0; 47 | 48 | /// A new waker value is being registered with the `AtomicWaker` cell. 49 | const REGISTERING: u8 = 0b01; 50 | 51 | /// The waker currently registered with the `AtomicWaker` cell is being woken. 52 | const WAKING: u8 = 0b10; 53 | 54 | #[doc(hidden)] 55 | /// Atomic waker used by `TimerState` 56 | pub struct AtomicWaker { 57 | state: AtomicU8, 58 | waker: UnsafeCell, 59 | } 60 | 61 | struct StateRestore(F); 62 | impl Drop for StateRestore { 63 | fn drop(&mut self) { 64 | (self.0)() 65 | } 66 | } 67 | 68 | macro_rules! impl_register { 69 | ($this:ident($waker:ident) { $($impl:tt)+ }) => { 70 | match $this.state.compare_exchange(WAITING, REGISTERING, Ordering::Acquire, Ordering::Acquire).unwrap_or_else(|err| err) { 71 | WAITING => { 72 | //Make sure we do not stuck in REGISTERING state 73 | let state_guard = StateRestore(|| { 74 | $this.state.store(WAITING, Ordering::Release); 75 | }); 76 | 77 | unsafe { 78 | $( 79 | $impl 80 | )+ 81 | 82 | // Release the lock. If the state transitioned to include 83 | // the `WAKING` bit, this means that a wake has been 84 | // called concurrently, so we have to remove the waker and 85 | // wake it.` 86 | // 87 | // Start by assuming that the state is `REGISTERING` as this 88 | // is what we jut set it to. 89 | match $this.state.compare_exchange(REGISTERING, WAITING, Ordering::AcqRel, Ordering::Acquire) { 90 | Ok(_) => { 91 | mem::forget(state_guard); 92 | } 93 | Err(actual) => { 94 | // This branch can only be reached if a 95 | // concurrent thread called `wake`. In this 96 | // case, `actual` **must** be `REGISTERING | 97 | // `WAKING`. 98 | debug_assert_eq!(actual, REGISTERING | WAKING); 99 | 100 | let mut waker = noop::waker(); 101 | ptr::swap($this.waker.get(), &mut waker); 102 | 103 | // Just restore state, 104 | // because no one could change state while state == `REGISTERING` | `WAKING`. 105 | drop(state_guard); 106 | waker.wake(); 107 | } 108 | } 109 | } 110 | } 111 | WAKING => { 112 | // Currently in the process of waking the task, i.e., 113 | // `wake` is currently being called on the old task handle. 114 | // So, we call wake on the new waker 115 | $waker.wake_by_ref(); 116 | hint::spin_loop(); 117 | } 118 | state => { 119 | // In this case, a concurrent thread is holding the 120 | // "registering" lock. This probably indicates a bug in the 121 | // caller's code as racing to call `register` doesn't make much 122 | // sense. 123 | // 124 | // We just want to maintain memory safety. It is ok to drop the 125 | // call to `register`. 126 | debug_assert!( 127 | state == REGISTERING || 128 | state == REGISTERING | WAKING 129 | ); 130 | } 131 | } 132 | }; 133 | } 134 | 135 | impl AtomicWaker { 136 | fn new() -> Self { 137 | Self { 138 | state: AtomicU8::new(WAITING), 139 | waker: UnsafeCell::new(noop::waker()), 140 | } 141 | } 142 | 143 | ///This is the same function as `register` but working with owned version. 144 | fn register(&self, waker: task::Waker) { 145 | impl_register!(self(waker) { 146 | //unconditionally store since we already have ownership 147 | *self.waker.get() = waker; 148 | }); 149 | } 150 | 151 | fn register_ref(&self, waker: &task::Waker) { 152 | impl_register!(self(waker) { 153 | // Lock acquired, update the waker cell 154 | if !(*self.waker.get()).will_wake(waker) { 155 | //Clone new waker if it is definitely not the same as old one 156 | *self.waker.get() = waker.clone(); 157 | } 158 | }); 159 | } 160 | 161 | fn wake(&self) { 162 | // AcqRel ordering is used in order to acquire the value of the `task` 163 | // cell as well as to establish a `release` ordering with whatever 164 | // memory the `AtomicWaker` is associated with. 165 | match self.state.fetch_or(WAKING, Ordering::AcqRel) { 166 | WAITING => { 167 | // The waking lock has been acquired. 168 | let mut waker = noop::waker(); 169 | unsafe { 170 | ptr::swap(self.waker.get(), &mut waker); 171 | } 172 | 173 | // Release the lock 174 | self.state.fetch_and(!WAKING, Ordering::Release); 175 | waker.wake(); 176 | } 177 | state => { 178 | // There is a concurrent thread currently updating the 179 | // associated task. 180 | // 181 | // Nothing more to do as the `WAKING` bit has been set. It 182 | // doesn't matter if there are concurrent registering threads or 183 | // not. 184 | debug_assert!( 185 | state == REGISTERING || 186 | state == REGISTERING | WAKING || 187 | state == WAKING 188 | ); 189 | } 190 | } 191 | } 192 | } 193 | 194 | unsafe impl Send for AtomicWaker {} 195 | unsafe impl Sync for AtomicWaker {} 196 | 197 | ///Timer's state 198 | pub struct TimerState { 199 | woken: AtomicBool, 200 | inner: AtomicWaker, 201 | } 202 | 203 | impl TimerState { 204 | ///Initializes state. 205 | pub fn new() -> Self { 206 | Self { 207 | woken: AtomicBool::new(false), 208 | inner: AtomicWaker::new(), 209 | } 210 | } 211 | 212 | #[inline] 213 | ///Returns whether notification has been fired. 214 | /// 215 | ///Namely it checks whether `Waker` is registered 216 | ///with `TimerState` or not. It is not intended for user 217 | ///to call `is_done` before `register` 218 | pub fn is_done(&self) -> bool { 219 | self.woken.load(Ordering::Acquire) 220 | } 221 | 222 | #[inline] 223 | ///Resets state, allowing to wake once again. 224 | pub fn reset(&self) { 225 | self.woken.store(false, Ordering::Release); 226 | } 227 | 228 | #[inline] 229 | ///Informs that timer is cancel, therefore no further callbacks to be passed 230 | pub fn cancel(&self) { 231 | self.woken.store(true, Ordering::Release); 232 | } 233 | 234 | #[inline] 235 | ///Registers `Callback` with the state. 236 | /// 237 | ///This callback is used replaces previous one, if any. 238 | pub fn register(&self, cb: C) { 239 | cb.register(&self.inner); 240 | } 241 | 242 | #[inline] 243 | ///Notifies underlying `Waker` 244 | /// 245 | ///After that `Waker` is no longer registered with `TimerState` 246 | pub(crate) fn wake(&self) { 247 | if !self.woken.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst).unwrap_or_else(|err| err) { 248 | self.inner.wake(); 249 | } 250 | } 251 | } 252 | 253 | ///Interface to timer's callback 254 | /// 255 | ///It is guaranteed that callback is invoked only once, unless `Timer` is restarted or 256 | ///`TimerState::reset` is called(happens when timer is restarted) 257 | pub trait Callback { 258 | #[doc(hidden)] 259 | fn register(self, waker: &AtomicWaker); 260 | } 261 | 262 | impl<'a> Callback for &'a task::Waker { 263 | #[inline(always)] 264 | fn register(self, waker: &AtomicWaker) { 265 | waker.register_ref(self) 266 | } 267 | } 268 | 269 | impl Callback for task::Waker { 270 | #[inline(always)] 271 | fn register(self, waker: &AtomicWaker) { 272 | waker.register(self) 273 | } 274 | } 275 | 276 | impl Callback for fn() { 277 | fn register(self, waker: &AtomicWaker) { 278 | waker.register(plain_fn::waker(self)); 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /src/timer/async_tokio1.rs: -------------------------------------------------------------------------------- 1 | use tokio_1 as tokio; 2 | 3 | use tokio::io::unix::AsyncFd; 4 | use libc::c_int; 5 | 6 | use core::{task, time}; 7 | use core::pin::Pin; 8 | use core::future::Future; 9 | 10 | pub trait TimerFd: crate::std::os::unix::io::AsRawFd + Sync + Send + Unpin { 11 | fn new() -> Self; 12 | fn set(&mut self, time: time::Duration); 13 | fn unset(&mut self); 14 | fn read(&mut self) -> usize; 15 | } 16 | 17 | ///Wrapper over fd based timer. 18 | pub struct RawTimer(c_int); 19 | 20 | impl crate::std::os::unix::io::AsRawFd for RawTimer { 21 | #[inline(always)] 22 | fn as_raw_fd(&self) -> c_int { 23 | self.0 24 | } 25 | } 26 | 27 | impl Drop for RawTimer { 28 | #[inline(always)] 29 | fn drop(&mut self) { 30 | unsafe { 31 | libc::close(self.0); 32 | } 33 | } 34 | } 35 | 36 | #[cfg(target_os = "android")] 37 | mod sys { 38 | #[repr(C)] 39 | pub struct itimerspec { 40 | pub it_interval: libc::timespec, 41 | pub it_value: libc::timespec, 42 | } 43 | 44 | extern "C" { 45 | pub fn timerfd_create(clockid: libc::clockid_t, flags: libc::c_int) -> libc::c_int; 46 | pub fn timerfd_settime(timerid: libc::c_int, flags: libc::c_int, new_value: *const itimerspec, old_value: *mut itimerspec) -> libc::c_int; 47 | } 48 | 49 | pub const TFD_NONBLOCK: libc::c_int = libc::O_NONBLOCK; 50 | } 51 | 52 | #[cfg(target_os = "linux")] 53 | use libc as sys; 54 | 55 | #[cfg(any(target_os = "linux", target_os = "android"))] 56 | impl TimerFd for RawTimer { 57 | fn new() -> Self { 58 | let fd = unsafe { sys::timerfd_create(libc::CLOCK_MONOTONIC, sys::TFD_NONBLOCK) }; 59 | 60 | os_assert!(fd != -1); 61 | Self(fd) 62 | } 63 | 64 | fn set(&mut self, timeout: time::Duration) { 65 | #[cfg(not(target_pointer_width = "64"))] 66 | use core::convert::TryFrom; 67 | 68 | let it_value = libc::timespec { 69 | tv_sec: timeout.as_secs() as libc::time_t, 70 | #[cfg(target_pointer_width = "64")] 71 | tv_nsec: libc::suseconds_t::from(timeout.subsec_nanos()), 72 | #[cfg(not(target_pointer_width = "64"))] 73 | tv_nsec: libc::suseconds_t::try_from(timeout.subsec_nanos()).unwrap_or(libc::suseconds_t::max_value()), 74 | }; 75 | 76 | let timer = sys::itimerspec { 77 | it_interval: unsafe { core::mem::MaybeUninit::zeroed().assume_init() }, 78 | it_value, 79 | }; 80 | 81 | let ret = unsafe { sys::timerfd_settime(self.0, 0, &timer, core::ptr::null_mut()) }; 82 | os_assert!(ret != -1); 83 | } 84 | 85 | #[inline] 86 | fn unset(&mut self) { 87 | self.set(time::Duration::from_secs(0)); 88 | } 89 | 90 | fn read(&mut self) -> usize { 91 | let mut read_num = 0u64; 92 | match unsafe { libc::read(self.0, &mut read_num as *mut u64 as *mut _, 8) } { 93 | -1 => { 94 | let error = crate::std::io::Error::last_os_error(); 95 | match error.kind() { 96 | crate::std::io::ErrorKind::WouldBlock => 0, 97 | _ => panic!("Unexpected read error: {}", error), 98 | } 99 | } 100 | _ => read_num as usize, 101 | } 102 | } 103 | } 104 | 105 | #[cfg(any(target_os = "bitrig", target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "netbsd", target_os = "openbsd"))] 106 | impl TimerFd for RawTimer { 107 | fn new() -> Self { 108 | let fd = unsafe { 109 | libc::kqueue() 110 | }; 111 | 112 | //If you hit this, then most likely you run into OS imposed limit on file descriptor number 113 | os_assert!(fd != -1); 114 | Self(fd) 115 | } 116 | 117 | fn set(&mut self, time: time::Duration) { 118 | let timeout = libc::timespec { 119 | tv_sec: 0, 120 | tv_nsec: 0, 121 | }; 122 | let mut empty = []; 123 | let mut event = libc::kevent { 124 | ident: 1, 125 | filter: libc::EVFILT_TIMER, 126 | flags: libc::EV_ADD | libc::EV_ENABLE | libc::EV_ONESHOT, 127 | fflags: libc::NOTE_NSECONDS, 128 | data: 0, 129 | udata: core::ptr::null_mut(), 130 | }; 131 | 132 | let mut time = time.as_nanos(); 133 | if time > isize::max_value() as u128 { 134 | event.fflags = libc::NOTE_USECONDS; 135 | time /= 1_000; 136 | } 137 | if time > isize::max_value() as u128 { 138 | event.fflags = 0; //default value is ms 139 | time /= 1_000; 140 | } 141 | if time > isize::max_value() as u128 { 142 | event.fflags = libc::NOTE_SECONDS; 143 | time /= 1_000; 144 | } 145 | 146 | event.data = time as _; 147 | let set = unsafe { 148 | libc::kevent(self.0, &event, 1, empty.as_mut_ptr(), 0, &timeout) 149 | }; 150 | os_assert!(set != -1); 151 | } 152 | 153 | fn unset(&mut self) { 154 | let timeout = libc::timespec { 155 | tv_sec: 0, 156 | tv_nsec: 0, 157 | }; 158 | let mut empty = []; 159 | let event = libc::kevent { 160 | ident: 1, 161 | filter: libc::EVFILT_TIMER, 162 | flags: libc::EV_DELETE, 163 | fflags: 0, 164 | data: 0, 165 | udata: core::ptr::null_mut(), 166 | }; 167 | let unset = unsafe { 168 | libc::kevent(self.0, &event, 1, empty.as_mut_ptr(), 0, &timeout) 169 | }; 170 | os_assert!(unset != -1); 171 | } 172 | 173 | fn read(&mut self) -> usize { 174 | let timeout = libc::timespec { 175 | tv_sec: 0, 176 | tv_nsec: 0, 177 | }; 178 | let empty = []; 179 | let mut event = libc::kevent { 180 | ident: 0, 181 | filter: libc::EVFILT_TIMER, 182 | flags: 0, 183 | fflags: 0, 184 | data: 0, 185 | udata: core::ptr::null_mut(), 186 | }; 187 | let read = unsafe { 188 | libc::kevent(self.0, empty.as_ptr(), 0, &mut event, 1, &timeout) 189 | }; 190 | os_assert!(read != -1); 191 | read as _ 192 | } 193 | } 194 | 195 | enum State { 196 | Init(time::Duration), 197 | Running(T, bool), 198 | } 199 | 200 | ///Timer implemented on top of `AsyncFd` 201 | pub struct AsyncTokioTimer { 202 | state: State> 203 | } 204 | 205 | impl AsyncTokioTimer { 206 | #[inline] 207 | ///Creates new instance 208 | pub const fn new(time: time::Duration) -> Self { 209 | Self { 210 | state: State::Init(time), 211 | } 212 | } 213 | } 214 | 215 | impl super::Timer for AsyncTokioTimer { 216 | #[inline(always)] 217 | fn new(timeout: time::Duration) -> Self { 218 | assert_time!(timeout); 219 | debug_assert!(timeout.as_millis() <= u32::max_value().into()); 220 | Self { 221 | state: State::Init(timeout), 222 | } 223 | } 224 | 225 | #[inline] 226 | fn is_ticking(&self) -> bool { 227 | match &self.state { 228 | State::Init(_) => false, 229 | State::Running(_, state) => !*state, 230 | } 231 | } 232 | 233 | #[inline] 234 | fn is_expired(&self) -> bool { 235 | match &self.state { 236 | State::Init(_) => false, 237 | State::Running(_, state) => *state 238 | } 239 | } 240 | 241 | fn restart(&mut self, new_value: time::Duration) { 242 | assert_time!(new_value); 243 | debug_assert!(new_value.as_millis() <= u32::max_value().into()); 244 | 245 | match &mut self.state { 246 | State::Init(ref mut timeout) => { 247 | *timeout = new_value; 248 | }, 249 | State::Running(ref mut fd, ref mut state) => { 250 | *state = false; 251 | fd.get_mut().set(new_value); 252 | } 253 | } 254 | } 255 | 256 | #[inline(always)] 257 | fn restart_ctx(&mut self, new_value: time::Duration, _: &task::Waker) { 258 | self.restart(new_value) 259 | } 260 | 261 | fn cancel(&mut self) { 262 | unreachable!(); 263 | } 264 | } 265 | 266 | impl Future for AsyncTokioTimer { 267 | type Output = (); 268 | 269 | fn poll(mut self: Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll { 270 | if let State::Init(ref timeout) = &self.state { 271 | let mut fd = AsyncFd::with_interest(T::new(), tokio::io::Interest::READABLE).expect("To create AsyncFd"); 272 | fd.get_mut().set(*timeout); 273 | self.state = State::Running(fd, false) 274 | }; 275 | 276 | if let State::Running(ref mut fd, ref mut state) = &mut self.state { 277 | if *state { 278 | return task::Poll::Ready(()); 279 | } 280 | 281 | let fd = Pin::new(fd); 282 | match fd.poll_read_ready(ctx) { 283 | task::Poll::Pending => return task::Poll::Pending, 284 | task::Poll::Ready(ready) => { 285 | let mut ready = ready.expect("Unable to read async timer's fd"); 286 | //technically we should read first, but we cannot borrow as mut then 287 | ready.clear_ready(); 288 | 289 | match fd.get_mut().get_mut().read() { 290 | 0 => { 291 | *state = false; 292 | return task::Poll::Pending 293 | }, 294 | _ => { 295 | *state = true; 296 | return task::Poll::Ready(()) 297 | } 298 | } 299 | } 300 | } 301 | } else { 302 | unreach!(); 303 | } 304 | } 305 | } 306 | 307 | ///Timer based on tokio's `AsyncFd` 308 | pub type AsyncTimer = AsyncTokioTimer; 309 | --------------------------------------------------------------------------------