async_timer/timer/mod.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
//!Raw Timer
use core::{time, task};
use core::future::Future;
use crate::state::TimerState;
///Timer
///
///## Common implementations:
///
///- Windows uses thread pooled timer
///- Apple systems uses dispatch source API
///- Posix compatible `timer_create`, available on major Posix-compliant systems. Depends on availability of `siginfo_t::si_value` method.
///- Wasm uses Web API `SetTimeout`
///- Dummy timer is used when no implementation is available. Panics when used.
///
///## Usage
///
///```no_run
///use async_timer::timer::{Timer, new_timer};
///
///use core::time;
///use core::pin::Pin;
///
///async fn do_something() {
/// let mut work = new_timer(time::Duration::from_secs(2));
/// assert!(!work.is_ticking()); //Timer starts only on initial poll
/// assert!(!work.is_expired());
/// Pin::new(&mut work).await; //Remember await consumes future, and we'd prefer to avoid that in order to re-use timer
/// assert!(work.is_expired());
///
///}
///```
pub trait Timer: Send + Sync + Unpin + Future<Output=()> {
///Creates new instance
fn new(timeout: time::Duration) -> Self;
///Returns whether timer is ongoing.
///
///Note that if it returns `false` it doesn't mean that `is_expired` will return `true`
///as initially timer may not be armed.
fn is_ticking(&self) -> bool;
///Returns whether timer has expired.
fn is_expired(&self) -> bool;
///Restarts timer with new timeout value.
fn restart(&mut self, timeout: time::Duration);
///Restarts timer with new timeout value and waker.
fn restart_ctx(&mut self, timeout: time::Duration, waker: &task::Waker);
///Cancels timer, if it is still ongoing.
fn cancel(&mut self);
}
///Describes timer interface that doesn't require async event loop.
///
///In most cases these timers are implemented by OS calling back a provided callback.
///
///As notification is not done via event loop, timer has to store callback in its own state.
///
///Whenever async timer relies on async event loop to handle notifications
///
///## Usage
///
///```
///use async_timer::timer::{Timer, SyncTimer, new_sync_timer};
///
///use core::sync::atomic::{AtomicBool, Ordering};
///use core::time;
///
///use std::thread;
///
///static EXPIRED: AtomicBool = AtomicBool::new(false);
///fn on_expire() {
/// EXPIRED.store(true, Ordering::Release);
///}
///
///let mut work = new_sync_timer(time::Duration::from_secs(1));
///assert!(!work.is_ticking());
///assert!(!work.is_expired());
///
///work.init(|state| state.register(on_expire as fn()));
///work.tick();
///
///assert!(work.is_ticking());
///assert!(!work.is_expired());
///thread::sleep(time::Duration::from_millis(1250)); //timer is not necessary expires immediately
///
///assert!(work.is_expired());
///assert!(EXPIRED.load(Ordering::Acquire));
///```
///
pub trait SyncTimer: Timer {
///Initializes timer state, performing initial arming and allowing to access `TimerState`
///during initialization
///
///The state can be used to register callback and check whether timer has notified user using
///configured callback
///
///If `Timer` is already armed, then `TimerState` is granted as it is.
fn init<R, F: Fn(&TimerState) -> R>(&mut self, init: F) -> R;
///Ticks timer.
///
///If timer is not started yet, starts returning false.
///Otherwise performs necessary actions, if any, to drive timer
///and returns whether it is expired.
///
///Default implementation initializes timer state, if necessary, and
///returns whether timer has expired or not.
#[inline(always)]
fn tick(&mut self) -> bool {
self.init(|state| state.is_done())
}
}
#[inline(always)]
fn poll_sync<T: SyncTimer>(timer: &mut T, ctx: &mut task::Context) -> task::Poll<()> {
timer.init(|state| {
state.register(ctx.waker());
match state.is_done() {
true => task::Poll::Ready(()),
false => task::Poll::Pending
}
})
}
#[cfg(windows)]
mod win;
#[cfg(windows)]
pub use win::WinTimer;
#[cfg(windows)]
///Platform alias to Windows timer
pub type Platform = win::WinTimer;
#[cfg(windows)]
///Platform alias to Windows timer
pub type SyncPlatform = win::WinTimer;
#[cfg(all(feature = "tokio1", unix))]
mod async_tokio1;
#[cfg(all(feature = "tokio1", unix))]
pub use async_tokio1::AsyncTimer;
#[cfg(all(feature = "tokio1", unix))]
///Timer based on tokio's `AsyncFd`
pub type Platform = AsyncTimer;
#[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))]
mod posix;
#[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))]
pub use posix::PosixTimer;
#[cfg(all(not(feature = "tokio1"), not(any(target_os = "macos", target_os = "ios")), unix))]
///Platform alias to POSIX timer
pub type Platform = posix::PosixTimer;
#[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))]
///Platform alias to POSIX Timer
pub type SyncPlatform = posix::PosixTimer;
#[cfg(any(target_os = "macos", target_os = "ios"))]
mod apple;
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub use apple::AppleTimer;
#[cfg(all(not(feature = "tokio1"), any(target_os = "macos", target_os = "ios")))]
///Platform alias to Apple Dispatch timer
pub type Platform = apple::AppleTimer;
#[cfg(any(target_os = "macos", target_os = "ios"))]
///Platform Alias to `kqueue` based Timer
pub type SyncPlatform = apple::AppleTimer;
#[cfg(target_arch = "wasm32")]
mod web;
#[cfg(target_arch = "wasm32")]
pub use web::WebTimer;
#[cfg(target_arch = "wasm32")]
///Platform alias to WASM Timer
pub type Platform = web::WebTimer;
#[cfg(target_arch = "wasm32")]
///Platform alias to WASM Timer
pub type SyncPlatform = web::WebTimer;
mod dummy;
pub use dummy::DummyTimer;
#[cfg(not(any(windows, target_arch = "wasm32", unix)))]
///Platform alias to Dummy Timer as no OS implementation is available.
pub type Platform = dummy::DummyTimer;
#[cfg(not(any(windows, target_arch = "wasm32", unix)))]
///Platform alias to Dummy Timer as no OS implementation is available.
pub type SyncPlatform = dummy::DummyTimer;
#[inline]
///Creates new timer, timer type depends on platform.
pub const fn new_timer(timeout: time::Duration) -> Platform {
Platform::new(timeout)
}
#[inline]
///Creates new timer, which always implements `SyncTimer`
pub const fn new_sync_timer(timeout: time::Duration) -> SyncPlatform {
SyncPlatform::new(timeout)
}