├── .gitignore ├── examples └── nodejs-example │ ├── .gitignore │ ├── .github │ └── dependabot.yml │ ├── README.md │ ├── tests │ └── web.rs │ ├── package.json │ ├── .appveyor.yml │ ├── src │ ├── utils.rs │ └── lib.rs │ ├── index.js │ ├── Cargo.toml │ ├── LICENSE_MIT │ ├── .travis.yml │ └── LICENSE_APACHE ├── .vim └── coc-settings.json ├── src ├── tokio │ ├── mod.rs │ ├── error.rs │ ├── test_utils.rs │ ├── timeout.rs │ ├── interval.rs │ └── sleep.rs ├── tokio_util │ ├── wheel │ │ ├── stack.rs │ │ ├── level.rs │ │ └── mod.rs │ ├── mod.rs │ └── delay_queue.rs ├── lib.rs ├── js.rs ├── timer │ ├── global.rs │ ├── clock.rs │ ├── arc_list.rs │ ├── heap.rs │ └── mod.rs └── std.rs ├── .github └── workflows │ ├── publish.yml │ └── main.yml ├── LICENSE ├── Cargo.toml ├── README.md ├── CHANGELOG.md └── tests └── web.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock -------------------------------------------------------------------------------- /examples/nodejs-example/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | bin/ 5 | pkg/ 6 | wasm-pack.log 7 | -------------------------------------------------------------------------------- /.vim/coc-settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.cargo.target": "wasm32-unknown-unknown", 3 | "rust-analyzer.cargo.features": ["tokio-test-util", "serde"], 4 | "rust-analyzer.procMacro.enable": true 5 | } 6 | -------------------------------------------------------------------------------- /examples/nodejs-example/.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "08:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /examples/nodejs-example/README.md: -------------------------------------------------------------------------------- 1 | # Wasmtimer NodeJS Example 2 | 3 | ## Building the project 4 | 5 | ``` 6 | cd examples/nodejs-example/ 7 | wasm-pack build --target nodejs 8 | ``` 9 | 10 | ## Run the project 11 | 12 | ``` 13 | node index.js 14 | ``` 15 | -------------------------------------------------------------------------------- /src/tokio/mod.rs: -------------------------------------------------------------------------------- 1 | mod sleep; 2 | pub use sleep::*; 3 | 4 | mod interval; 5 | pub use interval::*; 6 | 7 | mod timeout; 8 | pub use timeout::*; 9 | 10 | #[cfg(feature = "tokio-test-util")] 11 | mod test_utils; 12 | #[cfg(feature = "tokio-test-util")] 13 | pub use test_utils::*; 14 | 15 | pub mod error; 16 | -------------------------------------------------------------------------------- /examples/nodejs-example/tests/web.rs: -------------------------------------------------------------------------------- 1 | //! Test suite for the Web and headless browsers. 2 | 3 | #![cfg(target_arch = "wasm32")] 4 | 5 | extern crate wasm_bindgen_test; 6 | use wasm_bindgen_test::*; 7 | 8 | wasm_bindgen_test_configure!(run_in_browser); 9 | 10 | #[wasm_bindgen_test] 11 | fn pass() { 12 | assert_eq!(1 + 1, 2); 13 | } 14 | -------------------------------------------------------------------------------- /examples/nodejs-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs-example", 3 | "version": "1.0.0", 4 | "description": "Testing Wamtimer With NodeJS", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "tests" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "", 13 | "license": "MIT", 14 | "type": "module" 15 | } 16 | -------------------------------------------------------------------------------- /examples/nodejs-example/.appveyor.yml: -------------------------------------------------------------------------------- 1 | install: 2 | - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe 3 | - if not defined RUSTFLAGS rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain nightly 4 | - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin 5 | - rustc -V 6 | - cargo -V 7 | 8 | build: false 9 | 10 | test_script: 11 | - cargo test --locked 12 | -------------------------------------------------------------------------------- /examples/nodejs-example/src/utils.rs: -------------------------------------------------------------------------------- 1 | pub fn set_panic_hook() { 2 | // When the `console_error_panic_hook` feature is enabled, we can call the 3 | // `set_panic_hook` function at least once during initialization, and then 4 | // we will get better error messages if our code ever panics. 5 | // 6 | // For more details see 7 | // https://github.com/rustwasm/console_error_panic_hook#readme 8 | #[cfg(feature = "console_error_panic_hook")] 9 | console_error_panic_hook::set_once(); 10 | } 11 | -------------------------------------------------------------------------------- /src/tokio_util/wheel/stack.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Borrow; 2 | use std::cmp::Eq; 3 | use std::hash::Hash; 4 | 5 | pub(crate) trait Stack: Default { 6 | type Owned: Borrow; 7 | type Borrowed: Eq + Hash; 8 | type Store; 9 | fn is_empty(&self) -> bool; 10 | fn push(&mut self, item: Self::Owned, store: &mut Self::Store); 11 | fn pop(&mut self, store: &mut Self::Store) -> Option; 12 | fn remove(&mut self, item: &Self::Borrowed, store: &mut Self::Store); 13 | fn when(item: &Self::Borrowed, store: &Self::Store) -> u64; 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - 'v[0-9]+.[0-9]+.[0-9]+' 5 | workflow_dispatch: 6 | 7 | name: Publish 8 | 9 | jobs: 10 | publish: 11 | name: Publish 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout sources 15 | uses: actions/checkout@v2 16 | 17 | - name: Install stable toolchain 18 | uses: actions-rs/toolchain@v1 19 | with: 20 | profile: minimal 21 | toolchain: stable 22 | override: true 23 | 24 | - run: cargo publish --token ${CRATES_TOKEN} 25 | env: 26 | CRATES_TOKEN: ${{ secrets.CRATES_TOKEN }} 27 | -------------------------------------------------------------------------------- /examples/nodejs-example/index.js: -------------------------------------------------------------------------------- 1 | // index.js 2 | import * as example from "./pkg/nodejs_example.js"; 3 | 4 | (async () => { 5 | console.log("Sleeping JS"); 6 | await example.sleep_test(); 7 | console.log("Slept JS"); 8 | 9 | console.log("Interval JS"); 10 | await example.interval_test(); 11 | console.log("Interval End JS"); 12 | 13 | console.log("Timeout Start JS"); 14 | await example.timeout_test(); 15 | console.log("Timeout Finished JS"); 16 | 17 | console.log("Arc Leak Test JS"); 18 | await example.arc_leak_test(); 19 | console.log("Arc Leak Tested JS"); 20 | 21 | process.exit(0); 22 | })().catch((e) => { 23 | console.error(e); 24 | }); 25 | -------------------------------------------------------------------------------- /src/tokio_util/mod.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | mod wheel; 4 | 5 | pub mod delay_queue; 6 | 7 | #[doc(inline)] 8 | pub use delay_queue::DelayQueue; 9 | 10 | // ===== Internal utils ===== 11 | 12 | enum Round { 13 | Up, 14 | Down, 15 | } 16 | 17 | #[inline] 18 | fn ms(duration: Duration, round: Round) -> u64 { 19 | const NANOS_PER_MILLI: u32 = 1_000_000; 20 | const MILLIS_PER_SEC: u64 = 1_000; 21 | 22 | // Round up. 23 | let millis = match round { 24 | Round::Up => duration.subsec_nanos().div_ceil(NANOS_PER_MILLI), 25 | Round::Down => duration.subsec_millis(), 26 | }; 27 | 28 | duration 29 | .as_secs() 30 | .saturating_mul(MILLIS_PER_SEC) 31 | .saturating_add(u64::from(millis)) 32 | } 33 | -------------------------------------------------------------------------------- /src/tokio/error.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// Errors returned by `Timeout`. 4 | /// 5 | /// This error is returned when a timeout expires before the function was able 6 | /// to finish. 7 | #[derive(Debug, PartialEq, Eq)] 8 | pub struct Elapsed(()); 9 | 10 | // ===== impl Elapsed ===== 11 | 12 | impl Elapsed { 13 | pub(crate) fn new() -> Self { 14 | Elapsed(()) 15 | } 16 | } 17 | 18 | impl fmt::Display for Elapsed { 19 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 20 | "deadline has elapsed".fmt(fmt) 21 | } 22 | } 23 | 24 | impl std::error::Error for Elapsed {} 25 | 26 | impl From for std::io::Error { 27 | fn from(_err: Elapsed) -> std::io::Error { 28 | std::io::ErrorKind::TimedOut.into() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/tokio/test_utils.rs: -------------------------------------------------------------------------------- 1 | use crate::timer::TimerHandle; 2 | use std::time::Duration; 3 | 4 | use crate::timer::clock::clock; 5 | 6 | pub fn pause() { 7 | let clock = clock(); 8 | clock.pause(); 9 | let handle = TimerHandle::default(); 10 | if let Some(inner) = handle.inner.upgrade() { 11 | inner.waker.wake(); 12 | } 13 | } 14 | 15 | pub fn resume() { 16 | let clock = clock(); 17 | clock.resume(); 18 | let handle = TimerHandle::default(); 19 | if let Some(inner) = handle.inner.upgrade() { 20 | inner.waker.wake(); 21 | } 22 | } 23 | 24 | pub async fn advance(duration: Duration) { 25 | let clock = clock(); 26 | clock.advance(duration); 27 | let handle = TimerHandle::default(); 28 | if let Some(inner) = handle.inner.upgrade() { 29 | inner.waker.wake(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/nodejs-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nodejs-example" 3 | version = "0.1.0" 4 | authors = ["WhizSid "] 5 | edition = "2018" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [features] 11 | default = ["console_error_panic_hook"] 12 | 13 | [dependencies] 14 | wasm-bindgen = "0.2.84" 15 | wasm-bindgen-futures = "0.4" 16 | wasmtimer = { path = "../../" } 17 | web-sys = {version = "0.3", features = ["console", "Window", "Performance"]} 18 | 19 | # The `console_error_panic_hook` crate provides better debugging of panics by 20 | # logging them with `console.error`. This is great for development, but requires 21 | # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for 22 | # code size when deploying. 23 | console_error_panic_hook = { version = "0.1.7", optional = true } 24 | 25 | [dev-dependencies] 26 | wasm-bindgen-test = "0.3.34" 27 | 28 | [profile.release] 29 | # Tell `rustc` to optimize for small code size. 30 | opt-level = "s" 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Pierre Krieger 2 | Copyright (c) 2019 Tokio Contributors 3 | Copyright 2023 Ramesh Kithsiri 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 18 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 19 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /examples/nodejs-example/LICENSE_MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 WhizSid 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasmtimer" 3 | edition = "2021" 4 | description = "Time utils from std::time, tokio::time and tokio_util::time on WASM targets" 5 | version = "0.4.3" 6 | authors = ["WhizSid ", "Pierre Krieger "] 7 | license = "MIT" 8 | repository = "https://github.com/whizsid/wasmtimer-rs" 9 | keywords = ["timer", "wasm", "tokio", "browser", "delay-queue"] 10 | 11 | [dependencies] 12 | futures = {version= "^0.3", optional = true} 13 | parking_lot = {version= "^0.12", optional = true } 14 | pin-utils = {version = "^0.1", optional = true } 15 | js-sys = "^0.3" 16 | wasm-bindgen = "^0.2" 17 | slab = { version = "^0.4", optional = true } 18 | serde_crate = { package = "serde" , version = "^1.0", optional = true, default-features = false } 19 | 20 | [features] 21 | default = ["tokio", "tokio-util"] 22 | tokio-test-util = ["tokio"] 23 | tokio-util = ["slab", "tokio"] 24 | tokio = ["futures", "parking_lot", "pin-utils"] 25 | serde = ["serde_crate"] 26 | 27 | [dev-dependencies] 28 | wasm-bindgen-test = "0.3.0" 29 | wasm-bindgen-futures = "0.4" 30 | serde_json = "^1.0" 31 | 32 | [lib] 33 | crate-type = ["cdylib", "rlib"] 34 | 35 | [workspace] 36 | members = ["examples/nodejs-example"] 37 | resolver = "2" 38 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Pierre Krieger 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | mod js; 21 | pub mod std; 22 | #[cfg(feature = "tokio")] 23 | pub(crate) mod timer; 24 | #[cfg(feature = "tokio")] 25 | pub mod tokio; 26 | #[cfg(feature = "tokio-util")] 27 | pub mod tokio_util; 28 | -------------------------------------------------------------------------------- /src/js.rs: -------------------------------------------------------------------------------- 1 | use js_sys::Object; 2 | use wasm_bindgen::{prelude::wasm_bindgen, JsCast}; 3 | 4 | #[wasm_bindgen] 5 | extern "C" { 6 | pub type GlobalScope; 7 | 8 | pub type Performance; 9 | 10 | #[wasm_bindgen(structural, method, getter, js_name = "performance")] 11 | pub fn performance(this: &GlobalScope) -> Performance; 12 | 13 | #[wasm_bindgen(method, js_name = "now")] 14 | pub fn now(this: &Performance) -> f64; 15 | 16 | #[cfg(feature = "tokio")] 17 | #[wasm_bindgen(catch , method, js_name = setTimeout)] 18 | pub fn set_timeout_with_callback_and_timeout_and_arguments_0( 19 | this: &GlobalScope, 20 | handler: &::js_sys::Function, 21 | timeout: i32, 22 | ) -> Result; 23 | } 24 | 25 | pub fn performance_now() -> f64 { 26 | let global_this: Object = js_sys::global(); 27 | let global_scope = global_this.unchecked_ref::(); 28 | global_scope.performance().now() 29 | } 30 | 31 | #[cfg(feature = "tokio")] 32 | pub fn set_timeout( 33 | handler: &::js_sys::Function, 34 | timeout: i32, 35 | ) -> Result { 36 | let global_this: Object = js_sys::global(); 37 | let global_scope = global_this.unchecked_ref::(); 38 | global_scope.set_timeout_with_callback_and_timeout_and_arguments_0(handler, timeout) 39 | } 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wasmtimer-rs 2 | 3 | An implementation of `time` based functionalities from `std::time`, `tokio::time`, 4 | `tokio_util::time` for WASM targets. This crate tries to closely 5 | replicate above APIs. Users only have to change their `use` scripts by 6 | using a `cfg` macro. 7 | 8 | ```rust 9 | #[cfg(not(target_family="wasm"))] 10 | use tokio::time::*; 11 | #[cfg(target_family="wasm")] 12 | use wasmtimer::tokio::*; 13 | ``` 14 | 15 | Check the [API Documentation](https://docs.rs/wasmtimer) for more 16 | details. 17 | 18 | ## Story 19 | 20 | Core idea and core modules in `src/timer` folder were copied from 21 | [this](https://github.com/tomaka/wasm-timer) crate. This crate is 22 | abandoned now due to lack of maintainability. I've hard forked it, 23 | added some additional features and released to use for 24 | [this](https://github.com/google/tarpc/pull/388) PR. 25 | 26 | ## `tokio::time` vs `wasmtimer` 27 | 28 | - `wasmtimer` is only running on WASM browser targets and not using any 29 | `tokio` feature as a dependency. 30 | - This timer crate not supporting 31 | [Auto-advance](https://docs.rs/tokio/latest/tokio/time/fn.pause.html#auto-advance) 32 | like in the tokio crate. Because we can not track the background 33 | tasks(`Promise`) in browser scope. If we implemented such without caring 34 | about background tasks, then this implementation will not match with the 35 | tokio's original implementation. 36 | 37 | ## Features 38 | 39 | - Serde Support (`serde` feature flag) 40 | - Worker and NodeJS Support 41 | - Test Utilities 42 | -------------------------------------------------------------------------------- /src/tokio/timeout.rs: -------------------------------------------------------------------------------- 1 | use std::{task::Poll, time::Duration}; 2 | 3 | use futures::Future; 4 | use pin_utils::unsafe_pinned; 5 | 6 | use crate::std::Instant; 7 | 8 | use super::{error::Elapsed, Sleep}; 9 | 10 | pub struct Timeout { 11 | delay: Sleep, 12 | future: T, 13 | } 14 | 15 | impl Timeout 16 | where 17 | F: Future, 18 | { 19 | unsafe_pinned!(future: F); 20 | unsafe_pinned!(delay: Sleep); 21 | 22 | pub(crate) fn new(dur: Duration, fut: F) -> Timeout { 23 | Timeout { 24 | delay: Sleep::new(dur), 25 | future: fut, 26 | } 27 | } 28 | 29 | pub(crate) fn new_at(at: Instant, fut: F) -> Timeout { 30 | Timeout { 31 | delay: Sleep::new_at(at), 32 | future: fut, 33 | } 34 | } 35 | 36 | pub fn get_ref(&self) -> &F { 37 | &self.future 38 | } 39 | 40 | pub fn get_mut(&mut self) -> &mut F { 41 | &mut self.future 42 | } 43 | 44 | pub fn into_inner(self) -> F { 45 | self.future 46 | } 47 | } 48 | 49 | impl Future for Timeout 50 | where 51 | T: Future, 52 | { 53 | type Output = Result; 54 | 55 | fn poll( 56 | mut self: std::pin::Pin<&mut Self>, 57 | cx: &mut std::task::Context<'_>, 58 | ) -> Poll { 59 | match self.as_mut().future().poll(cx) { 60 | Poll::Pending => {} 61 | Poll::Ready(other) => return Poll::Ready(Ok(other)), 62 | } 63 | 64 | if self.delay().poll(cx).is_ready() { 65 | Poll::Ready(Err(Elapsed::new())) 66 | } else { 67 | Poll::Pending 68 | } 69 | } 70 | } 71 | 72 | pub fn timeout(duration: Duration, future: F) -> Timeout 73 | where 74 | F: Future, 75 | { 76 | Timeout::new(duration, future) 77 | } 78 | 79 | pub fn timeout_at(deadline: Instant, future: F) -> Timeout 80 | where 81 | F: Future, 82 | { 83 | Timeout::new_at(deadline, future) 84 | } 85 | -------------------------------------------------------------------------------- /examples/nodejs-example/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod utils; 2 | 3 | use std::time::Duration; 4 | 5 | use wasm_bindgen::prelude::*; 6 | use wasmtimer::std::Instant; 7 | use wasmtimer::tokio::{interval, sleep, sleep_until, timeout}; 8 | use web_sys::console::log_1; 9 | use web_sys::window; 10 | 11 | #[wasm_bindgen] 12 | pub async fn sleep_test() { 13 | log_1(&JsValue::from_str("Sleeping Rust")); 14 | sleep(Duration::from_secs(3)).await; 15 | log_1(&JsValue::from_str("Slept Rust")); 16 | } 17 | 18 | #[wasm_bindgen] 19 | pub async fn interval_test() { 20 | log_1(&JsValue::from_str("Interval Rust 1")); 21 | let mut interval = interval(Duration::from_secs(3)); 22 | interval.tick().await; 23 | log_1(&JsValue::from_str("Interval Rust 2")); 24 | interval.tick().await; 25 | log_1(&JsValue::from_str("Interval Rust 3")); 26 | interval.tick().await; 27 | log_1(&JsValue::from_str("Interval Rust 4")); 28 | interval.tick().await; 29 | log_1(&JsValue::from_str("Interval Rust 5")); 30 | drop(interval); 31 | } 32 | 33 | #[wasm_bindgen] 34 | pub async fn timeout_test() { 35 | log_1(&JsValue::from_str("Timeout Start Rust")); 36 | let result = timeout(Duration::from_secs(1), async { 37 | log_1(&JsValue::from_str("Timeout Callback Rust")); 38 | sleep(Duration::from_secs(3)).await; 39 | log_1(&JsValue::from_str("Timeout Failed 1 Rust")); 40 | }) 41 | .await; 42 | match result { 43 | Ok(_) => { 44 | log_1(&JsValue::from_str("Timeout Failed 2 Rust")); 45 | } 46 | Err(e) => { 47 | log_1(&JsValue::from_str(&format!( 48 | "Timeout Success. Error:- {:?}", 49 | e 50 | ))); 51 | } 52 | } 53 | } 54 | 55 | #[wasm_bindgen] 56 | extern "C" { 57 | #[wasm_bindgen(js_namespace = ["globalThis", "performance"])] 58 | fn now() -> f64; 59 | } 60 | 61 | #[wasm_bindgen] 62 | pub async fn arc_leak_test() { 63 | let duration = Duration::from_millis(50); 64 | for _ in 0..500 { 65 | let deadline = Instant::now() + duration; 66 | sleep_until(deadline).await; 67 | let ms = now(); 68 | log_1(&JsValue::from_f64(ms)); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 0.4.3 4 | 5 | - Update Instant derives [#29](https://github.com/whizsid/wasmtimer-rs/pull/29). 6 | 7 | ## 0.4.2 8 | 9 | - Fixed 'sleep_until() does not wake up until 5 seconds' issue [#22](https://github.com/whizsid/wasmtimer-rs/issues/22), [#27](https://github.com/whizsid/wasmtimer-rs/pull/27). 10 | 11 | ## 0.4.1 12 | 13 | - Fixing the `set_timeout` failure on dev profile [#18](https://github.com/whizsid/wasmtimer-rs/issues/18), [#23](https://github.com/whizsid/wasmtimer-rs/pull/23). 14 | 15 | ## 0.4.0 16 | 17 | - Added a NodeJS example project [#19](https://github.com/whizsid/wasmtimer-rs/pull/19). 18 | - Changed the behavior of `tokio::interval` to do the first tick immediately as in the original `tokio` crate [#20](https://github.com/whizsid/wasmtimer-rs/pull/20), [#12](https://github.com/whizsid/wasmtimer-rs/issues/12). 19 | 20 | ## 0.3.0 21 | 22 | - Added new methods (`checked_duration_since`, `saturating_duration_since`, `checked_add` and `checked_sub`) to `std::time::Instant`. [#16](https://github.com/whizsid/wasmtimer-rs/pull/16), [#9](https://github.com/whizsid/wasmtimer-rs/issues/9). 23 | - Fixed micro seconds precision lost issue. [#10](https://github.com/whizsid/wasmtimer-rs/issues/10) 24 | 25 | ## 0.2.1 26 | 27 | - Fixed the clippy warnings [#14](https://github.com/whizsid/wasmtimer-rs/pull/14). 28 | - Implemented the `Debug`, `Copy`, `Clone` and `Display` traits for `std::time::SystemTime` [#13](https://github.com/whizsid/wasmtimer-rs/pull/13). 29 | - Fixed the example on README file [#11](https://github.com/whizsid/wasmtimer-rs/pull/11). 30 | 31 | ## 0.2.0 32 | 33 | - Used `performance.now()` and `setTimeout` from global scope instead of window [#7](https://github.com/whizsid/wasmtimer-rs/pull/7). 34 | - NodeJS Support [#3](https://github.com/whizsid/wasmtimer-rs/issues/3). 35 | - Web Worker Support [#5](https://github.com/whizsid/wasmtimer-rs/issues/5). 36 | 37 | ## 0.1.0 38 | 39 | - Added serde support under a feature flag `serde` [#6](https://github.com/whizsid/wasmtimer-rs/pull/6), [#1](https://github.com/whizsid/wasmtimer-rs/issues/1). 40 | - Changed the version requirements of dependencies to match future versions. 41 | 42 | ## 0.0.1 43 | 44 | - Initial version with tokio, tokio_util, std APIs 45 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | pull_request: 6 | branches: 7 | - master 8 | 9 | name: Continuous integration 10 | 11 | jobs: 12 | check: 13 | name: Check 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | - uses: dtolnay/rust-toolchain@stable 18 | - run: cargo check 19 | - run: cargo check --no-default-features --features tokio-util 20 | - run: cargo check --no-default-features --features tokio-test-util 21 | - run: cargo check --no-default-features --features tokio 22 | - run: cargo check --no-default-features --features serde 23 | - run: cargo check --no-default-features 24 | 25 | unittest: 26 | name: Unit Test 27 | runs-on: ubuntu-latest 28 | steps: 29 | - uses: actions/checkout@v3 30 | - uses: dtolnay/rust-toolchain@stable 31 | - run: cargo test 32 | - run: cargo test --no-default-features --features tokio-util 33 | - run: cargo test --no-default-features --features tokio-test-util 34 | - run: cargo test --no-default-features --features tokio 35 | - run: cargo test --no-default-features --features serde 36 | - run: cargo test --no-default-features 37 | 38 | inttestbrowser: 39 | name: Integration Test Browser 40 | runs-on: ubuntu-latest 41 | env: 42 | RUSTFLAGS: "--cfg browser" 43 | steps: 44 | - uses: actions/checkout@v3 45 | - uses: dtolnay/rust-toolchain@stable 46 | - name: Install 47 | run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh 48 | 49 | - run: wasm-pack test --headless --firefox --features tokio-test-util,serde 50 | - run: wasm-pack test --headless --chrome --features tokio-test-util,serde 51 | inttestnode: 52 | name: Integration Test Node 53 | runs-on: ubuntu-latest 54 | steps: 55 | - uses: actions/checkout@v3 56 | - uses: dtolnay/rust-toolchain@stable 57 | - name: Install 58 | run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh 59 | - run: wasm-pack test --node --features tokio-test-util,serde 60 | fmt: 61 | name: Rustfmt 62 | runs-on: ubuntu-latest 63 | steps: 64 | - uses: actions/checkout@v3 65 | - uses: dtolnay/rust-toolchain@stable 66 | with: 67 | components: rustfmt 68 | - run: cargo fmt --all -- --check 69 | 70 | clippy: 71 | name: Clippy 72 | runs-on: ubuntu-latest 73 | steps: 74 | - uses: actions/checkout@v3 75 | - uses: dtolnay/rust-toolchain@stable 76 | with: 77 | components: clippy 78 | - run: cargo clippy --all-features -- -D warnings 79 | -------------------------------------------------------------------------------- /examples/nodejs-example/.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | 4 | cache: cargo 5 | 6 | matrix: 7 | include: 8 | 9 | # Builds with wasm-pack. 10 | - rust: beta 11 | env: RUST_BACKTRACE=1 12 | addons: 13 | firefox: latest 14 | chrome: stable 15 | before_script: 16 | - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) 17 | - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) 18 | - cargo install-update -a 19 | - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f 20 | script: 21 | - cargo generate --git . --name testing 22 | # Having a broken Cargo.toml (in that it has curlies in fields) anywhere 23 | # in any of our parent dirs is problematic. 24 | - mv Cargo.toml Cargo.toml.tmpl 25 | - cd testing 26 | - wasm-pack build 27 | - wasm-pack test --chrome --firefox --headless 28 | 29 | # Builds on nightly. 30 | - rust: nightly 31 | env: RUST_BACKTRACE=1 32 | before_script: 33 | - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) 34 | - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) 35 | - cargo install-update -a 36 | - rustup target add wasm32-unknown-unknown 37 | script: 38 | - cargo generate --git . --name testing 39 | - mv Cargo.toml Cargo.toml.tmpl 40 | - cd testing 41 | - cargo check 42 | - cargo check --target wasm32-unknown-unknown 43 | - cargo check --no-default-features 44 | - cargo check --target wasm32-unknown-unknown --no-default-features 45 | - cargo check --no-default-features --features console_error_panic_hook 46 | - cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook 47 | - cargo check --no-default-features --features "console_error_panic_hook wee_alloc" 48 | - cargo check --target wasm32-unknown-unknown --no-default-features --features "console_error_panic_hook wee_alloc" 49 | 50 | # Builds on beta. 51 | - rust: beta 52 | env: RUST_BACKTRACE=1 53 | before_script: 54 | - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) 55 | - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) 56 | - cargo install-update -a 57 | - rustup target add wasm32-unknown-unknown 58 | script: 59 | - cargo generate --git . --name testing 60 | - mv Cargo.toml Cargo.toml.tmpl 61 | - cd testing 62 | - cargo check 63 | - cargo check --target wasm32-unknown-unknown 64 | - cargo check --no-default-features 65 | - cargo check --target wasm32-unknown-unknown --no-default-features 66 | - cargo check --no-default-features --features console_error_panic_hook 67 | - cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook 68 | # Note: no enabling the `wee_alloc` feature here because it requires 69 | # nightly for now. 70 | -------------------------------------------------------------------------------- /src/timer/global.rs: -------------------------------------------------------------------------------- 1 | use futures::task::{self, ArcWake}; 2 | use parking_lot::Mutex; 3 | use std::convert::TryFrom; 4 | use std::future::Future; 5 | use std::pin::Pin; 6 | use std::sync::Arc; 7 | use std::task::Context; 8 | use std::time::Duration; 9 | use wasm_bindgen::{closure::Closure, JsCast}; 10 | 11 | use crate::js::set_timeout; 12 | use crate::std::Instant; 13 | use crate::timer::{Timer, TimerHandle}; 14 | 15 | /// Starts a background task, creates a `Timer`, and returns a handle to it. 16 | /// 17 | /// > **Note**: Contrary to the original `futures-timer` crate, we don't have 18 | /// > any `forget()` method, as the task is automatically considered 19 | /// > as "forgotten". 20 | pub(crate) fn run() -> TimerHandle { 21 | let timer = Timer::new(); 22 | let handle = timer.handle(); 23 | schedule_callback(Arc::new(Mutex::new(timer)), Duration::new(0, 0)); 24 | handle 25 | } 26 | 27 | /// Calls `Window::setTimeout` with the given `Duration`. The callback wakes up the timer and 28 | /// processes everything. 29 | fn schedule_callback(timer: Arc>, when: Duration) { 30 | let cb = move || { 31 | let mut timer_lock = timer.lock(); 32 | 33 | // We start by polling the timer. If any new `Delay` is created, the waker will be used 34 | // to wake up this task pre-emptively. As such, we pass a `Waker` that calls 35 | // `schedule_callback` with a delay of `0`. 36 | let waker = task::waker(Arc::new(Waker { 37 | timer: timer.clone(), 38 | })); 39 | let _ = Future::poll(Pin::new(&mut *timer_lock), &mut Context::from_waker(&waker)); 40 | 41 | // Notify the timers that are ready. 42 | let now = Instant::now(); 43 | timer_lock.advance_to(now); 44 | 45 | // A previous version of this function recursed in such a way as the strong count increased 46 | // From testing, this one doesn't (it remains at a constant 2) 47 | if Arc::strong_count(&timer) > 20 { 48 | return; 49 | } 50 | 51 | // We call `schedule_callback` again for the next event. 52 | let sleep_dur = timer_lock.next_event().map(|next_event| { 53 | if next_event > now { 54 | next_event - now 55 | } else { 56 | Duration::new(0, 0) 57 | } 58 | }); 59 | drop(timer_lock); 60 | 61 | if let Some(sleep) = sleep_dur { 62 | schedule_callback(timer, sleep); 63 | } 64 | }; 65 | 66 | #[cfg(feature = "tokio-test-util")] 67 | if super::clock::clock().paused() { 68 | cb(); 69 | } else { 70 | let _ = set_timeout( 71 | Closure::once_into_js(cb).unchecked_ref(), 72 | i32::try_from(when.as_millis()).unwrap_or(0), 73 | ) 74 | .unwrap(); 75 | } 76 | 77 | #[cfg(not(feature = "tokio-test-util"))] 78 | let _ = set_timeout( 79 | Closure::once_into_js(cb).unchecked_ref(), 80 | i32::try_from(when.as_millis()).unwrap_or(0), 81 | ) 82 | .unwrap(); 83 | } 84 | 85 | struct Waker { 86 | timer: Arc>, 87 | } 88 | 89 | impl ArcWake for Waker { 90 | fn wake_by_ref(arc_self: &Arc) { 91 | schedule_callback(arc_self.timer.clone(), Duration::new(0, 0)); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/timer/clock.rs: -------------------------------------------------------------------------------- 1 | use crate::std::Instant; 2 | use std::sync::atomic::{AtomicPtr, Ordering::SeqCst}; 3 | use std::sync::{Arc, Mutex}; 4 | use std::time::Duration; 5 | 6 | static CLOCK: AtomicPtr = AtomicPtr::new(EMPTY_CLOCK); 7 | const EMPTY_CLOCK: *mut Inner = std::ptr::null_mut(); 8 | 9 | pub(crate) fn clock() -> Clock { 10 | let mut clock = CLOCK.load(std::sync::atomic::Ordering::SeqCst); 11 | 12 | if clock == EMPTY_CLOCK { 13 | let clock_new = Clock::new(false); 14 | CLOCK 15 | .compare_exchange(EMPTY_CLOCK, clock_new.into_raw(), SeqCst, SeqCst) 16 | .unwrap(); 17 | clock = CLOCK.load(SeqCst); 18 | } 19 | 20 | assert!(clock != EMPTY_CLOCK); 21 | unsafe { 22 | let clock = Clock::from_raw(clock); 23 | let ret = clock.clone(); 24 | let _ = clock.into_raw(); 25 | ret 26 | } 27 | } 28 | 29 | pub(crate) fn now() -> Instant { 30 | clock().now() 31 | } 32 | 33 | #[derive(Debug)] 34 | struct Inner { 35 | base: Mutex, 36 | unfrozen: Mutex>, 37 | } 38 | 39 | #[derive(Debug, Clone)] 40 | pub struct Clock { 41 | inner: Arc, 42 | } 43 | 44 | impl Clock { 45 | fn into_raw(self) -> *mut Inner { 46 | Arc::into_raw(self.inner) as *mut Inner 47 | } 48 | 49 | unsafe fn from_raw(raw: *mut Inner) -> Clock { 50 | let inner = Arc::from_raw(raw); 51 | Clock { inner } 52 | } 53 | 54 | pub(crate) fn new(start_paused: bool) -> Clock { 55 | let now = Instant::now_js(); 56 | 57 | let clock = Clock { 58 | inner: Arc::new(Inner { 59 | base: Mutex::new(now), 60 | unfrozen: Mutex::new(Some(now)), 61 | }), 62 | }; 63 | 64 | if start_paused { 65 | clock.pause(); 66 | } 67 | 68 | clock 69 | } 70 | 71 | pub(crate) fn resume(&self) { 72 | if self.inner.unfrozen.lock().unwrap().is_some() { 73 | panic!("time is not frozen"); 74 | } 75 | 76 | let mut unforzen = self.inner.unfrozen.lock().unwrap(); 77 | (*unforzen) = Some(Instant::now_js()); 78 | } 79 | 80 | pub(crate) fn paused(&self) -> bool { 81 | self.inner.unfrozen.lock().unwrap().is_none() 82 | } 83 | 84 | #[track_caller] 85 | pub(crate) fn pause(&self) { 86 | let unfrozen = self 87 | .inner 88 | .unfrozen 89 | .lock() 90 | .unwrap() 91 | .expect("time is already frozen"); 92 | let elapsed = Instant::now_js() - unfrozen; 93 | let mut base = self.inner.base.lock().unwrap(); 94 | (*base) += elapsed; 95 | let mut unfrozen = self.inner.unfrozen.lock().unwrap(); 96 | (*unfrozen) = None; 97 | } 98 | 99 | #[track_caller] 100 | pub(crate) fn advance(&self, duration: Duration) { 101 | if self.inner.unfrozen.lock().unwrap().is_some() { 102 | panic!("time is not frozen"); 103 | } 104 | 105 | let mut base = self.inner.base.lock().unwrap(); 106 | 107 | (*base) += duration; 108 | } 109 | 110 | pub(crate) fn now(&self) -> Instant { 111 | let mut ret = *self.inner.base.lock().unwrap(); 112 | 113 | let unfrozen = self.inner.unfrozen.lock().unwrap(); 114 | 115 | if let Some(unfrozen) = *unfrozen { 116 | ret += Instant::now_js() - unfrozen; 117 | } 118 | 119 | ret 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/tokio/interval.rs: -------------------------------------------------------------------------------- 1 | use futures::future::poll_fn; 2 | use pin_utils::unsafe_pinned; 3 | use std::pin::Pin; 4 | use std::task::{Context, Poll}; 5 | use std::time::Duration; 6 | 7 | use futures::prelude::*; 8 | 9 | use crate::std::Instant; 10 | use crate::tokio::Sleep; 11 | 12 | /// A stream representing notifications at fixed interval 13 | /// 14 | /// Intervals are created through the `Interval::new` or 15 | /// `Interval::new_at` methods indicating when a first notification 16 | /// should be triggered and when it will be repeated. 17 | /// 18 | /// Note that intervals are not intended for high resolution timers, but rather 19 | /// they will likely fire some granularity after the exact instant that they're 20 | /// otherwise indicated to fire at. 21 | #[derive(Debug)] 22 | pub struct Interval { 23 | sleep: Sleep, 24 | interval: Duration, 25 | missed_tick_behavior: MissedTickBehavior, 26 | } 27 | 28 | impl Interval { 29 | unsafe_pinned!(sleep: Sleep); 30 | 31 | /// Creates a new interval which will fire at `dur` time into the future, 32 | /// and will repeat every `dur` interval after. The first tick completes immediately. 33 | /// 34 | /// The returned object will be bound to the default timer for this thread. 35 | /// The default timer will be spun up in a helper thread on first use. 36 | pub(crate) fn new(dur: Duration) -> Interval { 37 | Interval::new_at(Instant::now(), dur) 38 | } 39 | 40 | /// Creates a new interval which will fire at the time specified by `at`, 41 | /// and then will repeat every `dur` interval after 42 | /// 43 | /// The returned object will be bound to the default timer for this thread. 44 | /// The default timer will be spun up in a helper thread on first use. 45 | pub(crate) fn new_at(at: Instant, dur: Duration) -> Interval { 46 | Interval { 47 | sleep: Sleep::new_at(at), 48 | interval: dur, 49 | missed_tick_behavior: MissedTickBehavior::default(), 50 | } 51 | } 52 | 53 | pub async fn tick(&mut self) -> Instant { 54 | let instant = poll_fn(|cx| self.poll_tick(cx)); 55 | instant.await 56 | } 57 | 58 | pub fn poll_tick(&mut self, cx: &mut Context<'_>) -> Poll { 59 | if Pin::new(&mut *self).sleep().poll(cx).is_pending() { 60 | return Poll::Pending; 61 | } 62 | 63 | let timeout = self.sleep.deadline(); 64 | let now = Instant::now(); 65 | 66 | let next = if now > timeout + Duration::from_millis(5) { 67 | self.missed_tick_behavior 68 | .next_timeout(timeout, now, self.interval) 69 | } else { 70 | timeout + self.interval 71 | }; 72 | 73 | Pin::new(&mut self.sleep).reset(next); 74 | Poll::Ready(timeout) 75 | } 76 | 77 | pub fn reset(&mut self) { 78 | Pin::new(&mut self.sleep).reset(Instant::now() + self.interval); 79 | } 80 | 81 | /// Returns the [`MissedTickBehavior`] strategy currently being used. 82 | pub fn missed_tick_behavior(&self) -> MissedTickBehavior { 83 | self.missed_tick_behavior 84 | } 85 | 86 | /// Sets the [`MissedTickBehavior`] strategy that should be used. 87 | pub fn set_missed_tick_behavior(&mut self, behavior: MissedTickBehavior) { 88 | self.missed_tick_behavior = behavior; 89 | } 90 | 91 | /// Returns the period of the interval. 92 | pub fn period(&self) -> Duration { 93 | self.interval 94 | } 95 | } 96 | 97 | #[derive(Debug, PartialEq, Clone, Copy, Eq, Default)] 98 | pub enum MissedTickBehavior { 99 | #[default] 100 | Burst, 101 | Delay, 102 | Skip, 103 | } 104 | 105 | impl MissedTickBehavior { 106 | /// If a tick is missed, this method is called to determine when the next tick should happen. 107 | fn next_timeout(&self, timeout: Instant, now: Instant, period: Duration) -> Instant { 108 | match self { 109 | Self::Burst => timeout + period, 110 | Self::Delay => now + period, 111 | Self::Skip => { 112 | now + period 113 | - Duration::from_nanos( 114 | ((now - timeout).as_nanos() % period.as_nanos()) 115 | .try_into() 116 | // This operation is practically guaranteed not to 117 | // fail, as in order for it to fail, `period` would 118 | // have to be longer than `now - timeout`, and both 119 | // would have to be longer than 584 years. 120 | // 121 | // If it did fail, there's not a good way to pass 122 | // the error along to the user, so we just panic. 123 | .expect( 124 | "too much time has elapsed since the interval was supposed to tick", 125 | ), 126 | ) 127 | } 128 | } 129 | } 130 | } 131 | 132 | pub fn interval(period: Duration) -> Interval { 133 | Interval::new(period) 134 | } 135 | 136 | pub fn interval_at(start: Instant, period: Duration) -> Interval { 137 | Interval::new_at(start, period) 138 | } 139 | -------------------------------------------------------------------------------- /src/timer/arc_list.rs: -------------------------------------------------------------------------------- 1 | //! An atomically managed intrusive linked list of `Arc` nodes 2 | 3 | use std::marker; 4 | use std::ops::Deref; 5 | use std::sync::atomic::Ordering::SeqCst; 6 | use std::sync::atomic::{AtomicBool, AtomicPtr}; 7 | use std::sync::Arc; 8 | 9 | pub struct ArcList { 10 | list: AtomicPtr>, 11 | _marker: marker::PhantomData, 12 | } 13 | 14 | impl ArcList { 15 | pub fn new() -> ArcList { 16 | ArcList { 17 | list: AtomicPtr::new(Node::EMPTY), 18 | _marker: marker::PhantomData, 19 | } 20 | } 21 | 22 | /// Pushes the `data` provided onto this list if it's not already enqueued 23 | /// in this list. 24 | /// 25 | /// If `data` is already enqueued in this list then this is a noop, 26 | /// otherwise, the `data` here is pushed on the end of the list. 27 | pub fn push(&self, data: &Arc>) -> Result<(), ()> { 28 | if data.enqueued.swap(true, SeqCst) { 29 | // note that even if our list is sealed off then the other end is 30 | // still guaranteed to see us because we were previously enqueued. 31 | return Ok(()); 32 | } 33 | let mut head = self.list.load(SeqCst); 34 | let node = Arc::into_raw(data.clone()) as *mut Node; 35 | loop { 36 | // If we've been sealed off, abort and return an error 37 | if head == Node::sealed() { 38 | unsafe { 39 | drop(Arc::from_raw(node)); 40 | } 41 | return Err(()); 42 | } 43 | 44 | // Otherwise attempt to push this node 45 | data.next.store(head, SeqCst); 46 | match self.list.compare_exchange(head, node, SeqCst, SeqCst) { 47 | Ok(_) => break Ok(()), 48 | Err(new_head) => head = new_head, 49 | } 50 | } 51 | } 52 | 53 | /// Atomically empties this list, returning a new owned copy which can be 54 | /// used to iterate over the entries. 55 | pub fn take(&self) -> ArcList { 56 | let mut list = self.list.load(SeqCst); 57 | loop { 58 | if list == Node::sealed() { 59 | break; 60 | } 61 | match self 62 | .list 63 | .compare_exchange(list, Node::EMPTY, SeqCst, SeqCst) 64 | { 65 | Ok(_) => break, 66 | Err(l) => list = l, 67 | } 68 | } 69 | ArcList { 70 | list: AtomicPtr::new(list), 71 | _marker: marker::PhantomData, 72 | } 73 | } 74 | 75 | /// Atomically empties this list and prevents further successful calls to 76 | /// `push`. 77 | pub fn take_and_seal(&self) -> ArcList { 78 | ArcList { 79 | list: AtomicPtr::new(self.list.swap(Node::sealed(), SeqCst)), 80 | _marker: marker::PhantomData, 81 | } 82 | } 83 | 84 | /// Removes the head of the list of nodes, returning `None` if this is an 85 | /// empty list. 86 | pub fn pop(&mut self) -> Option>> { 87 | let head = *self.list.get_mut(); 88 | if head == Node::EMPTY || head == Node::sealed() { 89 | return None; 90 | } 91 | let head = unsafe { Arc::from_raw(head as *const Node) }; 92 | *self.list.get_mut() = head.next.load(SeqCst); 93 | // At this point, the node is out of the list, so store `false` so we 94 | // can enqueue it again and see further changes. 95 | assert!(head.enqueued.swap(false, SeqCst)); 96 | Some(head) 97 | } 98 | } 99 | 100 | impl Drop for ArcList { 101 | fn drop(&mut self) { 102 | while self.pop().is_some() { 103 | // ... 104 | } 105 | } 106 | } 107 | 108 | pub struct Node { 109 | next: AtomicPtr>, 110 | enqueued: AtomicBool, 111 | data: T, 112 | } 113 | 114 | impl Node { 115 | const EMPTY: *mut Node = std::ptr::null_mut(); 116 | 117 | const fn sealed() -> *mut Node { 118 | std::ptr::null_mut::>().wrapping_add(1) 119 | } 120 | 121 | pub fn new(data: T) -> Node { 122 | Node { 123 | next: AtomicPtr::new(Node::EMPTY), 124 | enqueued: AtomicBool::new(false), 125 | data, 126 | } 127 | } 128 | } 129 | 130 | impl Deref for Node { 131 | type Target = T; 132 | 133 | fn deref(&self) -> &T { 134 | &self.data 135 | } 136 | } 137 | 138 | #[cfg(test)] 139 | mod tests { 140 | use super::*; 141 | 142 | #[test] 143 | fn smoke() { 144 | let a = ArcList::new(); 145 | let n = Arc::new(Node::new(1)); 146 | assert!(a.push(&n).is_ok()); 147 | 148 | let mut l = a.take(); 149 | assert_eq!(**l.pop().unwrap(), 1); 150 | assert!(l.pop().is_none()); 151 | } 152 | 153 | #[test] 154 | fn seal() { 155 | let a = ArcList::new(); 156 | let n = Arc::new(Node::new(1)); 157 | let mut l = a.take_and_seal(); 158 | assert!(l.pop().is_none()); 159 | assert!(a.push(&n).is_err()); 160 | 161 | assert!(a.take().pop().is_none()); 162 | assert!(a.take_and_seal().pop().is_none()); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/tokio/sleep.rs: -------------------------------------------------------------------------------- 1 | use futures::task::AtomicWaker; 2 | use futures::Future; 3 | 4 | use crate::std::Instant; 5 | use crate::timer::arc_list::Node; 6 | use crate::timer::{ScheduledTimer, TimerHandle}; 7 | use std::fmt; 8 | use std::pin::Pin; 9 | use std::sync::atomic::{AtomicUsize, Ordering}; 10 | use std::sync::{Arc, Mutex}; 11 | use std::task::{Context, Poll}; 12 | use std::time::Duration; 13 | 14 | pub struct Sleep { 15 | state: Option>>, 16 | deadline: Instant, 17 | } 18 | 19 | impl Sleep { 20 | /// Creates a new future which will fire at `dur` time into the future. 21 | /// 22 | /// The returned object will be bound to the default timer for this thread. 23 | /// The default timer will be spun up in a helper thread on first use. 24 | #[inline] 25 | pub(crate) fn new(dur: Duration) -> Sleep { 26 | Sleep::new_at(Instant::now() + dur) 27 | } 28 | 29 | /// Creates a new future which will fire at the time specified by `at`. 30 | /// 31 | /// The returned object will be bound to the default timer for this thread. 32 | /// The default timer will be spun up in a helper thread on first use. 33 | #[inline] 34 | pub(crate) fn new_at(at: Instant) -> Sleep { 35 | Sleep::new_handle(at, Default::default()) 36 | } 37 | 38 | /// Creates a new future which will fire at the time specified by `at`. 39 | /// 40 | /// The returned instance of `Delay` will be bound to the timer specified by 41 | /// the `handle` argument. 42 | pub(crate) fn new_handle(at: Instant, handle: TimerHandle) -> Sleep { 43 | let inner = match handle.inner.upgrade() { 44 | Some(i) => i, 45 | None => { 46 | return Sleep { 47 | state: None, 48 | deadline: at, 49 | } 50 | } 51 | }; 52 | let state = Arc::new(Node::new(ScheduledTimer { 53 | at: Mutex::new(Some(at)), 54 | state: AtomicUsize::new(0), 55 | waker: AtomicWaker::new(), 56 | inner: handle.inner, 57 | slot: Mutex::new(None), 58 | })); 59 | 60 | // If we fail to actually push our node then we've become an inert 61 | // timer, meaning that we'll want to immediately return an error from 62 | // `poll`. 63 | if inner.list.push(&state).is_err() { 64 | return Sleep { 65 | state: None, 66 | deadline: at, 67 | }; 68 | } 69 | 70 | inner.waker.wake(); 71 | Sleep { 72 | state: Some(state), 73 | deadline: at, 74 | } 75 | } 76 | 77 | /// Returns the instant at which the future will complete. 78 | pub fn deadline(&self) -> Instant { 79 | self.deadline 80 | } 81 | 82 | /// Returns `true` if `Sleep` has elapsed 83 | /// 84 | /// A `Sleep` instance is elapsed when the requested duration has elapsed 85 | pub fn is_elapsed(&self) -> bool { 86 | match self.state { 87 | Some(ref state) => state.state.load(Ordering::SeqCst) & 1 != 0, 88 | None => false, 89 | } 90 | } 91 | 92 | /// Resets this timeout to an new timeout which will fire at the time 93 | /// specified by `dur`. 94 | /// 95 | /// This method is usable even of this instance of `Delay` has "already 96 | /// fired". That is, if this future has resovled, calling this method means 97 | /// that the future will still re-resolve at the specified instant. 98 | /// 99 | /// If `at` is in the past then this future will immediately be resolved 100 | /// (when `poll` is called). 101 | /// 102 | /// Note that if any task is currently blocked on this future then that task 103 | /// will be dropped. It is required to call `poll` again after this method 104 | /// has been called to ensure tha ta task is blocked on this future. 105 | // Actually we do not want to self to be a `Pin` but we gauranteed the 106 | // API should be similiar to tokio API. So we have to accept a `Pin` to 107 | // this method 108 | #[inline] 109 | pub fn reset(self: Pin<&mut Self>, deadline: Instant) { 110 | let inner = self.get_mut(); 111 | inner.deadline = deadline; 112 | if inner._reset(deadline).is_err() { 113 | inner.state = None 114 | } 115 | } 116 | 117 | fn _reset(&mut self, at: Instant) -> Result<(), ()> { 118 | let state = match self.state { 119 | Some(ref state) => state, 120 | None => return Err(()), 121 | }; 122 | if let Some(timeouts) = state.inner.upgrade() { 123 | let mut bits = state.state.load(Ordering::SeqCst); 124 | loop { 125 | // If we've been invalidated, cancel this reset 126 | if bits & 0b10 != 0 { 127 | return Err(()); 128 | } 129 | let new = bits.wrapping_add(0b100) & !0b11; 130 | match state 131 | .state 132 | .compare_exchange(bits, new, Ordering::SeqCst, Ordering::SeqCst) 133 | { 134 | Ok(_) => break, 135 | Err(s) => bits = s, 136 | } 137 | } 138 | *state.at.lock().unwrap() = Some(at); 139 | // If we fail to push our node then we've become an inert timer, so 140 | // we'll want to clear our `state` field accordingly 141 | timeouts.list.push(state)?; 142 | timeouts.waker.wake(); 143 | } 144 | 145 | Ok(()) 146 | } 147 | } 148 | 149 | pub fn sleep(duration: Duration) -> Sleep { 150 | Sleep::new(duration) 151 | } 152 | 153 | pub fn sleep_until(instant: Instant) -> Sleep { 154 | Sleep::new_at(instant) 155 | } 156 | 157 | impl Future for Sleep { 158 | type Output = (); 159 | 160 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 161 | let state = match self.state { 162 | Some(ref state) => state, 163 | None => panic!("timer has gone away"), 164 | }; 165 | 166 | if state.state.load(Ordering::SeqCst) & 1 != 0 { 167 | return Poll::Ready(()); 168 | } 169 | 170 | state.waker.register(cx.waker()); 171 | 172 | // Now that we've registered, do the full check of our own internal 173 | // state. If we've fired the first bit is set, and if we've been 174 | // invalidated the second bit is set. 175 | match state.state.load(Ordering::SeqCst) { 176 | n if n & 0b01 != 0 => Poll::Ready(()), 177 | n if n & 0b10 != 0 => panic!("Timer has gone away"), 178 | _ => Poll::Pending, 179 | } 180 | } 181 | } 182 | 183 | impl Drop for Sleep { 184 | fn drop(&mut self) { 185 | let state = match self.state { 186 | Some(ref s) => s, 187 | None => return, 188 | }; 189 | if let Some(timeouts) = state.inner.upgrade() { 190 | *state.at.lock().unwrap() = None; 191 | if timeouts.list.push(state).is_ok() { 192 | timeouts.waker.wake(); 193 | } 194 | } 195 | } 196 | } 197 | 198 | impl fmt::Debug for Sleep { 199 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { 200 | f.debug_struct("Delay") 201 | .field("deadline", &self.deadline) 202 | .finish() 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/tokio_util/wheel/level.rs: -------------------------------------------------------------------------------- 1 | use super::Stack; 2 | 3 | use std::fmt; 4 | 5 | /// Wheel for a single level in the timer. This wheel contains 64 slots. 6 | pub(crate) struct Level { 7 | level: usize, 8 | 9 | /// Bit field tracking which slots currently contain entries. 10 | /// 11 | /// Using a bit field to track slots that contain entries allows avoiding a 12 | /// scan to find entries. This field is updated when entries are added or 13 | /// removed from a slot. 14 | /// 15 | /// The least-significant bit represents slot zero. 16 | occupied: u64, 17 | 18 | /// Slots 19 | slot: [T; LEVEL_MULT], 20 | } 21 | 22 | /// Indicates when a slot must be processed next. 23 | #[derive(Debug)] 24 | pub(crate) struct Expiration { 25 | /// The level containing the slot. 26 | pub(crate) level: usize, 27 | 28 | /// The slot index. 29 | pub(crate) slot: usize, 30 | 31 | /// The instant at which the slot needs to be processed. 32 | pub(crate) deadline: u64, 33 | } 34 | 35 | /// Level multiplier. 36 | /// 37 | /// Being a power of 2 is very important. 38 | const LEVEL_MULT: usize = 64; 39 | 40 | impl Level { 41 | pub(crate) fn new(level: usize) -> Level { 42 | // Rust's derived implementations for arrays require that the value 43 | // contained by the array be `Copy`. So, here we have to manually 44 | // initialize every single slot. 45 | macro_rules! s { 46 | () => { 47 | T::default() 48 | }; 49 | } 50 | 51 | Level { 52 | level, 53 | occupied: 0, 54 | slot: [ 55 | // It does not look like the necessary traits are 56 | // derived for [T; 64]. 57 | s!(), 58 | s!(), 59 | s!(), 60 | s!(), 61 | s!(), 62 | s!(), 63 | s!(), 64 | s!(), 65 | s!(), 66 | s!(), 67 | s!(), 68 | s!(), 69 | s!(), 70 | s!(), 71 | s!(), 72 | s!(), 73 | s!(), 74 | s!(), 75 | s!(), 76 | s!(), 77 | s!(), 78 | s!(), 79 | s!(), 80 | s!(), 81 | s!(), 82 | s!(), 83 | s!(), 84 | s!(), 85 | s!(), 86 | s!(), 87 | s!(), 88 | s!(), 89 | s!(), 90 | s!(), 91 | s!(), 92 | s!(), 93 | s!(), 94 | s!(), 95 | s!(), 96 | s!(), 97 | s!(), 98 | s!(), 99 | s!(), 100 | s!(), 101 | s!(), 102 | s!(), 103 | s!(), 104 | s!(), 105 | s!(), 106 | s!(), 107 | s!(), 108 | s!(), 109 | s!(), 110 | s!(), 111 | s!(), 112 | s!(), 113 | s!(), 114 | s!(), 115 | s!(), 116 | s!(), 117 | s!(), 118 | s!(), 119 | s!(), 120 | s!(), 121 | ], 122 | } 123 | } 124 | 125 | /// Finds the slot that needs to be processed next and returns the slot and 126 | /// `Instant` at which this slot must be processed. 127 | pub(crate) fn next_expiration(&self, now: u64) -> Option { 128 | // Use the `occupied` bit field to get the index of the next slot that 129 | // needs to be processed. 130 | let slot = self.next_occupied_slot(now)?; 131 | 132 | // From the slot index, calculate the `Instant` at which it needs to be 133 | // processed. This value *must* be in the future with respect to `now`. 134 | 135 | let level_range = level_range(self.level); 136 | let slot_range = slot_range(self.level); 137 | 138 | // TODO: This can probably be simplified w/ power of 2 math 139 | let level_start = now - (now % level_range); 140 | let deadline = level_start + slot as u64 * slot_range; 141 | 142 | debug_assert!( 143 | deadline >= now, 144 | "deadline={}; now={}; level={}; slot={}; occupied={:b}", 145 | deadline, 146 | now, 147 | self.level, 148 | slot, 149 | self.occupied 150 | ); 151 | 152 | Some(Expiration { 153 | level: self.level, 154 | slot, 155 | deadline, 156 | }) 157 | } 158 | 159 | fn next_occupied_slot(&self, now: u64) -> Option { 160 | if self.occupied == 0 { 161 | return None; 162 | } 163 | 164 | // Get the slot for now using Maths 165 | let now_slot = (now / slot_range(self.level)) as usize; 166 | let occupied = self.occupied.rotate_right(now_slot as u32); 167 | let zeros = occupied.trailing_zeros() as usize; 168 | let slot = (zeros + now_slot) % 64; 169 | 170 | Some(slot) 171 | } 172 | 173 | pub(crate) fn add_entry(&mut self, when: u64, item: T::Owned, store: &mut T::Store) { 174 | let slot = slot_for(when, self.level); 175 | 176 | self.slot[slot].push(item, store); 177 | self.occupied |= occupied_bit(slot); 178 | } 179 | 180 | pub(crate) fn remove_entry(&mut self, when: u64, item: &T::Borrowed, store: &mut T::Store) { 181 | let slot = slot_for(when, self.level); 182 | 183 | self.slot[slot].remove(item, store); 184 | 185 | if self.slot[slot].is_empty() { 186 | // The bit is currently set 187 | debug_assert!(self.occupied & occupied_bit(slot) != 0); 188 | 189 | // Unset the bit 190 | self.occupied ^= occupied_bit(slot); 191 | } 192 | } 193 | 194 | pub(crate) fn pop_entry_slot(&mut self, slot: usize, store: &mut T::Store) -> Option { 195 | let ret = self.slot[slot].pop(store); 196 | 197 | if ret.is_some() && self.slot[slot].is_empty() { 198 | // The bit is currently set 199 | debug_assert!(self.occupied & occupied_bit(slot) != 0); 200 | 201 | self.occupied ^= occupied_bit(slot); 202 | } 203 | 204 | ret 205 | } 206 | } 207 | 208 | impl fmt::Debug for Level { 209 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 210 | fmt.debug_struct("Level") 211 | .field("occupied", &self.occupied) 212 | .finish() 213 | } 214 | } 215 | 216 | fn occupied_bit(slot: usize) -> u64 { 217 | 1 << slot 218 | } 219 | 220 | fn slot_range(level: usize) -> u64 { 221 | LEVEL_MULT.pow(level as u32) as u64 222 | } 223 | 224 | fn level_range(level: usize) -> u64 { 225 | LEVEL_MULT as u64 * slot_range(level) 226 | } 227 | 228 | /// Convert a duration (milliseconds) and a level to a slot position 229 | fn slot_for(duration: u64, level: usize) -> usize { 230 | ((duration >> (level * 6)) % LEVEL_MULT as u64) as usize 231 | } 232 | 233 | #[cfg(test)] 234 | mod test { 235 | use super::*; 236 | 237 | #[test] 238 | fn test_slot_for() { 239 | for pos in 0..64 { 240 | assert_eq!(pos as usize, slot_for(pos, 0)); 241 | } 242 | 243 | for level in 1..5 { 244 | for pos in level..64 { 245 | let a = pos * 64_usize.pow(level as u32); 246 | assert_eq!(pos as usize, slot_for(a as u64, level)); 247 | } 248 | } 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /src/tokio_util/wheel/mod.rs: -------------------------------------------------------------------------------- 1 | mod level; 2 | pub(crate) use self::level::Expiration; 3 | use self::level::Level; 4 | 5 | mod stack; 6 | pub(crate) use self::stack::Stack; 7 | 8 | use std::borrow::Borrow; 9 | use std::fmt::Debug; 10 | 11 | #[derive(Debug)] 12 | pub(crate) struct Wheel { 13 | elapsed: u64, 14 | levels: Vec>, 15 | } 16 | 17 | const NUM_LEVELS: usize = 6; 18 | 19 | const MAX_DURATION: u64 = (1 << (6 * NUM_LEVELS)) - 1; 20 | 21 | #[derive(Debug)] 22 | pub(crate) enum InsertError { 23 | Elapsed, 24 | Invalid, 25 | } 26 | 27 | impl Wheel 28 | where 29 | T: Stack, 30 | { 31 | pub(crate) fn new() -> Wheel { 32 | let levels = (0..NUM_LEVELS).map(Level::new).collect(); 33 | 34 | Wheel { elapsed: 0, levels } 35 | } 36 | 37 | pub(crate) fn elapsed(&self) -> u64 { 38 | self.elapsed 39 | } 40 | 41 | pub(crate) fn insert( 42 | &mut self, 43 | when: u64, 44 | item: T::Owned, 45 | store: &mut T::Store, 46 | ) -> Result<(), (T::Owned, InsertError)> { 47 | if when <= self.elapsed { 48 | return Err((item, InsertError::Elapsed)); 49 | } else if when - self.elapsed > MAX_DURATION { 50 | return Err((item, InsertError::Invalid)); 51 | } 52 | 53 | // Get the level at which the entry should be stored 54 | let level = self.level_for(when); 55 | 56 | self.levels[level].add_entry(when, item, store); 57 | 58 | debug_assert!({ 59 | self.levels[level] 60 | .next_expiration(self.elapsed) 61 | .map(|e| e.deadline >= self.elapsed) 62 | .unwrap_or(true) 63 | }); 64 | 65 | Ok(()) 66 | } 67 | 68 | #[track_caller] 69 | pub(crate) fn remove(&mut self, item: &T::Borrowed, store: &mut T::Store) { 70 | let when = T::when(item, store); 71 | 72 | assert!( 73 | self.elapsed <= when, 74 | "elapsed={}; when={}", 75 | self.elapsed, 76 | when 77 | ); 78 | 79 | let level = self.level_for(when); 80 | 81 | self.levels[level].remove_entry(when, item, store); 82 | } 83 | 84 | pub(crate) fn poll_at(&self) -> Option { 85 | self.next_expiration().map(|expiration| expiration.deadline) 86 | } 87 | 88 | pub(crate) fn poll(&mut self, now: u64, store: &mut T::Store) -> Option { 89 | loop { 90 | let expiration = self.next_expiration().and_then(|expiration| { 91 | if expiration.deadline > now { 92 | None 93 | } else { 94 | Some(expiration) 95 | } 96 | }); 97 | 98 | match expiration { 99 | Some(ref expiration) => { 100 | if let Some(item) = self.poll_expiration(expiration, store) { 101 | return Some(item); 102 | } 103 | 104 | self.set_elapsed(expiration.deadline); 105 | } 106 | None => { 107 | // in this case the poll did not indicate an expiration 108 | // _and_ we were not able to find a next expiration in 109 | // the current list of timers. advance to the poll's 110 | // current time and do nothing else. 111 | self.set_elapsed(now); 112 | return None; 113 | } 114 | } 115 | } 116 | } 117 | 118 | fn next_expiration(&self) -> Option { 119 | // Check all levels 120 | for level in 0..NUM_LEVELS { 121 | if let Some(expiration) = self.levels[level].next_expiration(self.elapsed) { 122 | // There cannot be any expirations at a higher level that happen 123 | // before this one. 124 | debug_assert!(self.no_expirations_before(level + 1, expiration.deadline)); 125 | 126 | return Some(expiration); 127 | } 128 | } 129 | 130 | None 131 | } 132 | 133 | fn no_expirations_before(&self, start_level: usize, before: u64) -> bool { 134 | let mut res = true; 135 | 136 | for l2 in start_level..NUM_LEVELS { 137 | if let Some(e2) = self.levels[l2].next_expiration(self.elapsed) { 138 | if e2.deadline < before { 139 | res = false; 140 | } 141 | } 142 | } 143 | 144 | res 145 | } 146 | 147 | pub(crate) fn poll_expiration( 148 | &mut self, 149 | expiration: &Expiration, 150 | store: &mut T::Store, 151 | ) -> Option { 152 | while let Some(item) = self.pop_entry(expiration, store) { 153 | if expiration.level == 0 { 154 | debug_assert_eq!(T::when(item.borrow(), store), expiration.deadline); 155 | 156 | return Some(item); 157 | } else { 158 | let when = T::when(item.borrow(), store); 159 | 160 | let next_level = expiration.level - 1; 161 | 162 | self.levels[next_level].add_entry(when, item, store); 163 | } 164 | } 165 | 166 | None 167 | } 168 | 169 | fn set_elapsed(&mut self, when: u64) { 170 | assert!( 171 | self.elapsed <= when, 172 | "elapsed={:?}; when={:?}", 173 | self.elapsed, 174 | when 175 | ); 176 | 177 | if when > self.elapsed { 178 | self.elapsed = when; 179 | } 180 | } 181 | 182 | fn pop_entry(&mut self, expiration: &Expiration, store: &mut T::Store) -> Option { 183 | self.levels[expiration.level].pop_entry_slot(expiration.slot, store) 184 | } 185 | 186 | fn level_for(&self, when: u64) -> usize { 187 | level_for(self.elapsed, when) 188 | } 189 | } 190 | 191 | fn level_for(elapsed: u64, when: u64) -> usize { 192 | const SLOT_MASK: u64 = (1 << 6) - 1; 193 | 194 | // Mask in the trailing bits ignored by the level calculation in order to cap 195 | // the possible leading zeros 196 | let masked = elapsed ^ when | SLOT_MASK; 197 | 198 | let leading_zeros = masked.leading_zeros() as usize; 199 | let significant = 63 - leading_zeros; 200 | significant / 6 201 | } 202 | 203 | #[cfg(test)] 204 | mod test { 205 | use super::*; 206 | 207 | #[test] 208 | fn test_level_for() { 209 | for pos in 0..64 { 210 | assert_eq!( 211 | 0, 212 | level_for(0, pos), 213 | "level_for({}) -- binary = {:b}", 214 | pos, 215 | pos 216 | ); 217 | } 218 | 219 | for level in 1..5 { 220 | for pos in level..64 { 221 | let a = pos * 64_usize.pow(level as u32); 222 | assert_eq!( 223 | level, 224 | level_for(0, a as u64), 225 | "level_for({}) -- binary = {:b}", 226 | a, 227 | a 228 | ); 229 | 230 | if pos > level { 231 | let a = a - 1; 232 | assert_eq!( 233 | level, 234 | level_for(0, a as u64), 235 | "level_for({}) -- binary = {:b}", 236 | a, 237 | a 238 | ); 239 | } 240 | 241 | if pos < 64 { 242 | let a = a + 1; 243 | assert_eq!( 244 | level, 245 | level_for(0, a as u64), 246 | "level_for({}) -- binary = {:b}", 247 | a, 248 | a 249 | ); 250 | } 251 | } 252 | } 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /examples/nodejs-example/LICENSE_APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/timer/heap.rs: -------------------------------------------------------------------------------- 1 | //! A simple binary heap with support for removal of arbitrary elements 2 | //! 3 | //! This heap is used to manage timer state in the event loop. All timeouts go 4 | //! into this heap and we also cancel timeouts from this heap. The crucial 5 | //! feature of this heap over the standard library's `BinaryHeap` is the ability 6 | //! to remove arbitrary elements. (e.g. when a timer is canceled) 7 | //! 8 | //! Note that this heap is not at all optimized right now, it should hopefully 9 | //! just work. 10 | 11 | use std::mem; 12 | 13 | pub struct Heap { 14 | // Binary heap of items, plus the slab index indicating what position in the 15 | // list they're in. 16 | items: Vec<(T, usize)>, 17 | 18 | // A map from a slab index (assigned to an item above) to the actual index 19 | // in the array the item appears at. 20 | index: Vec>, 21 | next_index: usize, 22 | } 23 | 24 | enum SlabSlot { 25 | Empty { next: usize }, 26 | Full { value: T }, 27 | } 28 | 29 | pub struct Slot { 30 | idx: usize, 31 | } 32 | 33 | impl Heap { 34 | pub fn new() -> Heap { 35 | Heap { 36 | items: Vec::new(), 37 | index: Vec::new(), 38 | next_index: 0, 39 | } 40 | } 41 | 42 | /// Pushes an element onto this heap, returning a slot token indicating 43 | /// where it was pushed on to. 44 | /// 45 | /// The slot can later get passed to `remove` to remove the element from the 46 | /// heap, but only if the element was previously not removed from the heap. 47 | pub fn push(&mut self, t: T) -> Slot { 48 | self.assert_consistent(); 49 | let len = self.items.len(); 50 | let slot = SlabSlot::Full { value: len }; 51 | let slot_idx = if self.next_index == self.index.len() { 52 | self.next_index += 1; 53 | self.index.push(slot); 54 | self.index.len() - 1 55 | } else { 56 | match mem::replace(&mut self.index[self.next_index], slot) { 57 | SlabSlot::Empty { next } => mem::replace(&mut self.next_index, next), 58 | SlabSlot::Full { .. } => panic!(), 59 | } 60 | }; 61 | self.items.push((t, slot_idx)); 62 | self.percolate_up(len); 63 | self.assert_consistent(); 64 | Slot { idx: slot_idx } 65 | } 66 | 67 | pub fn peek(&self) -> Option<&T> { 68 | self.assert_consistent(); 69 | self.items.first().map(|i| &i.0) 70 | } 71 | 72 | pub fn pop(&mut self) -> Option { 73 | self.assert_consistent(); 74 | if self.items.is_empty() { 75 | return None; 76 | } 77 | let slot = Slot { 78 | idx: self.items[0].1, 79 | }; 80 | Some(self.remove(slot)) 81 | } 82 | 83 | pub fn remove(&mut self, slot: Slot) -> T { 84 | self.assert_consistent(); 85 | let empty = SlabSlot::Empty { 86 | next: self.next_index, 87 | }; 88 | let idx = match mem::replace(&mut self.index[slot.idx], empty) { 89 | SlabSlot::Full { value } => value, 90 | SlabSlot::Empty { .. } => panic!(), 91 | }; 92 | self.next_index = slot.idx; 93 | let (item, slot_idx) = self.items.swap_remove(idx); 94 | debug_assert_eq!(slot.idx, slot_idx); 95 | if idx < self.items.len() { 96 | set_index(&mut self.index, self.items[idx].1, idx); 97 | if self.items[idx].0 < item { 98 | self.percolate_up(idx); 99 | } else { 100 | self.percolate_down(idx); 101 | } 102 | } 103 | self.assert_consistent(); 104 | item 105 | } 106 | 107 | fn percolate_up(&mut self, mut idx: usize) -> usize { 108 | while idx > 0 { 109 | let parent = (idx - 1) / 2; 110 | if self.items[idx].0 >= self.items[parent].0 { 111 | break; 112 | } 113 | let (a, b) = self.items.split_at_mut(idx); 114 | mem::swap(&mut a[parent], &mut b[0]); 115 | set_index(&mut self.index, a[parent].1, parent); 116 | set_index(&mut self.index, b[0].1, idx); 117 | idx = parent; 118 | } 119 | idx 120 | } 121 | 122 | fn percolate_down(&mut self, mut idx: usize) -> usize { 123 | loop { 124 | let left = 2 * idx + 1; 125 | let right = 2 * idx + 2; 126 | 127 | let mut swap_left = true; 128 | match (self.items.get(left), self.items.get(right)) { 129 | (Some(left), None) => { 130 | if left.0 >= self.items[idx].0 { 131 | break; 132 | } 133 | } 134 | (Some(left), Some(right)) => { 135 | if left.0 < self.items[idx].0 { 136 | if right.0 < left.0 { 137 | swap_left = false; 138 | } 139 | } else if right.0 < self.items[idx].0 { 140 | swap_left = false; 141 | } else { 142 | break; 143 | } 144 | } 145 | 146 | (None, None) => break, 147 | (None, Some(_right)) => panic!("not possible"), 148 | } 149 | 150 | let (a, b) = if swap_left { 151 | self.items.split_at_mut(left) 152 | } else { 153 | self.items.split_at_mut(right) 154 | }; 155 | mem::swap(&mut a[idx], &mut b[0]); 156 | set_index(&mut self.index, a[idx].1, idx); 157 | set_index(&mut self.index, b[0].1, a.len()); 158 | idx = a.len(); 159 | } 160 | idx 161 | } 162 | 163 | fn assert_consistent(&self) { 164 | assert_eq!( 165 | self.items.len(), 166 | self.index 167 | .iter() 168 | .filter(|slot| { 169 | match **slot { 170 | SlabSlot::Full { .. } => true, 171 | SlabSlot::Empty { .. } => false, 172 | } 173 | }) 174 | .count() 175 | ); 176 | 177 | for (i, &(_, j)) in self.items.iter().enumerate() { 178 | let index = match self.index[j] { 179 | SlabSlot::Full { value } => value, 180 | SlabSlot::Empty { .. } => panic!(), 181 | }; 182 | if index != i { 183 | panic!("self.index[j] != i : i={i} j={j} self.index[j]={index}"); 184 | } 185 | } 186 | 187 | for (i, (item, _)) in self.items.iter().enumerate() { 188 | if i > 0 { 189 | assert!(*item >= self.items[(i - 1) / 2].0, "bad at index: {i}"); 190 | } 191 | if let Some(left) = self.items.get(2 * i + 1) { 192 | assert!(*item <= left.0, "bad left at index: {i}"); 193 | } 194 | if let Some(right) = self.items.get(2 * i + 2) { 195 | assert!(*item <= right.0, "bad right at index: {i}"); 196 | } 197 | } 198 | } 199 | } 200 | 201 | fn set_index(slab: &mut [SlabSlot], slab_slot: usize, val: T) { 202 | match slab[slab_slot] { 203 | SlabSlot::Full { ref mut value } => *value = val, 204 | SlabSlot::Empty { .. } => panic!(), 205 | } 206 | } 207 | 208 | #[cfg(test)] 209 | mod tests { 210 | use super::Heap; 211 | 212 | #[test] 213 | fn simple() { 214 | let mut h = Heap::new(); 215 | h.push(1); 216 | h.push(2); 217 | h.push(8); 218 | h.push(4); 219 | assert_eq!(h.pop(), Some(1)); 220 | assert_eq!(h.pop(), Some(2)); 221 | assert_eq!(h.pop(), Some(4)); 222 | assert_eq!(h.pop(), Some(8)); 223 | assert_eq!(h.pop(), None); 224 | assert_eq!(h.pop(), None); 225 | } 226 | 227 | #[test] 228 | fn simple2() { 229 | let mut h = Heap::new(); 230 | h.push(5); 231 | h.push(4); 232 | h.push(3); 233 | h.push(2); 234 | h.push(1); 235 | assert_eq!(h.pop(), Some(1)); 236 | h.push(8); 237 | assert_eq!(h.pop(), Some(2)); 238 | h.push(1); 239 | assert_eq!(h.pop(), Some(1)); 240 | assert_eq!(h.pop(), Some(3)); 241 | assert_eq!(h.pop(), Some(4)); 242 | h.push(5); 243 | assert_eq!(h.pop(), Some(5)); 244 | assert_eq!(h.pop(), Some(5)); 245 | assert_eq!(h.pop(), Some(8)); 246 | } 247 | 248 | #[test] 249 | fn remove() { 250 | let mut h = Heap::new(); 251 | h.push(5); 252 | h.push(4); 253 | h.push(3); 254 | let two = h.push(2); 255 | h.push(1); 256 | assert_eq!(h.pop(), Some(1)); 257 | assert_eq!(h.remove(two), 2); 258 | h.push(1); 259 | assert_eq!(h.pop(), Some(1)); 260 | assert_eq!(h.pop(), Some(3)); 261 | } 262 | 263 | fn vec2heap(v: Vec) -> Heap { 264 | let mut h = Heap::new(); 265 | for t in v { 266 | h.push(t); 267 | } 268 | return h; 269 | } 270 | 271 | #[test] 272 | fn test_peek_and_pop() { 273 | let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]; 274 | let mut sorted = data.clone(); 275 | sorted.sort(); 276 | let mut heap = vec2heap(data); 277 | while heap.peek().is_some() { 278 | assert_eq!(heap.peek().unwrap(), sorted.first().unwrap()); 279 | assert_eq!(heap.pop().unwrap(), sorted.remove(0)); 280 | } 281 | } 282 | 283 | #[test] 284 | fn test_push() { 285 | let mut heap = Heap::new(); 286 | heap.push(-2); 287 | heap.push(-4); 288 | heap.push(-9); 289 | assert!(*heap.peek().unwrap() == -9); 290 | heap.push(-11); 291 | assert!(*heap.peek().unwrap() == -11); 292 | heap.push(-5); 293 | assert!(*heap.peek().unwrap() == -11); 294 | heap.push(-27); 295 | assert!(*heap.peek().unwrap() == -27); 296 | heap.push(-3); 297 | assert!(*heap.peek().unwrap() == -27); 298 | heap.push(-103); 299 | assert!(*heap.peek().unwrap() == -103); 300 | } 301 | 302 | fn check_to_vec(mut data: Vec) { 303 | let mut heap = Heap::new(); 304 | for data in data.iter() { 305 | heap.push(*data); 306 | } 307 | data.sort(); 308 | let mut v = Vec::new(); 309 | while let Some(i) = heap.pop() { 310 | v.push(i); 311 | } 312 | assert_eq!(v, data); 313 | } 314 | 315 | #[test] 316 | fn test_to_vec() { 317 | check_to_vec(vec![]); 318 | check_to_vec(vec![5]); 319 | check_to_vec(vec![3, 2]); 320 | check_to_vec(vec![2, 3]); 321 | check_to_vec(vec![5, 1, 2]); 322 | check_to_vec(vec![1, 100, 2, 3]); 323 | check_to_vec(vec![1, 3, 5, 7, 9, 2, 4, 6, 8, 0]); 324 | check_to_vec(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]); 325 | check_to_vec(vec![9, 11, 9, 9, 9, 9, 11, 2, 3, 4, 11, 9, 0, 0, 0, 0]); 326 | check_to_vec(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 327 | check_to_vec(vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]); 328 | check_to_vec(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 1, 2]); 329 | check_to_vec(vec![5, 4, 3, 2, 1, 5, 4, 3, 2, 1, 5, 4, 3, 2, 1]); 330 | } 331 | 332 | #[test] 333 | fn test_empty_pop() { 334 | let mut heap = Heap::::new(); 335 | assert!(heap.pop().is_none()); 336 | } 337 | 338 | #[test] 339 | fn test_empty_peek() { 340 | let empty = Heap::::new(); 341 | assert!(empty.peek().is_none()); 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /src/std.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Pierre Krieger 2 | // Copyright (c) 2019 Tokio Contributors 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | 22 | use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}; 23 | use std::ops::{Add, AddAssign, Sub, SubAssign}; 24 | use std::time::Duration; 25 | 26 | use crate::js::performance_now; 27 | 28 | #[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Debug)] 29 | pub struct Instant(Duration); 30 | 31 | impl Instant { 32 | #[cfg(not(all(feature = "tokio-test-util", feature = "tokio")))] 33 | pub fn now() -> Instant { 34 | Self::now_js() 35 | } 36 | 37 | #[cfg(all(feature = "tokio-test-util", feature = "tokio"))] 38 | pub fn now() -> Instant { 39 | crate::timer::clock::now() 40 | } 41 | 42 | pub(crate) fn now_js() -> Instant { 43 | let val = (performance_now() * 1000.0) as u64; 44 | Instant(Duration::from_micros(val)) 45 | } 46 | 47 | pub fn duration_since(&self, earlier: Instant) -> Duration { 48 | self.checked_duration_since(earlier).unwrap_or_default() 49 | } 50 | 51 | pub fn elapsed(&self) -> Duration { 52 | Instant::now() - *self 53 | } 54 | 55 | pub fn checked_duration_since(&self, earlier: Instant) -> Option { 56 | self.0.checked_sub(earlier.0) 57 | } 58 | 59 | pub fn saturating_duration_since(&self, earlier: Instant) -> Duration { 60 | self.checked_duration_since(earlier).unwrap_or_default() 61 | } 62 | 63 | pub fn checked_add(&self, duration: Duration) -> Option { 64 | self.0.checked_add(duration).map(Instant) 65 | } 66 | 67 | pub fn checked_sub(&self, duration: Duration) -> Option { 68 | self.0.checked_sub(duration).map(Instant) 69 | } 70 | } 71 | 72 | impl Add for Instant { 73 | type Output = Instant; 74 | 75 | /// # Panics 76 | /// 77 | /// This function may panic if the resulting point in time cannot be represented by the 78 | /// underlying data structure. See [`Instant::checked_add`] for a version without panic. 79 | fn add(self, other: Duration) -> Instant { 80 | self.checked_add(other) 81 | .expect("overflow when adding duration to instant") 82 | } 83 | } 84 | 85 | impl AddAssign for Instant { 86 | fn add_assign(&mut self, other: Duration) { 87 | *self = *self + other; 88 | } 89 | } 90 | 91 | impl Sub for Instant { 92 | type Output = Instant; 93 | 94 | fn sub(self, other: Duration) -> Instant { 95 | self.checked_sub(other) 96 | .expect("overflow when subtracting duration from instant") 97 | } 98 | } 99 | 100 | impl SubAssign for Instant { 101 | fn sub_assign(&mut self, other: Duration) { 102 | *self = *self - other; 103 | } 104 | } 105 | 106 | impl Sub for Instant { 107 | type Output = Duration; 108 | 109 | /// Returns the amount of time elapsed from another instant to this one, 110 | /// or zero duration if that instant is later than this one. 111 | /// 112 | /// # Panics 113 | /// 114 | /// Previous Rust versions panicked when `other` was later than `self`. Currently this 115 | /// method saturates. Future versions may reintroduce the panic in some circumstances. 116 | /// See [Monotonicity]. 117 | /// 118 | /// [Monotonicity]: Instant#monotonicity 119 | fn sub(self, other: Instant) -> Duration { 120 | self.duration_since(other) 121 | } 122 | } 123 | 124 | pub const UNIX_EPOCH: SystemTime = SystemTime { inner: 0.0 }; 125 | 126 | #[derive(Debug, Copy, Clone)] 127 | pub struct SystemTime { 128 | /// Unit is milliseconds. 129 | inner: f64, 130 | } 131 | 132 | #[derive(Debug, Copy, Clone)] 133 | pub struct SystemTimeError(Duration); 134 | 135 | impl SystemTimeError { 136 | pub fn duration(&self) -> Duration { 137 | self.0 138 | } 139 | } 140 | 141 | impl std::fmt::Display for SystemTimeError { 142 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 143 | write!(f, "second time provided was later than self") 144 | } 145 | } 146 | 147 | impl PartialEq for SystemTime { 148 | fn eq(&self, other: &SystemTime) -> bool { 149 | // Note that this will most likely only compare equal if we clone an `SystemTime`, 150 | // but that's ok. 151 | self.inner == other.inner 152 | } 153 | } 154 | 155 | impl Eq for SystemTime {} 156 | 157 | impl PartialOrd for SystemTime { 158 | fn partial_cmp(&self, other: &SystemTime) -> Option { 159 | Some(self.cmp(other)) 160 | } 161 | } 162 | 163 | impl Ord for SystemTime { 164 | fn cmp(&self, other: &Self) -> Ordering { 165 | self.inner.partial_cmp(&other.inner).unwrap() 166 | } 167 | } 168 | 169 | impl SystemTime { 170 | pub const UNIX_EPOCH: SystemTime = SystemTime { inner: 0.0 }; 171 | 172 | pub fn now() -> SystemTime { 173 | let val = js_sys::Date::now(); 174 | SystemTime { inner: val } 175 | } 176 | 177 | pub fn duration_since(&self, earlier: SystemTime) -> Result { 178 | let dur_ms = self.inner - earlier.inner; 179 | if dur_ms < 0.0 { 180 | return Err(SystemTimeError(Duration::from_millis(dur_ms.abs() as u64))); 181 | } 182 | Ok(Duration::from_millis(dur_ms as u64)) 183 | } 184 | 185 | pub fn elapsed(&self) -> Result { 186 | SystemTime::now().duration_since(*self) 187 | } 188 | 189 | pub fn checked_add(&self, duration: Duration) -> Option { 190 | Some(*self + duration) 191 | } 192 | 193 | pub fn checked_sub(&self, duration: Duration) -> Option { 194 | Some(*self - duration) 195 | } 196 | } 197 | 198 | impl Add for SystemTime { 199 | type Output = SystemTime; 200 | 201 | fn add(self, other: Duration) -> SystemTime { 202 | let new_val = self.inner + other.as_millis() as f64; 203 | SystemTime { inner: new_val } 204 | } 205 | } 206 | 207 | impl Sub for SystemTime { 208 | type Output = SystemTime; 209 | 210 | fn sub(self, other: Duration) -> SystemTime { 211 | let new_val = self.inner - other.as_millis() as f64; 212 | SystemTime { inner: new_val } 213 | } 214 | } 215 | 216 | impl AddAssign for SystemTime { 217 | fn add_assign(&mut self, rhs: Duration) { 218 | *self = *self + rhs; 219 | } 220 | } 221 | 222 | impl SubAssign for SystemTime { 223 | fn sub_assign(&mut self, rhs: Duration) { 224 | *self = *self - rhs; 225 | } 226 | } 227 | 228 | #[cfg(feature = "serde")] 229 | impl serde_crate::ser::Serialize for SystemTime { 230 | fn serialize(&self, serializer: S) -> Result 231 | where 232 | S: serde_crate::ser::Serializer, 233 | { 234 | use serde_crate::ser::{Error, SerializeStruct}; 235 | let duration_since_epoch = match self.duration_since(UNIX_EPOCH) { 236 | Ok(duration_since_epoch) => duration_since_epoch, 237 | Err(_) => return Err(S::Error::custom("SystemTime must be later than UNIX_EPOCH")), 238 | }; 239 | let mut state = serializer.serialize_struct("SystemTime", 2)?; 240 | state.serialize_field("secs_since_epoch", &duration_since_epoch.as_secs())?; 241 | state.serialize_field("nanos_since_epoch", &duration_since_epoch.subsec_nanos())?; 242 | state.end() 243 | } 244 | } 245 | 246 | #[cfg(feature = "serde")] 247 | impl<'de> serde_crate::de::Deserialize<'de> for SystemTime { 248 | fn deserialize(deserializer: D) -> Result 249 | where 250 | D: serde_crate::de::Deserializer<'de>, 251 | { 252 | use serde_crate::de::{Deserialize, Deserializer, Error, MapAccess, SeqAccess, Visitor}; 253 | use std::fmt; 254 | // Reuse duration 255 | enum Field { 256 | Secs, 257 | Nanos, 258 | } 259 | 260 | impl<'de> Deserialize<'de> for Field { 261 | fn deserialize(deserializer: D) -> Result 262 | where 263 | D: Deserializer<'de>, 264 | { 265 | struct FieldVisitor; 266 | 267 | impl<'de> Visitor<'de> for FieldVisitor { 268 | type Value = Field; 269 | 270 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 271 | formatter.write_str("`secs_since_epoch` or `nanos_since_epoch`") 272 | } 273 | 274 | fn visit_str(self, value: &str) -> Result 275 | where 276 | E: Error, 277 | { 278 | match value { 279 | "secs_since_epoch" => Ok(Field::Secs), 280 | "nanos_since_epoch" => Ok(Field::Nanos), 281 | _ => Err(Error::unknown_field(value, FIELDS)), 282 | } 283 | } 284 | 285 | fn visit_bytes(self, value: &[u8]) -> Result 286 | where 287 | E: Error, 288 | { 289 | match value { 290 | b"secs_since_epoch" => Ok(Field::Secs), 291 | b"nanos_since_epoch" => Ok(Field::Nanos), 292 | _ => { 293 | let value = String::from_utf8_lossy(value); 294 | Err(Error::unknown_field(&value, FIELDS)) 295 | } 296 | } 297 | } 298 | } 299 | 300 | deserializer.deserialize_identifier(FieldVisitor) 301 | } 302 | } 303 | 304 | fn check_overflow(secs: u64, nanos: u32) -> Result<(), E> 305 | where 306 | E: Error, 307 | { 308 | static NANOS_PER_SEC: u32 = 1_000_000_000; 309 | match secs.checked_add((nanos / NANOS_PER_SEC) as u64) { 310 | Some(_) => Ok(()), 311 | None => Err(E::custom("overflow deserializing SystemTime epoch offset")), 312 | } 313 | } 314 | 315 | struct DurationVisitor; 316 | 317 | impl<'de> Visitor<'de> for DurationVisitor { 318 | type Value = Duration; 319 | 320 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 321 | formatter.write_str("struct SystemTime") 322 | } 323 | 324 | fn visit_seq(self, mut seq: A) -> Result 325 | where 326 | A: SeqAccess<'de>, 327 | { 328 | let secs: u64 = match seq.next_element()? { 329 | Some(value) => value, 330 | None => { 331 | return Err(Error::invalid_length(0, &self)); 332 | } 333 | }; 334 | let nanos: u32 = match seq.next_element()? { 335 | Some(value) => value, 336 | None => { 337 | return Err(Error::invalid_length(1, &self)); 338 | } 339 | }; 340 | check_overflow(secs, nanos)?; 341 | Ok(Duration::new(secs, nanos)) 342 | } 343 | 344 | fn visit_map(self, mut map: A) -> Result 345 | where 346 | A: MapAccess<'de>, 347 | { 348 | let mut secs: Option = None; 349 | let mut nanos: Option = None; 350 | while let Some(key) = map.next_key()? { 351 | match key { 352 | Field::Secs => { 353 | if secs.is_some() { 354 | return Err(::duplicate_field( 355 | "secs_since_epoch", 356 | )); 357 | } 358 | secs = Some(map.next_value()?); 359 | } 360 | Field::Nanos => { 361 | if nanos.is_some() { 362 | return Err(::duplicate_field( 363 | "nanos_since_epoch", 364 | )); 365 | } 366 | nanos = Some(map.next_value()?); 367 | } 368 | } 369 | } 370 | let secs = match secs { 371 | Some(secs) => secs, 372 | None => return Err(::missing_field("secs_since_epoch")), 373 | }; 374 | let nanos = match nanos { 375 | Some(nanos) => nanos, 376 | None => return Err(::missing_field("nanos_since_epoch")), 377 | }; 378 | check_overflow(secs, nanos)?; 379 | Ok(Duration::new(secs, nanos)) 380 | } 381 | } 382 | 383 | const FIELDS: &[&str] = &["secs_since_epoch", "nanos_since_epoch"]; 384 | let duration = deserializer.deserialize_struct("SystemTime", FIELDS, DurationVisitor)?; 385 | UNIX_EPOCH 386 | .checked_add(duration) 387 | .ok_or_else(|| D::Error::custom("overflow deserializing SystemTime")) 388 | } 389 | } 390 | -------------------------------------------------------------------------------- /src/tokio_util/delay_queue.rs: -------------------------------------------------------------------------------- 1 | //! A queue of delayed elements. 2 | //! 3 | //! See [`DelayQueue`] for more details. 4 | //! 5 | //! [`DelayQueue`]: struct@DelayQueue 6 | 7 | use super::wheel::{self, Wheel}; 8 | 9 | use crate::std::Instant; 10 | use crate::tokio::{sleep_until, Sleep}; 11 | use futures::ready; 12 | use std::time::Duration; 13 | 14 | use core::ops::{Index, IndexMut}; 15 | use slab::Slab; 16 | use std::cmp; 17 | use std::collections::HashMap; 18 | use std::convert::From; 19 | use std::fmt; 20 | use std::fmt::Debug; 21 | use std::future::Future; 22 | use std::marker::PhantomData; 23 | use std::pin::Pin; 24 | use std::task::{self, Poll, Waker}; 25 | 26 | #[derive(Debug)] 27 | pub struct DelayQueue { 28 | slab: SlabStorage, 29 | wheel: Wheel>, 30 | expired: Stack, 31 | delay: Option>>, 32 | wheel_now: u64, 33 | start: Instant, 34 | waker: Option, 35 | } 36 | 37 | #[derive(Default)] 38 | struct SlabStorage { 39 | inner: Slab>, 40 | key_map: HashMap, 41 | next_key_index: usize, 42 | compact_called: bool, 43 | } 44 | 45 | impl SlabStorage { 46 | pub(crate) fn with_capacity(capacity: usize) -> SlabStorage { 47 | SlabStorage { 48 | inner: Slab::with_capacity(capacity), 49 | key_map: HashMap::new(), 50 | next_key_index: 0, 51 | compact_called: false, 52 | } 53 | } 54 | 55 | pub(crate) fn insert(&mut self, val: Data) -> Key { 56 | let mut key = KeyInternal::new(self.inner.insert(val)); 57 | let key_contained = self.key_map.contains_key(&key.into()); 58 | 59 | if key_contained { 60 | let key_to_give_out = self.create_new_key(); 61 | assert!(!self.key_map.contains_key(&key_to_give_out.into())); 62 | self.key_map.insert(key_to_give_out.into(), key); 63 | key = key_to_give_out; 64 | } else if self.compact_called { 65 | self.key_map.insert(key.into(), key); 66 | } 67 | 68 | key.into() 69 | } 70 | 71 | pub(crate) fn remove(&mut self, key: &Key) -> Data { 72 | let remapped_key = if self.compact_called { 73 | match self.key_map.remove(key) { 74 | Some(key_internal) => key_internal, 75 | None => panic!("invalid key"), 76 | } 77 | } else { 78 | (*key).into() 79 | }; 80 | 81 | self.inner.remove(remapped_key.index) 82 | } 83 | 84 | pub(crate) fn shrink_to_fit(&mut self) { 85 | self.inner.shrink_to_fit(); 86 | self.key_map.shrink_to_fit(); 87 | } 88 | 89 | pub(crate) fn compact(&mut self) { 90 | if !self.compact_called { 91 | for (key, _) in self.inner.iter() { 92 | self.key_map.insert(Key::new(key), KeyInternal::new(key)); 93 | } 94 | } 95 | 96 | let mut remapping = HashMap::new(); 97 | self.inner.compact(|_, from, to| { 98 | remapping.insert(from, to); 99 | true 100 | }); 101 | 102 | for internal_key in self.key_map.values_mut() { 103 | if let Some(new_internal_key) = remapping.get(&internal_key.index) { 104 | *internal_key = KeyInternal::new(*new_internal_key); 105 | } 106 | } 107 | 108 | if self.key_map.capacity() > 2 * self.key_map.len() { 109 | self.key_map.shrink_to_fit(); 110 | } 111 | 112 | self.compact_called = true; 113 | } 114 | 115 | fn remap_key(&self, key: &Key) -> Option { 116 | let key_map = &self.key_map; 117 | if self.compact_called { 118 | key_map.get(key).copied() 119 | } else { 120 | Some((*key).into()) 121 | } 122 | } 123 | 124 | fn create_new_key(&mut self) -> KeyInternal { 125 | while self.key_map.contains_key(&Key::new(self.next_key_index)) { 126 | self.next_key_index = self.next_key_index.wrapping_add(1); 127 | } 128 | 129 | KeyInternal::new(self.next_key_index) 130 | } 131 | 132 | pub(crate) fn len(&self) -> usize { 133 | self.inner.len() 134 | } 135 | 136 | pub(crate) fn capacity(&self) -> usize { 137 | self.inner.capacity() 138 | } 139 | 140 | pub(crate) fn clear(&mut self) { 141 | self.inner.clear(); 142 | self.key_map.clear(); 143 | self.compact_called = false; 144 | } 145 | 146 | pub(crate) fn reserve(&mut self, additional: usize) { 147 | self.inner.reserve(additional); 148 | 149 | if self.compact_called { 150 | self.key_map.reserve(additional); 151 | } 152 | } 153 | 154 | pub(crate) fn is_empty(&self) -> bool { 155 | self.inner.is_empty() 156 | } 157 | 158 | pub(crate) fn contains(&self, key: &Key) -> bool { 159 | let remapped_key = self.remap_key(key); 160 | 161 | match remapped_key { 162 | Some(internal_key) => self.inner.contains(internal_key.index), 163 | None => false, 164 | } 165 | } 166 | } 167 | 168 | impl fmt::Debug for SlabStorage 169 | where 170 | T: fmt::Debug, 171 | { 172 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 173 | if fmt.alternate() { 174 | fmt.debug_map().entries(self.inner.iter()).finish() 175 | } else { 176 | fmt.debug_struct("Slab") 177 | .field("len", &self.len()) 178 | .field("cap", &self.capacity()) 179 | .finish() 180 | } 181 | } 182 | } 183 | 184 | impl Index for SlabStorage { 185 | type Output = Data; 186 | 187 | fn index(&self, key: Key) -> &Self::Output { 188 | let remapped_key = self.remap_key(&key); 189 | 190 | match remapped_key { 191 | Some(internal_key) => &self.inner[internal_key.index], 192 | None => panic!("Invalid index {}", key.index), 193 | } 194 | } 195 | } 196 | 197 | impl IndexMut for SlabStorage { 198 | fn index_mut(&mut self, key: Key) -> &mut Data { 199 | let remapped_key = self.remap_key(&key); 200 | 201 | match remapped_key { 202 | Some(internal_key) => &mut self.inner[internal_key.index], 203 | None => panic!("Invalid index {}", key.index), 204 | } 205 | } 206 | } 207 | 208 | #[derive(Debug)] 209 | pub struct Expired { 210 | data: T, 211 | deadline: Instant, 212 | key: Key, 213 | } 214 | 215 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 216 | pub struct Key { 217 | index: usize, 218 | } 219 | 220 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 221 | struct KeyInternal { 222 | index: usize, 223 | } 224 | 225 | #[derive(Debug)] 226 | struct Stack { 227 | head: Option, 228 | _p: PhantomData T>, 229 | } 230 | 231 | #[derive(Debug)] 232 | struct Data { 233 | inner: T, 234 | when: u64, 235 | expired: bool, 236 | next: Option, 237 | prev: Option, 238 | } 239 | 240 | const MAX_ENTRIES: usize = (1 << 30) - 1; 241 | 242 | impl DelayQueue { 243 | pub fn new() -> DelayQueue { 244 | DelayQueue::with_capacity(0) 245 | } 246 | 247 | pub fn with_capacity(capacity: usize) -> DelayQueue { 248 | DelayQueue { 249 | wheel: Wheel::new(), 250 | slab: SlabStorage::with_capacity(capacity), 251 | expired: Stack::default(), 252 | delay: None, 253 | wheel_now: 0, 254 | start: Instant::now(), 255 | waker: None, 256 | } 257 | } 258 | 259 | #[track_caller] 260 | pub fn insert_at(&mut self, value: T, when: Instant) -> Key { 261 | assert!(self.slab.len() < MAX_ENTRIES, "max entries exceeded"); 262 | 263 | // Normalize the deadline. Values cannot be set to expire in the past. 264 | let when = self.normalize_deadline(when); 265 | 266 | // Insert the value in the store 267 | let key = self.slab.insert(Data { 268 | inner: value, 269 | when, 270 | expired: false, 271 | next: None, 272 | prev: None, 273 | }); 274 | 275 | self.insert_idx(when, key); 276 | 277 | // Set a new delay if the current's deadline is later than the one of the new item 278 | let should_set_delay = if let Some(ref delay) = self.delay { 279 | let current_exp = self.normalize_deadline(delay.deadline()); 280 | current_exp > when 281 | } else { 282 | true 283 | }; 284 | 285 | if should_set_delay { 286 | if let Some(waker) = self.waker.take() { 287 | waker.wake(); 288 | } 289 | 290 | let delay_time = self.start + Duration::from_millis(when); 291 | if let Some(ref mut delay) = &mut self.delay { 292 | delay.as_mut().reset(delay_time); 293 | } else { 294 | self.delay = Some(Box::pin(sleep_until(delay_time))); 295 | } 296 | } 297 | 298 | key 299 | } 300 | 301 | pub fn poll_expired(&mut self, cx: &mut task::Context<'_>) -> Poll>> { 302 | if !self 303 | .waker 304 | .as_ref() 305 | .map(|w| w.will_wake(cx.waker())) 306 | .unwrap_or(false) 307 | { 308 | self.waker = Some(cx.waker().clone()); 309 | } 310 | 311 | let item = ready!(self.poll_idx(cx)); 312 | Poll::Ready(item.map(|key| { 313 | let data = self.slab.remove(&key); 314 | debug_assert!(data.next.is_none()); 315 | debug_assert!(data.prev.is_none()); 316 | 317 | Expired { 318 | key, 319 | data: data.inner, 320 | deadline: self.start + Duration::from_millis(data.when), 321 | } 322 | })) 323 | } 324 | 325 | #[track_caller] 326 | pub fn insert(&mut self, value: T, timeout: Duration) -> Key { 327 | self.insert_at(value, Instant::now() + timeout) 328 | } 329 | 330 | #[track_caller] 331 | fn insert_idx(&mut self, when: u64, key: Key) { 332 | use self::wheel::{InsertError, Stack}; 333 | 334 | // Register the deadline with the timer wheel 335 | match self.wheel.insert(when, key, &mut self.slab) { 336 | Ok(_) => {} 337 | Err((_, InsertError::Elapsed)) => { 338 | self.slab[key].expired = true; 339 | // The delay is already expired, store it in the expired queue 340 | self.expired.push(key, &mut self.slab); 341 | } 342 | Err((_, err)) => panic!("invalid deadline; err={err:?}"), 343 | } 344 | } 345 | 346 | #[track_caller] 347 | fn remove_key(&mut self, key: &Key) { 348 | use super::wheel::Stack; 349 | 350 | // Special case the `expired` queue 351 | if self.slab[*key].expired { 352 | self.expired.remove(key, &mut self.slab); 353 | } else { 354 | self.wheel.remove(key, &mut self.slab); 355 | } 356 | } 357 | 358 | #[track_caller] 359 | pub fn remove(&mut self, key: &Key) -> Expired { 360 | let prev_deadline = self.next_deadline(); 361 | 362 | self.remove_key(key); 363 | let data = self.slab.remove(key); 364 | 365 | let next_deadline = self.next_deadline(); 366 | if prev_deadline != next_deadline { 367 | match (next_deadline, &mut self.delay) { 368 | (None, _) => self.delay = None, 369 | (Some(deadline), Some(delay)) => delay.as_mut().reset(deadline), 370 | (Some(deadline), None) => self.delay = Some(Box::pin(sleep_until(deadline))), 371 | } 372 | } 373 | 374 | Expired { 375 | key: Key::new(key.index), 376 | data: data.inner, 377 | deadline: self.start + Duration::from_millis(data.when), 378 | } 379 | } 380 | 381 | pub fn try_remove(&mut self, key: &Key) -> Option> { 382 | if self.slab.contains(key) { 383 | Some(self.remove(key)) 384 | } else { 385 | None 386 | } 387 | } 388 | 389 | #[track_caller] 390 | pub fn reset_at(&mut self, key: &Key, when: Instant) { 391 | self.remove_key(key); 392 | 393 | // Normalize the deadline. Values cannot be set to expire in the past. 394 | let when = self.normalize_deadline(when); 395 | 396 | self.slab[*key].when = when; 397 | self.slab[*key].expired = false; 398 | 399 | self.insert_idx(when, *key); 400 | 401 | let next_deadline = self.next_deadline(); 402 | if let (Some(ref mut delay), Some(deadline)) = (&mut self.delay, next_deadline) { 403 | // This should awaken us if necessary (ie, if already expired) 404 | delay.as_mut().reset(deadline); 405 | } 406 | } 407 | 408 | pub fn shrink_to_fit(&mut self) { 409 | self.slab.shrink_to_fit(); 410 | } 411 | 412 | pub fn compact(&mut self) { 413 | self.slab.compact(); 414 | } 415 | 416 | fn next_deadline(&mut self) -> Option { 417 | self.wheel 418 | .poll_at() 419 | .map(|poll_at| self.start + Duration::from_millis(poll_at)) 420 | } 421 | 422 | #[track_caller] 423 | pub fn reset(&mut self, key: &Key, timeout: Duration) { 424 | self.reset_at(key, Instant::now() + timeout); 425 | } 426 | 427 | pub fn clear(&mut self) { 428 | self.slab.clear(); 429 | self.expired = Stack::default(); 430 | self.wheel = Wheel::new(); 431 | self.delay = None; 432 | } 433 | 434 | pub fn capacity(&self) -> usize { 435 | self.slab.capacity() 436 | } 437 | 438 | pub fn len(&self) -> usize { 439 | self.slab.len() 440 | } 441 | 442 | #[track_caller] 443 | pub fn reserve(&mut self, additional: usize) { 444 | assert!( 445 | self.slab.capacity() + additional <= MAX_ENTRIES, 446 | "max queue capacity exceeded" 447 | ); 448 | self.slab.reserve(additional); 449 | } 450 | 451 | pub fn is_empty(&self) -> bool { 452 | self.slab.is_empty() 453 | } 454 | 455 | fn poll_idx(&mut self, cx: &mut task::Context<'_>) -> Poll> { 456 | use self::wheel::Stack; 457 | 458 | let expired = self.expired.pop(&mut self.slab); 459 | 460 | if expired.is_some() { 461 | return Poll::Ready(expired); 462 | } 463 | 464 | loop { 465 | if let Some(ref mut delay) = self.delay { 466 | if !delay.is_elapsed() { 467 | ready!(Pin::new(&mut *delay).poll(cx)); 468 | } 469 | 470 | let now = super::ms(delay.deadline() - self.start, super::Round::Down); 471 | 472 | #[cfg(feature = "tokio-test-util")] 473 | { 474 | // +1 here is a tricky way to avoid infinite loop 475 | // in tokio the auto advance feature will always advances time to next time 476 | // event and prevent the infinite loop. But this crate doesn't support auto 477 | // advancing and it will lead this loop to run till infinity. 478 | self.wheel_now = now + 1; 479 | } 480 | #[cfg(not(feature = "tokio-test-util"))] 481 | { 482 | self.wheel_now = now; 483 | } 484 | } 485 | 486 | // We poll the wheel to get the next value out before finding the next deadline. 487 | let wheel_idx = self.wheel.poll(self.wheel_now, &mut self.slab); 488 | 489 | self.delay = self.next_deadline().map(|when| Box::pin(sleep_until(when))); 490 | 491 | if let Some(idx) = wheel_idx { 492 | return Poll::Ready(Some(idx)); 493 | } 494 | 495 | if self.delay.is_none() { 496 | return Poll::Ready(None); 497 | } 498 | } 499 | } 500 | 501 | fn normalize_deadline(&self, when: Instant) -> u64 { 502 | let when = if when < self.start { 503 | 0 504 | } else { 505 | super::ms(when - self.start, super::Round::Up) 506 | }; 507 | 508 | cmp::max(when, self.wheel.elapsed()) 509 | } 510 | } 511 | 512 | // We never put `T` in a `Pin`... 513 | impl Unpin for DelayQueue {} 514 | 515 | impl Default for DelayQueue { 516 | fn default() -> DelayQueue { 517 | DelayQueue::new() 518 | } 519 | } 520 | 521 | impl futures::Stream for DelayQueue { 522 | // DelayQueue seems much more specific, where a user may care that it 523 | // has reached capacity, so return those errors instead of panicking. 524 | type Item = Expired; 525 | 526 | fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll> { 527 | DelayQueue::poll_expired(self.get_mut(), cx) 528 | } 529 | } 530 | 531 | impl wheel::Stack for Stack { 532 | type Owned = Key; 533 | type Borrowed = Key; 534 | type Store = SlabStorage; 535 | 536 | fn is_empty(&self) -> bool { 537 | self.head.is_none() 538 | } 539 | 540 | fn push(&mut self, item: Self::Owned, store: &mut Self::Store) { 541 | // Ensure the entry is not already in a stack. 542 | debug_assert!(store[item].next.is_none()); 543 | debug_assert!(store[item].prev.is_none()); 544 | 545 | // Remove the old head entry 546 | let old = self.head.take(); 547 | 548 | if let Some(idx) = old { 549 | store[idx].prev = Some(item); 550 | } 551 | 552 | store[item].next = old; 553 | self.head = Some(item); 554 | } 555 | 556 | fn pop(&mut self, store: &mut Self::Store) -> Option { 557 | if let Some(key) = self.head { 558 | self.head = store[key].next; 559 | 560 | if let Some(idx) = self.head { 561 | store[idx].prev = None; 562 | } 563 | 564 | store[key].next = None; 565 | debug_assert!(store[key].prev.is_none()); 566 | 567 | Some(key) 568 | } else { 569 | None 570 | } 571 | } 572 | 573 | #[track_caller] 574 | fn remove(&mut self, item: &Self::Borrowed, store: &mut Self::Store) { 575 | let key = *item; 576 | assert!(store.contains(item)); 577 | 578 | // Ensure that the entry is in fact contained by the stack 579 | debug_assert!({ 580 | // This walks the full linked list even if an entry is found. 581 | let mut next = self.head; 582 | let mut contains = false; 583 | 584 | while let Some(idx) = next { 585 | let data = &store[idx]; 586 | 587 | if idx == *item { 588 | debug_assert!(!contains); 589 | contains = true; 590 | } 591 | 592 | next = data.next; 593 | } 594 | 595 | contains 596 | }); 597 | 598 | if let Some(next) = store[key].next { 599 | store[next].prev = store[key].prev; 600 | } 601 | 602 | if let Some(prev) = store[key].prev { 603 | store[prev].next = store[key].next; 604 | } else { 605 | self.head = store[key].next; 606 | } 607 | 608 | store[key].next = None; 609 | store[key].prev = None; 610 | } 611 | 612 | fn when(item: &Self::Borrowed, store: &Self::Store) -> u64 { 613 | store[*item].when 614 | } 615 | } 616 | 617 | impl Default for Stack { 618 | fn default() -> Stack { 619 | Stack { 620 | head: None, 621 | _p: PhantomData, 622 | } 623 | } 624 | } 625 | 626 | impl Key { 627 | pub(crate) fn new(index: usize) -> Key { 628 | Key { index } 629 | } 630 | } 631 | 632 | impl KeyInternal { 633 | pub(crate) fn new(index: usize) -> KeyInternal { 634 | KeyInternal { index } 635 | } 636 | } 637 | 638 | impl From for KeyInternal { 639 | fn from(item: Key) -> Self { 640 | KeyInternal::new(item.index) 641 | } 642 | } 643 | 644 | impl From for Key { 645 | fn from(item: KeyInternal) -> Self { 646 | Key::new(item.index) 647 | } 648 | } 649 | 650 | impl Expired { 651 | pub fn get_ref(&self) -> &T { 652 | &self.data 653 | } 654 | 655 | pub fn get_mut(&mut self) -> &mut T { 656 | &mut self.data 657 | } 658 | 659 | pub fn into_inner(self) -> T { 660 | self.data 661 | } 662 | 663 | pub fn deadline(&self) -> Instant { 664 | self.deadline 665 | } 666 | 667 | pub fn key(&self) -> Key { 668 | self.key 669 | } 670 | } 671 | -------------------------------------------------------------------------------- /tests/web.rs: -------------------------------------------------------------------------------- 1 | use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure}; 2 | 3 | #[cfg(browser)] 4 | wasm_bindgen_test_configure!(run_in_browser); 5 | 6 | #[cfg(feature = "serde")] 7 | #[wasm_bindgen_test] 8 | pub fn test_serde() { 9 | use wasmtimer::std::SystemTime; 10 | let now = SystemTime::now(); 11 | let serialized = serde_json::to_string(&now).unwrap(); 12 | let deserialized: SystemTime = serde_json::from_str(&serialized).unwrap(); 13 | assert_eq!(now, deserialized); 14 | } 15 | 16 | #[cfg(feature = "tokio-test-util")] 17 | pub mod tokio_tests { 18 | use super::*; 19 | use std::{pin::Pin, sync::Once, time::Duration}; 20 | 21 | use futures::{task::noop_waker_ref, Future}; 22 | use std::task::{Context, Poll}; 23 | use wasmtimer::std::Instant; 24 | use wasmtimer::tokio::{advance, pause}; 25 | 26 | static INIT: Once = Once::new(); 27 | 28 | pub fn initialize() { 29 | INIT.call_once(|| { 30 | pause(); 31 | }); 32 | } 33 | 34 | #[wasm_bindgen_test] 35 | pub async fn test_micro_second_precision() { 36 | initialize(); 37 | let a = Instant::now(); 38 | advance(Duration::from_micros(435)).await; 39 | let b = Instant::now(); 40 | let diff = b - a; 41 | assert_eq!(diff.as_micros(), 435); 42 | } 43 | 44 | pub mod sleep_tests { 45 | 46 | use super::*; 47 | use wasmtimer::tokio::sleep; 48 | 49 | #[wasm_bindgen_test] 50 | async fn is_elapsed_test() { 51 | initialize(); 52 | let slept = sleep(Duration::from_millis(1000)); 53 | assert!(!slept.is_elapsed()); 54 | advance(Duration::from_millis(1005)).await; 55 | assert!(slept.is_elapsed()); 56 | } 57 | 58 | #[wasm_bindgen_test] 59 | async fn poll_test() { 60 | initialize(); 61 | let waker = noop_waker_ref(); 62 | let mut cx = Context::from_waker(waker); 63 | 64 | let mut slept = sleep(Duration::from_millis(1000)); 65 | assert_eq!(Pin::new(&mut slept).poll(&mut cx), Poll::Pending); 66 | advance(Duration::from_millis(1005)).await; 67 | assert_eq!(Pin::new(&mut slept).poll(&mut cx), Poll::Ready(())); 68 | } 69 | 70 | #[wasm_bindgen_test] 71 | async fn reset_before_exec_test() { 72 | initialize(); 73 | let waker = noop_waker_ref(); 74 | let mut cx = Context::from_waker(waker); 75 | 76 | let mut slept = sleep(Duration::from_millis(1000)); 77 | Pin::new(&mut slept).reset(Instant::now() + Duration::from_millis(2000)); 78 | assert_eq!(Pin::new(&mut slept).poll(&mut cx), Poll::Pending); 79 | advance(Duration::from_millis(1005)).await; 80 | assert_eq!(Pin::new(&mut slept).poll(&mut cx), Poll::Pending); 81 | advance(Duration::from_millis(1005)).await; 82 | assert_eq!(Pin::new(&mut slept).poll(&mut cx), Poll::Ready(())); 83 | } 84 | 85 | #[wasm_bindgen_test] 86 | async fn reset_after_exec_test() { 87 | initialize(); 88 | let waker = noop_waker_ref(); 89 | let mut cx = Context::from_waker(waker); 90 | 91 | let mut slept = sleep(Duration::from_millis(1000)); 92 | assert_eq!(Pin::new(&mut slept).poll(&mut cx), Poll::Pending); 93 | advance(Duration::from_millis(1005)).await; 94 | assert_eq!(Pin::new(&mut slept).poll(&mut cx), Poll::Ready(())); 95 | Pin::new(&mut slept).reset(Instant::now() + Duration::from_millis(1500)); 96 | assert_eq!(Pin::new(&mut slept).poll(&mut cx), Poll::Pending); 97 | advance(Duration::from_millis(1505)).await; 98 | assert_eq!(Pin::new(&mut slept).poll(&mut cx), Poll::Ready(())); 99 | } 100 | } 101 | 102 | pub mod interval_tests { 103 | 104 | use wasmtimer::tokio::{interval, interval_at, MissedTickBehavior}; 105 | 106 | use super::*; 107 | 108 | #[wasm_bindgen_test] 109 | async fn interval_tick_test() { 110 | initialize(); 111 | 112 | let waker = noop_waker_ref(); 113 | let mut cx = Context::from_waker(waker); 114 | 115 | let mut interval = interval(Duration::from_millis(500)); 116 | let mut fut = interval.tick(); 117 | unsafe { 118 | assert_ne!(Pin::new_unchecked(&mut fut).poll(&mut cx), Poll::Pending); 119 | } 120 | drop(fut); 121 | let mut fut2 = interval.tick(); 122 | unsafe { 123 | assert_eq!(Pin::new_unchecked(&mut fut2).poll(&mut cx), Poll::Pending); 124 | } 125 | advance(Duration::from_millis(501)).await; 126 | unsafe { 127 | assert!(matches!( 128 | Pin::new_unchecked(&mut fut2).poll(&mut cx), 129 | Poll::Ready(_) 130 | )); 131 | } 132 | drop(fut2); 133 | let mut fut3 = interval.tick(); 134 | unsafe { 135 | assert_eq!(Pin::new_unchecked(&mut fut3).poll(&mut cx), Poll::Pending); 136 | } 137 | advance(Duration::from_millis(501)).await; 138 | unsafe { 139 | assert!(matches!( 140 | Pin::new_unchecked(&mut fut3).poll(&mut cx), 141 | Poll::Ready(_) 142 | )); 143 | } 144 | } 145 | 146 | #[wasm_bindgen_test] 147 | async fn interval_at_tick_test() { 148 | initialize(); 149 | 150 | let waker = noop_waker_ref(); 151 | let mut cx = Context::from_waker(waker); 152 | 153 | let mut interval = interval_at( 154 | Instant::now() + Duration::from_millis(1000), 155 | Duration::from_millis(500), 156 | ); 157 | let mut fut = interval.tick(); 158 | unsafe { 159 | assert_eq!(Pin::new_unchecked(&mut fut).poll(&mut cx), Poll::Pending); 160 | } 161 | advance(Duration::from_millis(501)).await; 162 | unsafe { 163 | assert_eq!(Pin::new_unchecked(&mut fut).poll(&mut cx), Poll::Pending); 164 | } 165 | advance(Duration::from_millis(501)).await; 166 | unsafe { 167 | assert!(matches!( 168 | Pin::new_unchecked(&mut fut).poll(&mut cx), 169 | Poll::Ready(_) 170 | )); 171 | } 172 | drop(fut); 173 | let mut fut2 = interval.tick(); 174 | unsafe { 175 | assert_eq!(Pin::new_unchecked(&mut fut2).poll(&mut cx), Poll::Pending); 176 | } 177 | advance(Duration::from_millis(501)).await; 178 | unsafe { 179 | assert!(matches!( 180 | Pin::new_unchecked(&mut fut2).poll(&mut cx), 181 | Poll::Ready(_) 182 | )); 183 | } 184 | } 185 | 186 | #[wasm_bindgen_test] 187 | async fn interval_poll_tick_test() { 188 | initialize(); 189 | 190 | let waker = noop_waker_ref(); 191 | let mut cx = Context::from_waker(waker); 192 | 193 | let mut interval = interval(Duration::from_millis(500)); 194 | assert_ne!(interval.poll_tick(&mut cx), Poll::Pending); 195 | assert_eq!(interval.poll_tick(&mut cx), Poll::Pending); 196 | advance(Duration::from_millis(501)).await; 197 | assert!(matches!(interval.poll_tick(&mut cx), Poll::Ready(_))); 198 | assert_eq!(interval.poll_tick(&mut cx), Poll::Pending); 199 | advance(Duration::from_millis(501)).await; 200 | assert!(matches!(interval.poll_tick(&mut cx), Poll::Ready(_))); 201 | } 202 | 203 | #[wasm_bindgen_test] 204 | async fn interval_at_poll_tick_test() { 205 | initialize(); 206 | 207 | let waker = noop_waker_ref(); 208 | let mut cx = Context::from_waker(waker); 209 | 210 | let mut interval = interval_at( 211 | Instant::now() + Duration::from_millis(1000), 212 | Duration::from_millis(500), 213 | ); 214 | assert_eq!(interval.poll_tick(&mut cx), Poll::Pending); 215 | advance(Duration::from_millis(501)).await; 216 | assert_eq!(interval.poll_tick(&mut cx), Poll::Pending); 217 | advance(Duration::from_millis(501)).await; 218 | assert!(matches!(interval.poll_tick(&mut cx), Poll::Ready(_))); 219 | assert_eq!(interval.poll_tick(&mut cx), Poll::Pending); 220 | advance(Duration::from_millis(501)).await; 221 | assert!(matches!(interval.poll_tick(&mut cx), Poll::Ready(_))); 222 | } 223 | 224 | #[wasm_bindgen_test] 225 | async fn reset_test() { 226 | initialize(); 227 | 228 | let waker = noop_waker_ref(); 229 | let mut cx = Context::from_waker(waker); 230 | 231 | let mut interval = interval(Duration::from_millis(500)); 232 | assert_ne!(interval.poll_tick(&mut cx), Poll::Pending); 233 | assert_eq!(interval.poll_tick(&mut cx), Poll::Pending); 234 | advance(Duration::from_millis(301)).await; 235 | assert_eq!(interval.poll_tick(&mut cx), Poll::Pending); 236 | interval.reset(); 237 | advance(Duration::from_millis(201)).await; 238 | assert_eq!(interval.poll_tick(&mut cx), Poll::Pending); 239 | advance(Duration::from_millis(301)).await; 240 | assert!(matches!(interval.poll_tick(&mut cx), Poll::Ready(_))); 241 | } 242 | 243 | #[wasm_bindgen_test] 244 | async fn interval_at_reset_test() { 245 | initialize(); 246 | 247 | let waker = noop_waker_ref(); 248 | let mut cx = Context::from_waker(waker); 249 | 250 | let mut interval = interval_at( 251 | Instant::now() + Duration::from_millis(1000), 252 | Duration::from_millis(500), 253 | ); 254 | assert_eq!(interval.poll_tick(&mut cx), Poll::Pending); 255 | advance(Duration::from_millis(301)).await; 256 | assert_eq!(interval.poll_tick(&mut cx), Poll::Pending); 257 | interval.reset(); 258 | advance(Duration::from_millis(201)).await; 259 | assert_eq!(interval.poll_tick(&mut cx), Poll::Pending); 260 | advance(Duration::from_millis(301)).await; 261 | assert!(matches!(interval.poll_tick(&mut cx), Poll::Ready(_))); 262 | } 263 | 264 | #[wasm_bindgen_test] 265 | async fn missed_tick_behavior_burst_test() { 266 | initialize(); 267 | 268 | let waker = noop_waker_ref(); 269 | let mut cx = Context::from_waker(waker); 270 | 271 | let mut interval = interval(Duration::from_millis(500)); 272 | interval.set_missed_tick_behavior(MissedTickBehavior::Burst); 273 | advance(Duration::from_millis(1501)).await; 274 | assert!(matches!(interval.poll_tick(&mut cx), Poll::Ready(_))); 275 | assert!(matches!(interval.poll_tick(&mut cx), Poll::Ready(_))); 276 | assert!(matches!(interval.poll_tick(&mut cx), Poll::Ready(_))); 277 | assert!(matches!(interval.poll_tick(&mut cx), Poll::Ready(_))); 278 | assert_eq!(interval.poll_tick(&mut cx), Poll::Pending); 279 | } 280 | 281 | #[wasm_bindgen_test] 282 | async fn missed_tick_behavior_skip_test() { 283 | initialize(); 284 | 285 | let waker = noop_waker_ref(); 286 | let mut cx = Context::from_waker(waker); 287 | 288 | let mut interval = interval(Duration::from_millis(500)); 289 | interval.set_missed_tick_behavior(MissedTickBehavior::Skip); 290 | advance(Duration::from_millis(1601)).await; 291 | assert!(matches!(interval.poll_tick(&mut cx), Poll::Ready(_))); 292 | assert_eq!(interval.poll_tick(&mut cx), Poll::Pending); 293 | advance(Duration::from_millis(401)).await; 294 | assert!(matches!(interval.poll_tick(&mut cx), Poll::Ready(_))); 295 | assert_eq!(interval.poll_tick(&mut cx), Poll::Pending); 296 | } 297 | 298 | #[wasm_bindgen_test] 299 | async fn missed_tick_behavior_delay_test() { 300 | initialize(); 301 | 302 | let waker = noop_waker_ref(); 303 | let mut cx = Context::from_waker(waker); 304 | 305 | let mut interval = interval(Duration::from_millis(500)); 306 | interval.set_missed_tick_behavior(MissedTickBehavior::Delay); 307 | advance(Duration::from_millis(1601)).await; 308 | assert!(matches!(interval.poll_tick(&mut cx), Poll::Ready(_))); 309 | assert_eq!(interval.poll_tick(&mut cx), Poll::Pending); 310 | advance(Duration::from_millis(401)).await; 311 | assert_eq!(interval.poll_tick(&mut cx), Poll::Pending); 312 | advance(Duration::from_millis(100)).await; 313 | assert!(matches!(interval.poll_tick(&mut cx), Poll::Ready(_))); 314 | assert_eq!(interval.poll_tick(&mut cx), Poll::Pending); 315 | } 316 | } 317 | 318 | pub mod timeout_tests { 319 | 320 | use super::*; 321 | use wasmtimer::tokio::{sleep, timeout, timeout_at}; 322 | 323 | #[wasm_bindgen_test] 324 | async fn timeout_success_test() { 325 | initialize(); 326 | 327 | let waker = noop_waker_ref(); 328 | let mut cx = Context::from_waker(waker); 329 | 330 | let mut fut = timeout( 331 | Duration::from_millis(1500), 332 | sleep(Duration::from_millis(1000)), 333 | ); 334 | assert_eq!(Pin::new(&mut fut).poll(&mut cx), Poll::Pending); 335 | advance(Duration::from_millis(1001)).await; 336 | assert!(matches!( 337 | Pin::new(&mut fut).poll(&mut cx), 338 | Poll::Ready(Ok(_)) 339 | )); 340 | } 341 | 342 | #[wasm_bindgen_test] 343 | async fn timeout_fail_test() { 344 | initialize(); 345 | 346 | let waker = noop_waker_ref(); 347 | let mut cx = Context::from_waker(waker); 348 | 349 | let mut fut = timeout( 350 | Duration::from_millis(1000), 351 | sleep(Duration::from_millis(1500)), 352 | ); 353 | assert_eq!(Pin::new(&mut fut).poll(&mut cx), Poll::Pending); 354 | advance(Duration::from_millis(1001)).await; 355 | assert!(matches!( 356 | Pin::new(&mut fut).poll(&mut cx), 357 | Poll::Ready(Err(_)) 358 | )); 359 | } 360 | 361 | #[wasm_bindgen_test] 362 | async fn timeout_at_success_test() { 363 | initialize(); 364 | 365 | let waker = noop_waker_ref(); 366 | let mut cx = Context::from_waker(waker); 367 | 368 | let mut fut = timeout_at( 369 | Instant::now() + Duration::from_millis(1500), 370 | sleep(Duration::from_millis(1000)), 371 | ); 372 | assert_eq!(Pin::new(&mut fut).poll(&mut cx), Poll::Pending); 373 | advance(Duration::from_millis(1001)).await; 374 | assert!(matches!( 375 | Pin::new(&mut fut).poll(&mut cx), 376 | Poll::Ready(Ok(_)) 377 | )); 378 | } 379 | 380 | #[wasm_bindgen_test] 381 | async fn timeout_at_fail_test() { 382 | initialize(); 383 | 384 | let waker = noop_waker_ref(); 385 | let mut cx = Context::from_waker(waker); 386 | 387 | let mut fut = timeout_at( 388 | Instant::now() + Duration::from_millis(1000), 389 | sleep(Duration::from_millis(1500)), 390 | ); 391 | assert_eq!(Pin::new(&mut fut).poll(&mut cx), Poll::Pending); 392 | advance(Duration::from_millis(1001)).await; 393 | assert!(matches!( 394 | Pin::new(&mut fut).poll(&mut cx), 395 | Poll::Ready(Err(_)) 396 | )); 397 | } 398 | } 399 | 400 | #[cfg(feature = "tokio-util")] 401 | pub mod delay_queue_tests { 402 | use wasmtimer::tokio_util::DelayQueue; 403 | 404 | use super::*; 405 | 406 | #[wasm_bindgen_test] 407 | async fn insert_at_test() { 408 | initialize(); 409 | 410 | let waker = noop_waker_ref(); 411 | let mut cx = Context::from_waker(waker); 412 | 413 | let mut delay_queue = DelayQueue::<()>::new(); 414 | let key1 = delay_queue.insert_at((), Instant::now() + Duration::from_millis(1000)); 415 | let key2 = delay_queue.insert_at((), Instant::now() + Duration::from_millis(1000)); 416 | let key3 = delay_queue.insert_at((), Instant::now() + Duration::from_millis(1500)); 417 | advance(Duration::from_millis(1000)).await; 418 | 419 | let expired_1 = delay_queue.poll_expired(&mut cx); 420 | assert!(matches!(expired_1, Poll::Ready(Some(_)))); 421 | if let Poll::Ready(Some(expired)) = expired_1 { 422 | assert_eq!(key1, expired.key()); 423 | } 424 | 425 | let expired_2 = delay_queue.poll_expired(&mut cx); 426 | assert!(matches!(expired_2, Poll::Ready(Some(_)))); 427 | if let Poll::Ready(Some(expired)) = expired_2 { 428 | assert_eq!(key2, expired.key()); 429 | } 430 | 431 | assert!(matches!(delay_queue.poll_expired(&mut cx), Poll::Pending)); 432 | 433 | advance(Duration::from_millis(500)).await; 434 | 435 | let expired_3 = delay_queue.poll_expired(&mut cx); 436 | assert!(matches!(expired_3, Poll::Ready(Some(_)))); 437 | if let Poll::Ready(Some(expired)) = expired_3 { 438 | assert_eq!(key3, expired.key()); 439 | } 440 | 441 | assert!(matches!( 442 | delay_queue.poll_expired(&mut cx), 443 | Poll::Ready(None) 444 | )); 445 | 446 | let key4 = delay_queue.insert_at((), Instant::now() + Duration::from_millis(500)); 447 | advance(Duration::from_millis(500)).await; 448 | 449 | let expired_4 = delay_queue.poll_expired(&mut cx); 450 | assert!(matches!(expired_4, Poll::Ready(Some(_)))); 451 | if let Poll::Ready(Some(expired)) = expired_4 { 452 | assert_eq!(key4, expired.key()); 453 | } 454 | 455 | assert!(matches!( 456 | delay_queue.poll_expired(&mut cx), 457 | Poll::Ready(None) 458 | )); 459 | } 460 | 461 | #[wasm_bindgen_test] 462 | async fn insert_test() { 463 | initialize(); 464 | 465 | let waker = noop_waker_ref(); 466 | let mut cx = Context::from_waker(waker); 467 | 468 | let mut delay_queue = DelayQueue::<()>::new(); 469 | let key1 = delay_queue.insert((), Duration::from_millis(1000)); 470 | let key2 = delay_queue.insert((), Duration::from_millis(1000)); 471 | let key3 = delay_queue.insert((), Duration::from_millis(1500)); 472 | advance(Duration::from_millis(1000)).await; 473 | 474 | let expired_1 = delay_queue.poll_expired(&mut cx); 475 | assert!(matches!(expired_1, Poll::Ready(Some(_)))); 476 | if let Poll::Ready(Some(expired)) = expired_1 { 477 | assert_eq!(key1, expired.key()); 478 | } 479 | let expired_2 = delay_queue.poll_expired(&mut cx); 480 | assert!(matches!(expired_2, Poll::Ready(Some(_)))); 481 | if let Poll::Ready(Some(expired)) = expired_2 { 482 | assert_eq!(key2, expired.key()); 483 | } 484 | 485 | assert!(matches!(delay_queue.poll_expired(&mut cx), Poll::Pending)); 486 | 487 | advance(Duration::from_millis(500)).await; 488 | 489 | let expired_3 = delay_queue.poll_expired(&mut cx); 490 | assert!(matches!(expired_3, Poll::Ready(Some(_)))); 491 | if let Poll::Ready(Some(expired)) = expired_3 { 492 | assert_eq!(key3, expired.key()); 493 | } 494 | 495 | assert!(matches!( 496 | delay_queue.poll_expired(&mut cx), 497 | Poll::Ready(None) 498 | )); 499 | 500 | let key4 = delay_queue.insert((), Duration::from_millis(500)); 501 | advance(Duration::from_millis(500)).await; 502 | 503 | let expired_4 = delay_queue.poll_expired(&mut cx); 504 | assert!(matches!(expired_4, Poll::Ready(Some(_)))); 505 | if let Poll::Ready(Some(expired)) = expired_4 { 506 | assert_eq!(key4, expired.key()); 507 | } 508 | 509 | assert!(matches!( 510 | delay_queue.poll_expired(&mut cx), 511 | Poll::Ready(None) 512 | )); 513 | } 514 | 515 | #[wasm_bindgen_test] 516 | async fn immidiately_remove_test() { 517 | initialize(); 518 | 519 | let waker = noop_waker_ref(); 520 | let mut cx = Context::from_waker(waker); 521 | 522 | let mut delay_queue = DelayQueue::<()>::new(); 523 | 524 | let key1 = delay_queue.insert((), Duration::from_millis(1000)); 525 | delay_queue.remove(&key1); 526 | assert!(matches!( 527 | delay_queue.poll_expired(&mut cx), 528 | Poll::Ready(None) 529 | )); 530 | 531 | advance(Duration::from_millis(1000)).await; 532 | assert!(matches!( 533 | delay_queue.poll_expired(&mut cx), 534 | Poll::Ready(None) 535 | )); 536 | } 537 | 538 | #[wasm_bindgen_test] 539 | async fn remove_after_deadline_test() { 540 | initialize(); 541 | 542 | let waker = noop_waker_ref(); 543 | let mut cx = Context::from_waker(waker); 544 | 545 | let mut delay_queue = DelayQueue::<()>::new(); 546 | 547 | let key1 = delay_queue.insert((), Duration::from_millis(1000)); 548 | assert!(matches!(delay_queue.poll_expired(&mut cx), Poll::Pending)); 549 | advance(Duration::from_millis(1000)).await; 550 | delay_queue.remove(&key1); 551 | assert!(matches!( 552 | delay_queue.poll_expired(&mut cx), 553 | Poll::Ready(None) 554 | )); 555 | } 556 | 557 | #[wasm_bindgen_test] 558 | async fn reset_test() { 559 | initialize(); 560 | 561 | let waker = noop_waker_ref(); 562 | let mut cx = Context::from_waker(waker); 563 | 564 | let mut delay_queue = DelayQueue::<()>::new(); 565 | 566 | let key1 = delay_queue.insert((), Duration::from_millis(1000)); 567 | assert!(matches!(delay_queue.poll_expired(&mut cx), Poll::Pending)); 568 | delay_queue.reset(&key1, Duration::from_millis(1500)); 569 | advance(Duration::from_millis(1000)).await; 570 | assert!(matches!(delay_queue.poll_expired(&mut cx), Poll::Pending)); 571 | advance(Duration::from_millis(500)).await; 572 | assert!(matches!( 573 | delay_queue.poll_expired(&mut cx), 574 | Poll::Ready(Some(_)) 575 | )); 576 | assert!(matches!( 577 | delay_queue.poll_expired(&mut cx), 578 | Poll::Ready(None) 579 | )); 580 | } 581 | } 582 | } 583 | -------------------------------------------------------------------------------- /src/timer/mod.rs: -------------------------------------------------------------------------------- 1 | // The `timer` module is a copy-paste from the code of `futures-timer`, but 2 | // adjusted for WASM. 3 | // 4 | // Copyright (c) 2014 Alex Crichton 5 | // 6 | // Permission is hereby granted, free of charge, to any 7 | // person obtaining a copy of this software and associated 8 | // documentation files (the "Software"), to deal in the 9 | // Software without restriction, including without 10 | // limitation the rights to use, copy, modify, merge, 11 | // publish, distribute, sublicense, and/or sell copies of 12 | // the Software, and to permit persons to whom the Software 13 | // is furnished to do so, subject to the following 14 | // conditions: 15 | // 16 | // The above copyright notice and this permission notice 17 | // shall be included in all copies or substantial portions 18 | // of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 21 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 22 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 23 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 24 | // SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 25 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 26 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 27 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | // DEALINGS IN THE SOFTWARE. 29 | // 30 | // Apache License 31 | // Version 2.0, January 2004 32 | // http://www.apache.org/licenses/ 33 | // 34 | // TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 35 | // 36 | // 1. Definitions. 37 | // 38 | // "License" shall mean the terms and conditions for use, reproduction, 39 | // and distribution as defined by Sections 1 through 9 of this document. 40 | // 41 | // "Licensor" shall mean the copyright owner or entity authorized by 42 | // the copyright owner that is granting the License. 43 | // 44 | // "Legal Entity" shall mean the union of the acting entity and all 45 | // other entities that control, are controlled by, or are under common 46 | // control with that entity. For the purposes of this definition, 47 | // "control" means (i) the power, direct or indirect, to cause the 48 | // direction or management of such entity, whether by contract or 49 | // otherwise, or (ii) ownership of fifty percent (50%) or more of the 50 | // outstanding shares, or (iii) beneficial ownership of such entity. 51 | // 52 | // "You" (or "Your") shall mean an individual or Legal Entity 53 | // exercising permissions granted by this License. 54 | // 55 | // "Source" form shall mean the preferred form for making modifications, 56 | // including but not limited to software source code, documentation 57 | // source, and configuration files. 58 | // 59 | // "Object" form shall mean any form resulting from mechanical 60 | // transformation or translation of a Source form, including but 61 | // not limited to compiled object code, generated documentation, 62 | // and conversions to other media types. 63 | // 64 | // "Work" shall mean the work of authorship, whether in Source or 65 | // Object form, made available under the License, as indicated by a 66 | // copyright notice that is included in or attached to the work 67 | // (an example is provided in the Appendix below). 68 | // 69 | // "Derivative Works" shall mean any work, whether in Source or Object 70 | // form, that is based on (or derived from) the Work and for which the 71 | // editorial revisions, annotations, elaborations, or other modifications 72 | // represent, as a whole, an original work of authorship. For the purposes 73 | // of this License, Derivative Works shall not include works that remain 74 | // separable from, or merely link (or bind by name) to the interfaces of, 75 | // the Work and Derivative Works thereof. 76 | // 77 | // "Contribution" shall mean any work of authorship, including 78 | // the original version of the Work and any modifications or additions 79 | // to that Work or Derivative Works thereof, that is intentionally 80 | // submitted to Licensor for inclusion in the Work by the copyright owner 81 | // or by an individual or Legal Entity authorized to submit on behalf of 82 | // the copyright owner. For the purposes of this definition, "submitted" 83 | // means any form of electronic, verbal, or written communication sent 84 | // to the Licensor or its representatives, including but not limited to 85 | // communication on electronic mailing lists, source code control systems, 86 | // and issue tracking systems that are managed by, or on behalf of, the 87 | // Licensor for the purpose of discussing and improving the Work, but 88 | // excluding communication that is conspicuously marked or otherwise 89 | // designated in writing by the copyright owner as "Not a Contribution." 90 | // 91 | // "Contributor" shall mean Licensor and any individual or Legal Entity 92 | // on behalf of whom a Contribution has been received by Licensor and 93 | // subsequently incorporated within the Work. 94 | // 95 | // 2. Grant of Copyright License. Subject to the terms and conditions of 96 | // this License, each Contributor hereby grants to You a perpetual, 97 | // worldwide, non-exclusive, no-charge, royalty-free, irrevocable 98 | // copyright license to reproduce, prepare Derivative Works of, 99 | // publicly display, publicly perform, sublicense, and distribute the 100 | // Work and such Derivative Works in Source or Object form. 101 | // 102 | // 3. Grant of Patent License. Subject to the terms and conditions of 103 | // this License, each Contributor hereby grants to You a perpetual, 104 | // worldwide, non-exclusive, no-charge, royalty-free, irrevocable 105 | // (except as stated in this section) patent license to make, have made, 106 | // use, offer to sell, sell, import, and otherwise transfer the Work, 107 | // where such license applies only to those patent claims licensable 108 | // by such Contributor that are necessarily infringed by their 109 | // Contribution(s) alone or by combination of their Contribution(s) 110 | // with the Work to which such Contribution(s) was submitted. If You 111 | // institute patent litigation against any entity (including a 112 | // cross-claim or counterclaim in a lawsuit) alleging that the Work 113 | // or a Contribution incorporated within the Work constitutes direct 114 | // or contributory patent infringement, then any patent licenses 115 | // granted to You under this License for that Work shall terminate 116 | // as of the date such litigation is filed. 117 | // 118 | // 4. Redistribution. You may reproduce and distribute copies of the 119 | // Work or Derivative Works thereof in any medium, with or without 120 | // modifications, and in Source or Object form, provided that You 121 | // meet the following conditions: 122 | // 123 | // (a) You must give any other recipients of the Work or 124 | // Derivative Works a copy of this License; and 125 | // 126 | // (b) You must cause any modified files to carry prominent notices 127 | // stating that You changed the files; and 128 | // 129 | // (c) You must retain, in the Source form of any Derivative Works 130 | // that You distribute, all copyright, patent, trademark, and 131 | // attribution notices from the Source form of the Work, 132 | // excluding those notices that do not pertain to any part of 133 | // the Derivative Works; and 134 | // 135 | // (d) If the Work includes a "NOTICE" text file as part of its 136 | // distribution, then any Derivative Works that You distribute must 137 | // include a readable copy of the attribution notices contained 138 | // within such NOTICE file, excluding those notices that do not 139 | // pertain to any part of the Derivative Works, in at least one 140 | // of the following places: within a NOTICE text file distributed 141 | // as part of the Derivative Works; within the Source form or 142 | // documentation, if provided along with the Derivative Works; or, 143 | // within a display generated by the Derivative Works, if and 144 | // wherever such third-party notices normally appear. The contents 145 | // of the NOTICE file are for informational purposes only and 146 | // do not modify the License. You may add Your own attribution 147 | // notices within Derivative Works that You distribute, alongside 148 | // or as an addendum to the NOTICE text from the Work, provided 149 | // that such additional attribution notices cannot be construed 150 | // as modifying the License. 151 | // 152 | // You may add Your own copyright statement to Your modifications and 153 | // may provide additional or different license terms and conditions 154 | // for use, reproduction, or distribution of Your modifications, or 155 | // for any such Derivative Works as a whole, provided Your use, 156 | // reproduction, and distribution of the Work otherwise complies with 157 | // the conditions stated in this License. 158 | // 159 | // 5. Submission of Contributions. Unless You explicitly state otherwise, 160 | // any Contribution intentionally submitted for inclusion in the Work 161 | // by You to the Licensor shall be under the terms and conditions of 162 | // this License, without any additional terms or conditions. 163 | // Notwithstanding the above, nothing herein shall supersede or modify 164 | // the terms of any separate license agreement you may have executed 165 | // with Licensor regarding such Contributions. 166 | // 167 | // 6. Trademarks. This License does not grant permission to use the trade 168 | // names, trademarks, service marks, or product names of the Licensor, 169 | // except as required for reasonable and customary use in describing the 170 | // origin of the Work and reproducing the content of the NOTICE file. 171 | // 172 | // 7. Disclaimer of Warranty. Unless required by applicable law or 173 | // agreed to in writing, Licensor provides the Work (and each 174 | // Contributor provides its Contributions) on an "AS IS" BASIS, 175 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 176 | // implied, including, without limitation, any warranties or conditions 177 | // of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 178 | // PARTICULAR PURPOSE. You are solely responsible for determining the 179 | // appropriateness of using or redistributing the Work and assume any 180 | // risks associated with Your exercise of permissions under this License. 181 | // 182 | // 8. Limitation of Liability. In no event and under no legal theory, 183 | // whether in tort (including negligence), contract, or otherwise, 184 | // unless required by applicable law (such as deliberate and grossly 185 | // negligent acts) or agreed to in writing, shall any Contributor be 186 | // liable to You for damages, including any direct, indirect, special, 187 | // incidental, or consequential damages of any character arising as a 188 | // result of this License or out of the use or inability to use the 189 | // Work (including but not limited to damages for loss of goodwill, 190 | // work stoppage, computer failure or malfunction, or any and all 191 | // other commercial damages or losses), even if such Contributor 192 | // has been advised of the possibility of such damages. 193 | // 194 | // 9. Accepting Warranty or Additional Liability. While redistributing 195 | // the Work or Derivative Works thereof, You may choose to offer, 196 | // and charge a fee for, acceptance of support, warranty, indemnity, 197 | // or other liability obligations and/or rights consistent with this 198 | // License. However, in accepting such obligations, You may act only 199 | // on Your own behalf and on Your sole responsibility, not on behalf 200 | // of any other Contributor, and only if You agree to indemnify, 201 | // defend, and hold each Contributor harmless for any liability 202 | // incurred by, or claims asserted against, such Contributor by reason 203 | // of your accepting any such warranty or additional liability. 204 | // 205 | // END OF TERMS AND CONDITIONS 206 | // 207 | // APPENDIX: How to apply the Apache License to your work. 208 | // 209 | // To apply the Apache License to your work, attach the following 210 | // boilerplate notice, with the fields enclosed by brackets "[]" 211 | // replaced with your own identifying information. (Don't include 212 | // the brackets!) The text should be enclosed in the appropriate 213 | // comment syntax for the file format. We also recommend that a 214 | // file or class name and description of purpose be included on the 215 | // same "printed page" as the copyright notice for easier 216 | // identification within third-party archives. 217 | // 218 | // Copyright [yyyy] [name of copyright owner] 219 | // 220 | // Licensed under the Apache License, Version 2.0 (the "License"); 221 | // you may not use this file except in compliance with the License. 222 | // You may obtain a copy of the License at 223 | // 224 | // http://www.apache.org/licenses/LICENSE-2.0 225 | // 226 | // Unless required by applicable law or agreed to in writing, software 227 | // distributed under the License is distributed on an "AS IS" BASIS, 228 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 229 | // See the License for the specific language governing permissions and 230 | // limitations under the License. 231 | 232 | use crate::std::Instant; 233 | use std::cmp::Ordering; 234 | use std::fmt; 235 | use std::pin::Pin; 236 | use std::sync::atomic::Ordering::SeqCst; 237 | use std::sync::atomic::{AtomicPtr, AtomicUsize}; 238 | use std::sync::{Arc, Mutex, Weak}; 239 | use std::task::{Context, Poll}; 240 | 241 | use futures::prelude::*; 242 | use futures::task::AtomicWaker; 243 | 244 | use arc_list::{ArcList, Node}; 245 | use heap::{Heap, Slot}; 246 | 247 | pub mod arc_list; 248 | #[cfg(feature = "tokio-test-util")] 249 | pub mod clock; 250 | mod global; 251 | mod heap; 252 | 253 | /// A "timer heap" used to power separately owned instances of `Delay` and 254 | /// `Interval`. 255 | /// 256 | /// This timer is implemented as a priority queued-based heap. Each `Timer` 257 | /// contains a few primary methods which which to drive it: 258 | /// 259 | /// * `next_wake` indicates how long the ambient system needs to sleep until it 260 | /// invokes further processing on a `Timer` 261 | /// * `advance_to` is what actually fires timers on the `Timer`, and should be 262 | /// called essentially every iteration of the event loop, or when the time 263 | /// specified by `next_wake` has elapsed. 264 | /// * The `Future` implementation for `Timer` is used to process incoming timer 265 | /// updates and requests. This is used to schedule new timeouts, update 266 | /// existing ones, or delete existing timeouts. The `Future` implementation 267 | /// will never resolve, but it'll schedule notifications of when to wake up 268 | /// and process more messages. 269 | /// 270 | /// Note that if you're using this crate you probably don't need to use a 271 | /// `Timer` as there is a global one already available for you run on a helper 272 | /// thread. If this isn't desirable, though, then the 273 | /// `TimerHandle::set_fallback` method can be used instead! 274 | pub struct Timer { 275 | inner: Arc, 276 | timer_heap: Heap, 277 | } 278 | 279 | /// A handle to a `Timer` which is used to create instances of a `Delay`. 280 | #[derive(Clone)] 281 | pub struct TimerHandle { 282 | pub inner: Weak, 283 | } 284 | 285 | pub struct Inner { 286 | /// List of updates the `Timer` needs to process 287 | pub list: ArcList, 288 | 289 | /// The blocked `Timer` task to receive notifications to the `list` above. 290 | pub waker: AtomicWaker, 291 | } 292 | 293 | /// Shared state between the `Timer` and a `Delay`. 294 | pub struct ScheduledTimer { 295 | pub waker: AtomicWaker, 296 | 297 | // The lowest bit here is whether the timer has fired or not, the second 298 | // lowest bit is whether the timer has been invalidated, and all the other 299 | // bits are the "generation" of the timer which is reset during the `reset` 300 | // function. Only timers for a matching generation are fired. 301 | pub state: AtomicUsize, 302 | 303 | pub inner: Weak, 304 | pub at: Mutex>, 305 | 306 | // TODO: this is only accessed by the timer thread, should have a more 307 | // lightweight protection than a `Mutex` 308 | pub slot: Mutex>, 309 | } 310 | 311 | /// Entries in the timer heap, sorted by the instant they're firing at and then 312 | /// also containing some payload data. 313 | struct HeapTimer { 314 | at: Instant, 315 | gen: usize, 316 | node: Arc>, 317 | } 318 | 319 | impl Timer { 320 | /// Creates a new timer heap ready to create new timers. 321 | pub fn new() -> Timer { 322 | Timer { 323 | inner: Arc::new(Inner { 324 | list: ArcList::new(), 325 | waker: AtomicWaker::new(), 326 | }), 327 | timer_heap: Heap::new(), 328 | } 329 | } 330 | 331 | /// Returns a handle to this timer heap, used to create new timeouts. 332 | pub fn handle(&self) -> TimerHandle { 333 | TimerHandle { 334 | inner: Arc::downgrade(&self.inner), 335 | } 336 | } 337 | 338 | /// Returns the time at which this timer next needs to be invoked with 339 | /// `advance_to`. 340 | /// 341 | /// Event loops or threads typically want to sleep until the specified 342 | /// instant. 343 | pub fn next_event(&self) -> Option { 344 | self.timer_heap.peek().map(|t| t.at) 345 | } 346 | 347 | /// Proces any timers which are supposed to fire before `now` specified. 348 | /// 349 | /// This method should be called on `Timer` periodically to advance the 350 | /// internal state and process any pending timers which need to fire. 351 | pub fn advance_to(&mut self, now: Instant) { 352 | loop { 353 | match self.timer_heap.peek() { 354 | Some(head) if head.at <= now => {} 355 | Some(_) => break, 356 | None => break, 357 | }; 358 | 359 | // Flag the timer as fired and then notify its task, if any, that's 360 | // blocked. 361 | let heap_timer = self.timer_heap.pop().unwrap(); 362 | *heap_timer.node.slot.lock().unwrap() = None; 363 | let bits = heap_timer.gen << 2; 364 | match heap_timer 365 | .node 366 | .state 367 | .compare_exchange(bits, bits | 0b01, SeqCst, SeqCst) 368 | { 369 | Ok(_) => heap_timer.node.waker.wake(), 370 | Err(_b) => {} 371 | } 372 | } 373 | } 374 | 375 | /// Either updates the timer at slot `idx` to fire at `at`, or adds a new 376 | /// timer at `idx` and sets it to fire at `at`. 377 | fn update_or_add(&mut self, at: Instant, node: Arc>) { 378 | // TODO: avoid remove + push and instead just do one sift of the heap? 379 | // In theory we could update it in place and then do the percolation 380 | // as necessary 381 | let gen = node.state.load(SeqCst) >> 2; 382 | let mut slot = node.slot.lock().unwrap(); 383 | if let Some(heap_slot) = slot.take() { 384 | self.timer_heap.remove(heap_slot); 385 | } 386 | *slot = Some(self.timer_heap.push(HeapTimer { 387 | at, 388 | gen, 389 | node: node.clone(), 390 | })); 391 | } 392 | 393 | fn remove(&mut self, node: Arc>) { 394 | // If this `idx` is still around and it's still got a registered timer, 395 | // then we jettison it form the timer heap. 396 | let mut slot = node.slot.lock().unwrap(); 397 | let heap_slot = match slot.take() { 398 | Some(slot) => slot, 399 | None => return, 400 | }; 401 | self.timer_heap.remove(heap_slot); 402 | } 403 | 404 | fn invalidate(&mut self, node: Arc>) { 405 | node.state.fetch_or(0b10, SeqCst); 406 | node.waker.wake(); 407 | } 408 | } 409 | 410 | impl Future for Timer { 411 | type Output = (); 412 | 413 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 414 | Pin::new(&mut self.inner).waker.register(cx.waker()); 415 | let mut list = self.inner.list.take(); 416 | while let Some(node) = list.pop() { 417 | let at = *node.at.lock().unwrap(); 418 | match at { 419 | Some(at) => self.update_or_add(at, node), 420 | None => self.remove(node), 421 | } 422 | } 423 | Poll::Pending 424 | } 425 | } 426 | 427 | impl Drop for Timer { 428 | fn drop(&mut self) { 429 | // Seal off our list to prevent any more updates from getting pushed on. 430 | // Any timer which sees an error from the push will immediately become 431 | // inert. 432 | let mut list = self.inner.list.take_and_seal(); 433 | 434 | // Now that we'll never receive another timer, drain the list of all 435 | // updates and also drain our heap of all active timers, invalidating 436 | // everything. 437 | while let Some(t) = list.pop() { 438 | self.invalidate(t); 439 | } 440 | while let Some(t) = self.timer_heap.pop() { 441 | self.invalidate(t.node); 442 | } 443 | } 444 | } 445 | 446 | impl fmt::Debug for Timer { 447 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { 448 | f.debug_struct("Timer").field("heap", &"...").finish() 449 | } 450 | } 451 | 452 | impl PartialEq for HeapTimer { 453 | fn eq(&self, other: &HeapTimer) -> bool { 454 | self.at == other.at 455 | } 456 | } 457 | 458 | impl Eq for HeapTimer {} 459 | 460 | impl PartialOrd for HeapTimer { 461 | fn partial_cmp(&self, other: &HeapTimer) -> Option { 462 | Some(self.cmp(other)) 463 | } 464 | } 465 | 466 | impl Ord for HeapTimer { 467 | fn cmp(&self, other: &HeapTimer) -> Ordering { 468 | self.at.cmp(&other.at) 469 | } 470 | } 471 | 472 | pub(crate) static HANDLE_FALLBACK: AtomicPtr = AtomicPtr::new(EMPTY_HANDLE); 473 | const EMPTY_HANDLE: *mut Inner = std::ptr::null_mut(); 474 | 475 | /// Error returned from `TimerHandle::set_fallback`. 476 | #[derive(Clone, Debug)] 477 | pub struct SetDefaultError(()); 478 | 479 | impl TimerHandle { 480 | /// Configures this timer handle to be the one returned by 481 | /// `TimerHandle::default`. 482 | /// 483 | /// By default a global thread is initialized on the first call to 484 | /// `TimerHandle::default`. This first call can happen transitively through 485 | /// `Delay::new`. If, however, that hasn't happened yet then the global 486 | /// default timer handle can be configured through this method. 487 | /// 488 | /// This method can be used to prevent the global helper thread from 489 | /// spawning. If this method is successful then the global helper thread 490 | /// will never get spun up. 491 | /// 492 | /// On success this timer handle will have installed itself globally to be 493 | /// used as the return value for `TimerHandle::default` unless otherwise 494 | /// specified. 495 | /// 496 | /// # Errors 497 | /// 498 | /// If another thread has already called `set_as_global_fallback` or this 499 | /// thread otherwise loses a race to call this method then it will fail 500 | /// returning an error. Once a call to `set_as_global_fallback` is 501 | /// successful then no future calls may succeed. 502 | pub fn set_as_global_fallback(self) -> Result<(), SetDefaultError> { 503 | unsafe { 504 | let val = self.into_raw(); 505 | match HANDLE_FALLBACK.compare_exchange(EMPTY_HANDLE, val, SeqCst, SeqCst) { 506 | Ok(_) => Ok(()), 507 | Err(_) => { 508 | drop(TimerHandle::from_raw(val)); 509 | Err(SetDefaultError(())) 510 | } 511 | } 512 | } 513 | } 514 | 515 | fn into_raw(self) -> *mut Inner { 516 | self.inner.into_raw() as *mut Inner 517 | } 518 | 519 | unsafe fn from_raw(raw: *mut Inner) -> TimerHandle { 520 | let inner = Weak::from_raw(raw); 521 | TimerHandle { inner } 522 | } 523 | } 524 | 525 | impl Default for TimerHandle { 526 | fn default() -> TimerHandle { 527 | let mut fallback = HANDLE_FALLBACK.load(SeqCst); 528 | 529 | // If the fallback hasn't been previously initialized then let's spin 530 | // up a helper thread and try to initialize with that. If we can't 531 | // actually create a helper thread then we'll just return a "defunkt" 532 | // handle which will return errors when timer objects are attempted to 533 | // be associated. 534 | if fallback == EMPTY_HANDLE { 535 | let handle = global::run(); 536 | 537 | // If we successfully set ourselves as the actual fallback then we 538 | // want to `forget` the helper thread to ensure that it persists 539 | // globally. If we fail to set ourselves as the fallback that means 540 | // that someone was racing with this call to 541 | // `TimerHandle::default`. They ended up winning so we'll destroy 542 | // our helper thread (which shuts down the thread) and reload the 543 | // fallback. 544 | if handle.clone().set_as_global_fallback().is_ok() { 545 | return handle; 546 | } 547 | fallback = HANDLE_FALLBACK.load(SeqCst); 548 | } 549 | 550 | // At this point our fallback handle global was configured so we use 551 | // its value to reify a handle, clone it, and then forget our reified 552 | // handle as we don't actually have an owning reference to it. 553 | assert!(fallback != EMPTY_HANDLE); 554 | unsafe { 555 | let handle = TimerHandle::from_raw(fallback); 556 | let ret = handle.clone(); 557 | let _ = handle.into_raw(); 558 | ret 559 | } 560 | } 561 | } 562 | 563 | impl fmt::Debug for TimerHandle { 564 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { 565 | f.debug_struct("TimerHandle") 566 | .field("inner", &"...") 567 | .finish() 568 | } 569 | } 570 | --------------------------------------------------------------------------------