From abf64b7416a493b1f0b4ae840060dc1e40793a55 Mon Sep 17 00:00:00 2001 From: Ashley Date: Sun, 15 Dec 2019 00:17:40 +0100 Subject: [PATCH 01/26] Support wasm --- Cargo.toml | 3 +++ src/delay.rs | 3 ++- src/global.rs | 2 +- src/heap_timer.rs | 2 +- src/lib.rs | 4 ++++ src/timer.rs | 3 +-- tests/smoke.rs | 3 ++- tests/timeout.rs | 3 ++- 8 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 676fe3f..499ddfa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,9 @@ Timeouts for futures. [dependencies] +[target.wasm32-unknown-unknown.dependencies] +instant = { version = "0.1.2", features = ["wasm-bindgen"] } + [dev-dependencies] async-std = { version = "1.0.1", features = ["attributes"] } futures = "0.3.1" diff --git a/src/delay.rs b/src/delay.rs index 07b139a..78e7efe 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -10,7 +10,8 @@ use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::SeqCst; use std::sync::{Arc, Mutex}; use std::task::{Context, Poll}; -use std::time::{Duration, Instant}; +use std::time::Duration; +use crate::Instant; use crate::arc_list::Node; use crate::AtomicWaker; diff --git a/src/global.rs b/src/global.rs index 4329913..0d30e3a 100644 --- a/src/global.rs +++ b/src/global.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use std::task::{Context, RawWaker, RawWakerVTable, Waker}; use std::thread; use std::thread::Thread; -use std::time::Instant; +use crate::Instant; use crate::{Timer, TimerHandle}; diff --git a/src/heap_timer.rs b/src/heap_timer.rs index d1c90e2..b53630b 100644 --- a/src/heap_timer.rs +++ b/src/heap_timer.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; use std::sync::Arc; -use std::time::Instant; +use crate::Instant; use crate::{Node, ScheduledTimer}; diff --git a/src/lib.rs b/src/lib.rs index 005c75f..70ff2ff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,3 +31,7 @@ use heap_timer::HeapTimer; use timer::{ScheduledTimer, Timer, TimerHandle}; pub use self::delay::Delay; +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +use instant::Instant; +#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] +use std::time::Instant; diff --git a/src/timer.rs b/src/timer.rs index e3dfacd..d291eb5 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -5,8 +5,7 @@ use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::SeqCst; use std::sync::{Arc, Mutex, Weak}; use std::task::{Context, Poll}; -use std::time::Instant; - +use crate::Instant; use std::future::Future; use crate::AtomicWaker; diff --git a/tests/smoke.rs b/tests/smoke.rs index a8da290..2d80695 100644 --- a/tests/smoke.rs +++ b/tests/smoke.rs @@ -1,6 +1,7 @@ use std::error::Error; use std::pin::Pin; -use std::time::{Duration, Instant}; +use std::time::Duration; +use crate::Instant; use futures_timer::Delay; diff --git a/tests/timeout.rs b/tests/timeout.rs index 0a8798d..a81734c 100644 --- a/tests/timeout.rs +++ b/tests/timeout.rs @@ -1,5 +1,6 @@ use std::error::Error; -use std::time::{Duration, Instant}; +use std::time::Duration; +use crate::Instant; use futures_timer::Delay; From 2f1984abc738ffafc0f5f978f913a0f156f4bac7 Mon Sep 17 00:00:00 2001 From: Ashley Date: Sun, 15 Dec 2019 00:44:59 +0100 Subject: [PATCH 02/26] Error instead --- src/delay.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 78e7efe..533c980 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -5,6 +5,7 @@ use std::fmt; use std::future::Future; +use std::io; use std::pin::Pin; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::SeqCst; @@ -125,16 +126,19 @@ impl Delay { } impl Future for Delay { - type Output = (); + type Output = io::Result<()>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let state = match self.state { Some(ref state) => state, - None => panic!("timer has gone away"), + None => { + let err = Err(io::Error::new(io::ErrorKind::Other, "timer has gone away")); + return Poll::Ready(err); + } }; if state.state.load(SeqCst) & 1 != 0 { - return Poll::Ready(()); + return Poll::Ready(Ok(())); } state.waker.register(&cx.waker()); @@ -143,8 +147,11 @@ impl Future for Delay { // state. If we've fired the first bit is set, and if we've been // invalidated the second bit is set. match state.state.load(SeqCst) { - n if n & 0b01 != 0 => Poll::Ready(()), - n if n & 0b10 != 0 => panic!("timer has gone away"), + n if n & 0b01 != 0 => Poll::Ready(Ok(())), + n if n & 0b10 != 0 => Poll::Ready(Err(io::Error::new( + io::ErrorKind::Other, + "timer has gone away", + ))), _ => Poll::Pending, } } From 17b80a1c3c540e9c5425e5abdfb6f6a395bd5d0e Mon Sep 17 00:00:00 2001 From: Ashley Date: Sun, 15 Dec 2019 00:49:32 +0100 Subject: [PATCH 03/26] Revert "Error instead" This reverts commit 2f1984abc738ffafc0f5f978f913a0f156f4bac7. --- src/delay.rs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 533c980..78e7efe 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -5,7 +5,6 @@ use std::fmt; use std::future::Future; -use std::io; use std::pin::Pin; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::SeqCst; @@ -126,19 +125,16 @@ impl Delay { } impl Future for Delay { - type Output = io::Result<()>; + type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let state = match self.state { Some(ref state) => state, - None => { - let err = Err(io::Error::new(io::ErrorKind::Other, "timer has gone away")); - return Poll::Ready(err); - } + None => panic!("timer has gone away"), }; if state.state.load(SeqCst) & 1 != 0 { - return Poll::Ready(Ok(())); + return Poll::Ready(()); } state.waker.register(&cx.waker()); @@ -147,11 +143,8 @@ impl Future for Delay { // state. If we've fired the first bit is set, and if we've been // invalidated the second bit is set. match state.state.load(SeqCst) { - n if n & 0b01 != 0 => Poll::Ready(Ok(())), - n if n & 0b10 != 0 => Poll::Ready(Err(io::Error::new( - io::ErrorKind::Other, - "timer has gone away", - ))), + n if n & 0b01 != 0 => Poll::Ready(()), + n if n & 0b10 != 0 => panic!("timer has gone away"), _ => Poll::Pending, } } From 10722a7a3b13c7e577bc09cbae6c4f957ea160b3 Mon Sep 17 00:00:00 2001 From: Ashley Date: Sun, 15 Dec 2019 01:05:11 +0100 Subject: [PATCH 04/26] add wasm global --- Cargo.toml | 4 +++ src/global_wasm.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 ++ src/timer.rs | 44 +++++++++++++++++--------- 4 files changed, 112 insertions(+), 15 deletions(-) create mode 100644 src/global_wasm.rs diff --git a/Cargo.toml b/Cargo.toml index 499ddfa..64427fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,10 @@ Timeouts for futures. [target.wasm32-unknown-unknown.dependencies] instant = { version = "0.1.2", features = ["wasm-bindgen"] } +wasm-bindgen = "0.2.55" +futures = "0.3.1" +web-sys = "0.3.32" +parking_lot = "0.10.0" [dev-dependencies] async-std = { version = "1.0.1", features = ["attributes"] } diff --git a/src/global_wasm.rs b/src/global_wasm.rs new file mode 100644 index 0000000..67c919f --- /dev/null +++ b/src/global_wasm.rs @@ -0,0 +1,77 @@ +use futures::task::{self, ArcWake}; +use parking_lot::Mutex; +use std::convert::TryFrom; +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; +use std::task::Context; +use std::time::Duration; +use wasm_bindgen::{JsCast, closure::Closure}; + +use crate::{Instant, Timer, TimerHandle}; + +/// Starts a background task, creates a `Timer`, and returns a handle to it. +/// +/// > **Note**: Contrary to the original `futures-timer` crate, we don't have +/// > any `forget()` method, as the task is automatically considered +/// > as "forgotten". +pub(crate) fn run() -> TimerHandle { + let timer = Timer::new(); + let handle = timer.handle(); + schedule_callback(Arc::new(Mutex::new(timer)), Duration::new(0, 0)); + handle +} + +/// Calls `Window::setTimeout` with the given `Duration`. The callback wakes up the timer and +/// processes everything. +fn schedule_callback(timer: Arc>, when: Duration) { + let window = web_sys::window().expect("Unable to access Window"); + let _ = window.set_timeout_with_callback_and_timeout_and_arguments_0( + &Closure::once_into_js(move || { + let mut timer_lock = timer.lock(); + + // We start by polling the timer. If any new `Delay` is created, the waker will be used + // to wake up this task pre-emptively. As such, we pass a `Waker` that calls + // `schedule_callback` with a delay of `0`. + let waker = task::waker(Arc::new(Waker { timer: timer.clone() })); + let _ = Future::poll(Pin::new(&mut *timer_lock), &mut Context::from_waker(&waker)); + + // Notify the timers that are ready. + let now = Instant::now(); + timer_lock.advance_to(now); + + // Each call to `schedule_callback` calls `schedule_callback` again, but also leaves + // the possibility for `schedule_callback` to be called in parallel. Since we don't + // want too many useless callbacks, we... + // TODO: ugh, that's a hack + if Arc::strong_count(&timer) > 20 { + return; + } + + // We call `schedule_callback` again for the next event. + let sleep_dur = timer_lock.next_event() + .map(|next_event| { + if next_event > now { + next_event - now + } else { + Duration::new(0, 0) + } + }) + .unwrap_or(Duration::from_secs(5)); + drop(timer_lock); + schedule_callback(timer, sleep_dur); + + }).unchecked_ref(), + i32::try_from(when.as_millis()).unwrap_or(0) + ).unwrap(); +} + +struct Waker { + timer: Arc>, +} + +impl ArcWake for Waker { + fn wake_by_ref(arc_self: &Arc) { + schedule_callback(arc_self.timer.clone(), Duration::new(0, 0)); + } +} diff --git a/src/lib.rs b/src/lib.rs index 70ff2ff..f2bea43 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,8 @@ mod global; mod heap; mod heap_timer; mod timer; +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +mod global_wasm; use arc_list::{ArcList, Node}; use atomic_waker::AtomicWaker; diff --git a/src/timer.rs b/src/timer.rs index d291eb5..73c8b42 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -277,23 +277,37 @@ impl Default for TimerHandle { // handle which will return errors when timer objects are attempted to // be associated. if fallback == 0 { - let helper = match global::HelperThread::new() { - Ok(helper) => helper, - Err(_) => return TimerHandle { inner: Weak::new() }, - }; + #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] + { + let helper = match global::HelperThread::new() { + Ok(helper) => helper, + Err(_) => return TimerHandle { inner: Weak::new() }, + }; + + + // If we successfully set ourselves as the actual fallback then we + // want to `forget` the helper thread to ensure that it persists + // globally. If we fail to set ourselves as the fallback that means + // that someone was racing with this call to + // `TimerHandle::default`. They ended up winning so we'll destroy + // our helper thread (which shuts down the thread) and reload the + // fallback. + if helper.handle().set_as_global_fallback().is_ok() { + let ret = helper.handle(); + helper.forget(); + return ret; + } + } - // If we successfully set ourselves as the actual fallback then we - // want to `forget` the helper thread to ensure that it persists - // globally. If we fail to set ourselves as the fallback that means - // that someone was racing with this call to - // `TimerHandle::default`. They ended up winning so we'll destroy - // our helper thread (which shuts down the thread) and reload the - // fallback. - if helper.handle().set_as_global_fallback().is_ok() { - let ret = helper.handle(); - helper.forget(); - return ret; + #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] + { + let handle = crate::global_wasm::run(); + + if handle.clone().set_as_global_fallback().is_ok() { + return handle; + } } + fallback = HANDLE_FALLBACK.load(SeqCst); } From 979bae8a64783d8fce4efda24a9360f3bc8262e1 Mon Sep 17 00:00:00 2001 From: Ashley Date: Sun, 15 Dec 2019 20:52:27 +0100 Subject: [PATCH 05/26] src/global.rs -> src/global_native.rs --- src/{global.rs => global_native.rs} | 0 src/global_wasm.rs | 2 +- src/lib.rs | 3 ++- src/timer.rs | 5 +++-- 4 files changed, 6 insertions(+), 4 deletions(-) rename src/{global.rs => global_native.rs} (100%) diff --git a/src/global.rs b/src/global_native.rs similarity index 100% rename from src/global.rs rename to src/global_native.rs diff --git a/src/global_wasm.rs b/src/global_wasm.rs index 67c919f..bd92df0 100644 --- a/src/global_wasm.rs +++ b/src/global_wasm.rs @@ -12,7 +12,7 @@ use crate::{Instant, Timer, TimerHandle}; /// Starts a background task, creates a `Timer`, and returns a handle to it. /// -/// > **Note**: Contrary to the original `futures-timer` crate, we don't have +/// > **Note**: Contrary to the native implementation, we don't have /// > any `forget()` method, as the task is automatically considered /// > as "forgotten". pub(crate) fn run() -> TimerHandle { diff --git a/src/lib.rs b/src/lib.rs index f2bea43..47e3f6a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,10 +19,11 @@ mod arc_list; mod atomic_waker; mod delay; -mod global; mod heap; mod heap_timer; mod timer; +#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] +mod global_native; #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] mod global_wasm; diff --git a/src/timer.rs b/src/timer.rs index 73c8b42..d2242f3 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -9,7 +9,7 @@ use crate::Instant; use std::future::Future; use crate::AtomicWaker; -use crate::{global, ArcList, Heap, HeapTimer, Node, Slot}; +use crate::{ArcList, Heap, HeapTimer, Node, Slot}; /// A "timer heap" used to power separately owned instances of `Delay`. /// @@ -279,7 +279,7 @@ impl Default for TimerHandle { if fallback == 0 { #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] { - let helper = match global::HelperThread::new() { + let helper = match crate::global_native::HelperThread::new() { Ok(helper) => helper, Err(_) => return TimerHandle { inner: Weak::new() }, }; @@ -303,6 +303,7 @@ impl Default for TimerHandle { { let handle = crate::global_wasm::run(); + // Same as above. if handle.clone().set_as_global_fallback().is_ok() { return handle; } From 0ca73c2ea2a4d00353b51ebf51e296cf6af50210 Mon Sep 17 00:00:00 2001 From: Ashley Date: Sun, 15 Dec 2019 20:58:19 +0100 Subject: [PATCH 06/26] Fix tests, cargo fmt --- src/delay.rs | 2 +- src/global_native.rs | 2 +- src/global_wasm.rs | 93 +++++++++++++++++++++++--------------------- src/heap_timer.rs | 2 +- src/lib.rs | 6 +-- src/timer.rs | 5 +-- tests/smoke.rs | 3 +- tests/timeout.rs | 3 +- 8 files changed, 59 insertions(+), 57 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 78e7efe..2045f15 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -3,6 +3,7 @@ //! This module contains the `Delay` type which is a future that will resolve //! at a particular point in the future. +use crate::Instant; use std::fmt; use std::future::Future; use std::pin::Pin; @@ -11,7 +12,6 @@ use std::sync::atomic::Ordering::SeqCst; use std::sync::{Arc, Mutex}; use std::task::{Context, Poll}; use std::time::Duration; -use crate::Instant; use crate::arc_list::Node; use crate::AtomicWaker; diff --git a/src/global_native.rs b/src/global_native.rs index 0d30e3a..98ccada 100644 --- a/src/global_native.rs +++ b/src/global_native.rs @@ -1,3 +1,4 @@ +use crate::Instant; use std::future::Future; use std::io; use std::mem::{self, ManuallyDrop}; @@ -7,7 +8,6 @@ use std::sync::Arc; use std::task::{Context, RawWaker, RawWakerVTable, Waker}; use std::thread; use std::thread::Thread; -use crate::Instant; use crate::{Timer, TimerHandle}; diff --git a/src/global_wasm.rs b/src/global_wasm.rs index bd92df0..d6d7274 100644 --- a/src/global_wasm.rs +++ b/src/global_wasm.rs @@ -6,7 +6,7 @@ use std::pin::Pin; use std::sync::Arc; use std::task::Context; use std::time::Duration; -use wasm_bindgen::{JsCast, closure::Closure}; +use wasm_bindgen::{closure::Closure, JsCast}; use crate::{Instant, Timer, TimerHandle}; @@ -16,62 +16,67 @@ use crate::{Instant, Timer, TimerHandle}; /// > any `forget()` method, as the task is automatically considered /// > as "forgotten". pub(crate) fn run() -> TimerHandle { - let timer = Timer::new(); - let handle = timer.handle(); - schedule_callback(Arc::new(Mutex::new(timer)), Duration::new(0, 0)); - handle + let timer = Timer::new(); + let handle = timer.handle(); + schedule_callback(Arc::new(Mutex::new(timer)), Duration::new(0, 0)); + handle } /// Calls `Window::setTimeout` with the given `Duration`. The callback wakes up the timer and /// processes everything. fn schedule_callback(timer: Arc>, when: Duration) { - let window = web_sys::window().expect("Unable to access Window"); - let _ = window.set_timeout_with_callback_and_timeout_and_arguments_0( - &Closure::once_into_js(move || { - let mut timer_lock = timer.lock(); + let window = web_sys::window().expect("Unable to access Window"); + let _ = window + .set_timeout_with_callback_and_timeout_and_arguments_0( + &Closure::once_into_js(move || { + let mut timer_lock = timer.lock(); - // We start by polling the timer. If any new `Delay` is created, the waker will be used - // to wake up this task pre-emptively. As such, we pass a `Waker` that calls - // `schedule_callback` with a delay of `0`. - let waker = task::waker(Arc::new(Waker { timer: timer.clone() })); - let _ = Future::poll(Pin::new(&mut *timer_lock), &mut Context::from_waker(&waker)); + // We start by polling the timer. If any new `Delay` is created, the waker will be used + // to wake up this task pre-emptively. As such, we pass a `Waker` that calls + // `schedule_callback` with a delay of `0`. + let waker = task::waker(Arc::new(Waker { + timer: timer.clone(), + })); + let _ = Future::poll(Pin::new(&mut *timer_lock), &mut Context::from_waker(&waker)); - // Notify the timers that are ready. - let now = Instant::now(); - timer_lock.advance_to(now); + // Notify the timers that are ready. + let now = Instant::now(); + timer_lock.advance_to(now); - // Each call to `schedule_callback` calls `schedule_callback` again, but also leaves - // the possibility for `schedule_callback` to be called in parallel. Since we don't - // want too many useless callbacks, we... - // TODO: ugh, that's a hack - if Arc::strong_count(&timer) > 20 { - return; - } + // Each call to `schedule_callback` calls `schedule_callback` again, but also leaves + // the possibility for `schedule_callback` to be called in parallel. Since we don't + // want too many useless callbacks, we... + // TODO: ugh, that's a hack + if Arc::strong_count(&timer) > 20 { + return; + } - // We call `schedule_callback` again for the next event. - let sleep_dur = timer_lock.next_event() - .map(|next_event| { - if next_event > now { - next_event - now - } else { - Duration::new(0, 0) - } - }) - .unwrap_or(Duration::from_secs(5)); - drop(timer_lock); - schedule_callback(timer, sleep_dur); - - }).unchecked_ref(), - i32::try_from(when.as_millis()).unwrap_or(0) - ).unwrap(); + // We call `schedule_callback` again for the next event. + let sleep_dur = timer_lock + .next_event() + .map(|next_event| { + if next_event > now { + next_event - now + } else { + Duration::new(0, 0) + } + }) + .unwrap_or(Duration::from_secs(5)); + drop(timer_lock); + schedule_callback(timer, sleep_dur); + }) + .unchecked_ref(), + i32::try_from(when.as_millis()).unwrap_or(0), + ) + .unwrap(); } struct Waker { - timer: Arc>, + timer: Arc>, } impl ArcWake for Waker { - fn wake_by_ref(arc_self: &Arc) { - schedule_callback(arc_self.timer.clone(), Duration::new(0, 0)); - } + fn wake_by_ref(arc_self: &Arc) { + schedule_callback(arc_self.timer.clone(), Duration::new(0, 0)); + } } diff --git a/src/heap_timer.rs b/src/heap_timer.rs index b53630b..82d31e1 100644 --- a/src/heap_timer.rs +++ b/src/heap_timer.rs @@ -1,6 +1,6 @@ +use crate::Instant; use std::cmp::Ordering; use std::sync::Arc; -use crate::Instant; use crate::{Node, ScheduledTimer}; diff --git a/src/lib.rs b/src/lib.rs index 47e3f6a..2e9adef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,13 +19,13 @@ mod arc_list; mod atomic_waker; mod delay; -mod heap; -mod heap_timer; -mod timer; #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] mod global_native; #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] mod global_wasm; +mod heap; +mod heap_timer; +mod timer; use arc_list::{ArcList, Node}; use atomic_waker::AtomicWaker; diff --git a/src/timer.rs b/src/timer.rs index d2242f3..f28e056 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -1,12 +1,12 @@ +use crate::Instant; use std::fmt; +use std::future::Future; use std::mem; use std::pin::Pin; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::SeqCst; use std::sync::{Arc, Mutex, Weak}; use std::task::{Context, Poll}; -use crate::Instant; -use std::future::Future; use crate::AtomicWaker; use crate::{ArcList, Heap, HeapTimer, Node, Slot}; @@ -284,7 +284,6 @@ impl Default for TimerHandle { Err(_) => return TimerHandle { inner: Weak::new() }, }; - // If we successfully set ourselves as the actual fallback then we // want to `forget` the helper thread to ensure that it persists // globally. If we fail to set ourselves as the fallback that means diff --git a/tests/smoke.rs b/tests/smoke.rs index 2d80695..a8da290 100644 --- a/tests/smoke.rs +++ b/tests/smoke.rs @@ -1,7 +1,6 @@ use std::error::Error; use std::pin::Pin; -use std::time::Duration; -use crate::Instant; +use std::time::{Duration, Instant}; use futures_timer::Delay; diff --git a/tests/timeout.rs b/tests/timeout.rs index a81734c..0a8798d 100644 --- a/tests/timeout.rs +++ b/tests/timeout.rs @@ -1,6 +1,5 @@ use std::error::Error; -use std::time::Duration; -use crate::Instant; +use std::time::{Duration, Instant}; use futures_timer::Delay; From 11baa0ed571161f10d8ae9df1b397d088d4218ca Mon Sep 17 00:00:00 2001 From: Ashley Date: Tue, 17 Dec 2019 12:47:59 +0100 Subject: [PATCH 07/26] Switch to wasm-timer for Instant, don't use parking_lot --- Cargo.toml | 3 +-- src/global_wasm.rs | 6 +++--- src/lib.rs | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 64427fe..8eed02b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,11 +15,10 @@ Timeouts for futures. [dependencies] [target.wasm32-unknown-unknown.dependencies] -instant = { version = "0.1.2", features = ["wasm-bindgen"] } +wasm-timer = "0.2.4" wasm-bindgen = "0.2.55" futures = "0.3.1" web-sys = "0.3.32" -parking_lot = "0.10.0" [dev-dependencies] async-std = { version = "1.0.1", features = ["attributes"] } diff --git a/src/global_wasm.rs b/src/global_wasm.rs index d6d7274..96a75cf 100644 --- a/src/global_wasm.rs +++ b/src/global_wasm.rs @@ -1,5 +1,5 @@ use futures::task::{self, ArcWake}; -use parking_lot::Mutex; +use std::sync::Mutex; use std::convert::TryFrom; use std::future::Future; use std::pin::Pin; @@ -26,10 +26,10 @@ pub(crate) fn run() -> TimerHandle { /// processes everything. fn schedule_callback(timer: Arc>, when: Duration) { let window = web_sys::window().expect("Unable to access Window"); - let _ = window + window .set_timeout_with_callback_and_timeout_and_arguments_0( &Closure::once_into_js(move || { - let mut timer_lock = timer.lock(); + let mut timer_lock = timer.lock().unwrap(); // We start by polling the timer. If any new `Delay` is created, the waker will be used // to wake up this task pre-emptively. As such, we pass a `Waker` that calls diff --git a/src/lib.rs b/src/lib.rs index 2e9adef..a6c083b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,6 +35,6 @@ use timer::{ScheduledTimer, Timer, TimerHandle}; pub use self::delay::Delay; #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] -use instant::Instant; +use wasm_timer::Instant; #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] use std::time::Instant; From e39459682a23c84e50ff56357f1e297438f88b9b Mon Sep 17 00:00:00 2001 From: Ashley Date: Tue, 17 Dec 2019 13:03:09 +0100 Subject: [PATCH 08/26] Fmt --- src/global_wasm.rs | 2 +- src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/global_wasm.rs b/src/global_wasm.rs index 96a75cf..fc71994 100644 --- a/src/global_wasm.rs +++ b/src/global_wasm.rs @@ -1,9 +1,9 @@ use futures::task::{self, ArcWake}; -use std::sync::Mutex; use std::convert::TryFrom; use std::future::Future; use std::pin::Pin; use std::sync::Arc; +use std::sync::Mutex; use std::task::Context; use std::time::Duration; use wasm_bindgen::{closure::Closure, JsCast}; diff --git a/src/lib.rs b/src/lib.rs index a6c083b..7305d0e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,7 +34,7 @@ use heap_timer::HeapTimer; use timer::{ScheduledTimer, Timer, TimerHandle}; pub use self::delay::Delay; -#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] -use wasm_timer::Instant; #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] use std::time::Instant; +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +use wasm_timer::Instant; From bfbe40deac79e519f01c381fb09855aa694a789d Mon Sep 17 00:00:00 2001 From: Ashley Date: Thu, 2 Jan 2020 11:44:20 +0100 Subject: [PATCH 09/26] Use feature flag instead --- Cargo.toml | 18 ++++++++++++------ src/global_wasm.rs | 2 +- src/lib.rs | 8 ++++---- src/timer.rs | 4 ++-- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8eed02b..c8eeca4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,13 +13,19 @@ Timeouts for futures. """ [dependencies] - -[target.wasm32-unknown-unknown.dependencies] -wasm-timer = "0.2.4" -wasm-bindgen = "0.2.55" -futures = "0.3.1" -web-sys = "0.3.32" +wasm-timer = { version = "0.2.4", optional = true } +wasm-bindgen-crate = { package = "wasm-bindgen", version = "0.2.55", optional = true } +futures = { version = "0.3.1", optional = true } +web-sys = { version = "0.3.32", optional = true } [dev-dependencies] async-std = { version = "1.0.1", features = ["attributes"] } futures = "0.3.1" + +[features] +wasm-bindgen = [ + "wasm-timer", + "wasm-bindgen-crate", + "futures", + "web-sys" +] diff --git a/src/global_wasm.rs b/src/global_wasm.rs index fc71994..98d16a0 100644 --- a/src/global_wasm.rs +++ b/src/global_wasm.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use std::sync::Mutex; use std::task::Context; use std::time::Duration; -use wasm_bindgen::{closure::Closure, JsCast}; +use wasm_bindgen_crate::{closure::Closure, JsCast}; use crate::{Instant, Timer, TimerHandle}; diff --git a/src/lib.rs b/src/lib.rs index 7305d0e..78ca8bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,9 +19,9 @@ mod arc_list; mod atomic_waker; mod delay; -#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] +#[cfg(not(feature = "wasm-bindgen"))] mod global_native; -#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +#[cfg(feature = "wasm-bindgen")] mod global_wasm; mod heap; mod heap_timer; @@ -34,7 +34,7 @@ use heap_timer::HeapTimer; use timer::{ScheduledTimer, Timer, TimerHandle}; pub use self::delay::Delay; -#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] +#[cfg(not(feature = "wasm-bindgen"))] use std::time::Instant; -#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +#[cfg(feature = "wasm-bindgen")] use wasm_timer::Instant; diff --git a/src/timer.rs b/src/timer.rs index f28e056..15d0621 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -277,7 +277,7 @@ impl Default for TimerHandle { // handle which will return errors when timer objects are attempted to // be associated. if fallback == 0 { - #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] + #[cfg(not(feature = "wasm-bindgen"))] { let helper = match crate::global_native::HelperThread::new() { Ok(helper) => helper, @@ -298,7 +298,7 @@ impl Default for TimerHandle { } } - #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] + #[cfg(feature = "wasm-bindgen")] { let handle = crate::global_wasm::run(); From 0b5fe5ec15c4ace6673ec4da4f0da7a869cd9490 Mon Sep 17 00:00:00 2001 From: Ashley Date: Thu, 2 Jan 2020 16:09:36 +0100 Subject: [PATCH 10/26] Update Cargo.toml Co-Authored-By: Pierre Krieger --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c8eeca4..93033b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ Timeouts for futures. [dependencies] wasm-timer = { version = "0.2.4", optional = true } -wasm-bindgen-crate = { package = "wasm-bindgen", version = "0.2.55", optional = true } +wasm-bindgen = { version = "0.2.55", optional = true } futures = { version = "0.3.1", optional = true } web-sys = { version = "0.3.32", optional = true } From e8e60aa400678699a457806c780b8b4b63a6997c Mon Sep 17 00:00:00 2001 From: Ashley Date: Thu, 2 Jan 2020 17:17:43 +0100 Subject: [PATCH 11/26] Revert "Update Cargo.toml" This reverts commit 0b5fe5ec15c4ace6673ec4da4f0da7a869cd9490. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 93033b0..c8eeca4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ Timeouts for futures. [dependencies] wasm-timer = { version = "0.2.4", optional = true } -wasm-bindgen = { version = "0.2.55", optional = true } +wasm-bindgen-crate = { package = "wasm-bindgen", version = "0.2.55", optional = true } futures = { version = "0.3.1", optional = true } web-sys = { version = "0.3.32", optional = true } From ab267cce9a1851b11fee62aa3358a64f6ced2c80 Mon Sep 17 00:00:00 2001 From: Ashley Date: Tue, 7 Jan 2020 23:19:06 +0100 Subject: [PATCH 12/26] Fix native build --- src/timer.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/timer.rs b/src/timer.rs index 15d0621..c6c006f 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -277,7 +277,7 @@ impl Default for TimerHandle { // handle which will return errors when timer objects are attempted to // be associated. if fallback == 0 { - #[cfg(not(feature = "wasm-bindgen"))] + #[cfg(not(target_arch = "wasm32"))] { let helper = match crate::global_native::HelperThread::new() { Ok(helper) => helper, @@ -298,7 +298,7 @@ impl Default for TimerHandle { } } - #[cfg(feature = "wasm-bindgen")] + #[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))] { let handle = crate::global_wasm::run(); @@ -307,6 +307,8 @@ impl Default for TimerHandle { return handle; } } + #[cfg(all(not(feature = "wasm-bindgen"), target_arch = "wasm32"))] + compile_error!("The `wasm-bindgen` feature must be used when compiling to wasm."); fallback = HANDLE_FALLBACK.load(SeqCst); } From 94cb27608b641ac726e3cf666eab37e4ab63f113 Mon Sep 17 00:00:00 2001 From: Ashley Date: Tue, 7 Jan 2020 23:25:46 +0100 Subject: [PATCH 13/26] Use global_native when not on wasm32 --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 78ca8bf..010e8f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,7 @@ mod arc_list; mod atomic_waker; mod delay; -#[cfg(not(feature = "wasm-bindgen"))] +#[cfg(not(target_arch = "wasm32"))] mod global_native; #[cfg(feature = "wasm-bindgen")] mod global_wasm; From db64f45fe118ce8ee68e1ee2042aaca827576c95 Mon Sep 17 00:00:00 2001 From: Ashley Date: Wed, 8 Jan 2020 11:04:53 +0100 Subject: [PATCH 14/26] Appease clippy a bit --- src/global_native.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/global_native.rs b/src/global_native.rs index 98ccada..4ed8e51 100644 --- a/src/global_native.rs +++ b/src/global_native.rs @@ -84,7 +84,7 @@ static VTABLE: RawWakerVTable = RawWakerVTable::new(raw_clone, raw_wake, raw_wak fn raw_clone(ptr: *const ()) -> RawWaker { let me = ManuallyDrop::new(unsafe { Arc::from_raw(ptr as *const Thread) }); - mem::forget(me.clone()); + mem::forget(me); RawWaker::new(ptr, &VTABLE) } From ae4a83723a95b3150e35bff6f1a8fdd1af8a53a5 Mon Sep 17 00:00:00 2001 From: Ashley Date: Wed, 15 Jan 2020 10:41:28 +0100 Subject: [PATCH 15/26] Switch to gloo-timers --- Cargo.toml | 10 +--- src/delay.rs | 3 +- src/delay_wasm.rs | 22 ++++++++ src/{global_native.rs => global.rs} | 4 +- src/global_wasm.rs | 82 ----------------------------- src/heap_timer.rs | 2 +- src/lib.rs | 15 +++--- src/timer.rs | 53 +++++++------------ 8 files changed, 54 insertions(+), 137 deletions(-) create mode 100644 src/delay_wasm.rs rename src/{global_native.rs => global.rs} (98%) delete mode 100644 src/global_wasm.rs diff --git a/Cargo.toml b/Cargo.toml index c8eeca4..2d33ece 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,10 +13,7 @@ Timeouts for futures. """ [dependencies] -wasm-timer = { version = "0.2.4", optional = true } -wasm-bindgen-crate = { package = "wasm-bindgen", version = "0.2.55", optional = true } -futures = { version = "0.3.1", optional = true } -web-sys = { version = "0.3.32", optional = true } +gloo-timers = { version = "0.2.0", features = ["futures"], optional = true } [dev-dependencies] async-std = { version = "1.0.1", features = ["attributes"] } @@ -24,8 +21,5 @@ futures = "0.3.1" [features] wasm-bindgen = [ - "wasm-timer", - "wasm-bindgen-crate", - "futures", - "web-sys" + "gloo-timers" ] diff --git a/src/delay.rs b/src/delay.rs index dd4f45d..328eb2f 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -3,7 +3,6 @@ //! This module contains the `Delay` type which is a future that will resolve //! at a particular point in the future. -use crate::Instant; use std::fmt; use std::future::Future; use std::pin::Pin; @@ -11,7 +10,7 @@ use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::SeqCst; use std::sync::{Arc, Mutex}; use std::task::{Context, Poll}; -use std::time::Duration; +use std::time::{Duration, Instant}; use crate::arc_list::Node; use crate::AtomicWaker; diff --git a/src/delay_wasm.rs b/src/delay_wasm.rs new file mode 100644 index 0000000..246fc23 --- /dev/null +++ b/src/delay_wasm.rs @@ -0,0 +1,22 @@ +//! A version of `Delay` that works on wasm. + +use gloo_timers::future::TimeoutFuture; +use std::{time::Duration, pin::Pin, task::{Context, Poll}, future::Future}; + +#[derive(Debug)] +pub struct Delay(TimeoutFuture); + +impl Delay { + #[inline] + pub fn new(dur: Duration) -> Delay { + Self(TimeoutFuture::new(dur.as_millis() as u32)) + } +} + +impl Future for Delay { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + Pin::new(*self.0).poll(cx) + } +} diff --git a/src/global_native.rs b/src/global.rs similarity index 98% rename from src/global_native.rs rename to src/global.rs index 4ed8e51..4329913 100644 --- a/src/global_native.rs +++ b/src/global.rs @@ -1,4 +1,3 @@ -use crate::Instant; use std::future::Future; use std::io; use std::mem::{self, ManuallyDrop}; @@ -8,6 +7,7 @@ use std::sync::Arc; use std::task::{Context, RawWaker, RawWakerVTable, Waker}; use std::thread; use std::thread::Thread; +use std::time::Instant; use crate::{Timer, TimerHandle}; @@ -84,7 +84,7 @@ static VTABLE: RawWakerVTable = RawWakerVTable::new(raw_clone, raw_wake, raw_wak fn raw_clone(ptr: *const ()) -> RawWaker { let me = ManuallyDrop::new(unsafe { Arc::from_raw(ptr as *const Thread) }); - mem::forget(me); + mem::forget(me.clone()); RawWaker::new(ptr, &VTABLE) } diff --git a/src/global_wasm.rs b/src/global_wasm.rs deleted file mode 100644 index 98d16a0..0000000 --- a/src/global_wasm.rs +++ /dev/null @@ -1,82 +0,0 @@ -use futures::task::{self, ArcWake}; -use std::convert::TryFrom; -use std::future::Future; -use std::pin::Pin; -use std::sync::Arc; -use std::sync::Mutex; -use std::task::Context; -use std::time::Duration; -use wasm_bindgen_crate::{closure::Closure, JsCast}; - -use crate::{Instant, Timer, TimerHandle}; - -/// Starts a background task, creates a `Timer`, and returns a handle to it. -/// -/// > **Note**: Contrary to the native implementation, we don't have -/// > any `forget()` method, as the task is automatically considered -/// > as "forgotten". -pub(crate) fn run() -> TimerHandle { - let timer = Timer::new(); - let handle = timer.handle(); - schedule_callback(Arc::new(Mutex::new(timer)), Duration::new(0, 0)); - handle -} - -/// Calls `Window::setTimeout` with the given `Duration`. The callback wakes up the timer and -/// processes everything. -fn schedule_callback(timer: Arc>, when: Duration) { - let window = web_sys::window().expect("Unable to access Window"); - window - .set_timeout_with_callback_and_timeout_and_arguments_0( - &Closure::once_into_js(move || { - let mut timer_lock = timer.lock().unwrap(); - - // We start by polling the timer. If any new `Delay` is created, the waker will be used - // to wake up this task pre-emptively. As such, we pass a `Waker` that calls - // `schedule_callback` with a delay of `0`. - let waker = task::waker(Arc::new(Waker { - timer: timer.clone(), - })); - let _ = Future::poll(Pin::new(&mut *timer_lock), &mut Context::from_waker(&waker)); - - // Notify the timers that are ready. - let now = Instant::now(); - timer_lock.advance_to(now); - - // Each call to `schedule_callback` calls `schedule_callback` again, but also leaves - // the possibility for `schedule_callback` to be called in parallel. Since we don't - // want too many useless callbacks, we... - // TODO: ugh, that's a hack - if Arc::strong_count(&timer) > 20 { - return; - } - - // We call `schedule_callback` again for the next event. - let sleep_dur = timer_lock - .next_event() - .map(|next_event| { - if next_event > now { - next_event - now - } else { - Duration::new(0, 0) - } - }) - .unwrap_or(Duration::from_secs(5)); - drop(timer_lock); - schedule_callback(timer, sleep_dur); - }) - .unchecked_ref(), - i32::try_from(when.as_millis()).unwrap_or(0), - ) - .unwrap(); -} - -struct Waker { - timer: Arc>, -} - -impl ArcWake for Waker { - fn wake_by_ref(arc_self: &Arc) { - schedule_callback(arc_self.timer.clone(), Duration::new(0, 0)); - } -} diff --git a/src/heap_timer.rs b/src/heap_timer.rs index 82d31e1..d1c90e2 100644 --- a/src/heap_timer.rs +++ b/src/heap_timer.rs @@ -1,6 +1,6 @@ -use crate::Instant; use std::cmp::Ordering; use std::sync::Arc; +use std::time::Instant; use crate::{Node, ScheduledTimer}; diff --git a/src/lib.rs b/src/lib.rs index 010e8f5..9a661ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,11 +18,11 @@ mod arc_list; mod atomic_waker; +#[cfg(not(all(target_arch = "wasm32", feature = "wasm-bindgen")))] mod delay; -#[cfg(not(target_arch = "wasm32"))] -mod global_native; -#[cfg(feature = "wasm-bindgen")] -mod global_wasm; +#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))] +mod delay_wasm; +mod global; mod heap; mod heap_timer; mod timer; @@ -33,8 +33,7 @@ use heap::{Heap, Slot}; use heap_timer::HeapTimer; use timer::{ScheduledTimer, Timer, TimerHandle}; +#[cfg(not(all(target_arch = "wasm32", feature = "wasm-bindgen")))] pub use self::delay::Delay; -#[cfg(not(feature = "wasm-bindgen"))] -use std::time::Instant; -#[cfg(feature = "wasm-bindgen")] -use wasm_timer::Instant; +#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))] +pub use self::delay_wasm::Delay; diff --git a/src/timer.rs b/src/timer.rs index c6c006f..e3dfacd 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -1,15 +1,16 @@ -use crate::Instant; use std::fmt; -use std::future::Future; use std::mem; use std::pin::Pin; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::SeqCst; use std::sync::{Arc, Mutex, Weak}; use std::task::{Context, Poll}; +use std::time::Instant; + +use std::future::Future; use crate::AtomicWaker; -use crate::{ArcList, Heap, HeapTimer, Node, Slot}; +use crate::{global, ArcList, Heap, HeapTimer, Node, Slot}; /// A "timer heap" used to power separately owned instances of `Delay`. /// @@ -277,39 +278,23 @@ impl Default for TimerHandle { // handle which will return errors when timer objects are attempted to // be associated. if fallback == 0 { - #[cfg(not(target_arch = "wasm32"))] - { - let helper = match crate::global_native::HelperThread::new() { - Ok(helper) => helper, - Err(_) => return TimerHandle { inner: Weak::new() }, - }; - - // If we successfully set ourselves as the actual fallback then we - // want to `forget` the helper thread to ensure that it persists - // globally. If we fail to set ourselves as the fallback that means - // that someone was racing with this call to - // `TimerHandle::default`. They ended up winning so we'll destroy - // our helper thread (which shuts down the thread) and reload the - // fallback. - if helper.handle().set_as_global_fallback().is_ok() { - let ret = helper.handle(); - helper.forget(); - return ret; - } - } - - #[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))] - { - let handle = crate::global_wasm::run(); + let helper = match global::HelperThread::new() { + Ok(helper) => helper, + Err(_) => return TimerHandle { inner: Weak::new() }, + }; - // Same as above. - if handle.clone().set_as_global_fallback().is_ok() { - return handle; - } + // If we successfully set ourselves as the actual fallback then we + // want to `forget` the helper thread to ensure that it persists + // globally. If we fail to set ourselves as the fallback that means + // that someone was racing with this call to + // `TimerHandle::default`. They ended up winning so we'll destroy + // our helper thread (which shuts down the thread) and reload the + // fallback. + if helper.handle().set_as_global_fallback().is_ok() { + let ret = helper.handle(); + helper.forget(); + return ret; } - #[cfg(all(not(feature = "wasm-bindgen"), target_arch = "wasm32"))] - compile_error!("The `wasm-bindgen` feature must be used when compiling to wasm."); - fallback = HANDLE_FALLBACK.load(SeqCst); } From 22505772fd6a3434900ee28cc61f369df4486023 Mon Sep 17 00:00:00 2001 From: Ashley Date: Wed, 15 Jan 2020 11:00:14 +0100 Subject: [PATCH 16/26] Fix pinning --- src/delay_wasm.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/delay_wasm.rs b/src/delay_wasm.rs index 246fc23..c811498 100644 --- a/src/delay_wasm.rs +++ b/src/delay_wasm.rs @@ -3,10 +3,12 @@ use gloo_timers::future::TimeoutFuture; use std::{time::Duration, pin::Pin, task::{Context, Poll}, future::Future}; +/// A version of `Delay` that works on wasm. #[derive(Debug)] pub struct Delay(TimeoutFuture); impl Delay { + /// Creates a new future which will fire at `dur` time into the future. #[inline] pub fn new(dur: Duration) -> Delay { Self(TimeoutFuture::new(dur.as_millis() as u32)) @@ -17,6 +19,6 @@ impl Future for Delay { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - Pin::new(*self.0).poll(cx) + Pin::new(&mut Pin::into_inner(self).0).poll(cx) } } From 5ced38b4c6441ea6ef399dc0b5c65a99a37e0808 Mon Sep 17 00:00:00 2001 From: Ashley Date: Wed, 15 Jan 2020 11:33:48 +0100 Subject: [PATCH 17/26] Add send_wrapper --- Cargo.toml | 4 +++- src/delay_wasm.rs | 9 ++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2d33ece..f795fb3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ Timeouts for futures. [dependencies] gloo-timers = { version = "0.2.0", features = ["futures"], optional = true } +send_wrapper = { version = "0.4.0", optional = true } [dev-dependencies] async-std = { version = "1.0.1", features = ["attributes"] } @@ -21,5 +22,6 @@ futures = "0.3.1" [features] wasm-bindgen = [ - "gloo-timers" + "gloo-timers", + "send_wrapper" ] diff --git a/src/delay_wasm.rs b/src/delay_wasm.rs index c811498..033431c 100644 --- a/src/delay_wasm.rs +++ b/src/delay_wasm.rs @@ -1,17 +1,20 @@ //! A version of `Delay` that works on wasm. use gloo_timers::future::TimeoutFuture; +use send_wrapper::SendWrapper; use std::{time::Duration, pin::Pin, task::{Context, Poll}, future::Future}; /// A version of `Delay` that works on wasm. #[derive(Debug)] -pub struct Delay(TimeoutFuture); +pub struct Delay(SendWrapper); impl Delay { /// Creates a new future which will fire at `dur` time into the future. #[inline] pub fn new(dur: Duration) -> Delay { - Self(TimeoutFuture::new(dur.as_millis() as u32)) + Self( + SendWrapper::new(TimeoutFuture::new(dur.as_millis() as u32)) + ) } } @@ -19,6 +22,6 @@ impl Future for Delay { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - Pin::new(&mut Pin::into_inner(self).0).poll(cx) + Pin::new(&mut *Pin::into_inner(self).0).poll(cx) } } From 1c66b187d2323e6f3f6643cad4cbe00918527dd1 Mon Sep 17 00:00:00 2001 From: Ashley Date: Wed, 15 Jan 2020 11:45:04 +0100 Subject: [PATCH 18/26] Add a reset stub --- src/delay_wasm.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/delay_wasm.rs b/src/delay_wasm.rs index 033431c..82a2ad4 100644 --- a/src/delay_wasm.rs +++ b/src/delay_wasm.rs @@ -2,7 +2,7 @@ use gloo_timers::future::TimeoutFuture; use send_wrapper::SendWrapper; -use std::{time::Duration, pin::Pin, task::{Context, Poll}, future::Future}; +use std::{time::{Duration, Instant}, pin::Pin, task::{Context, Poll}, future::Future}; /// A version of `Delay` that works on wasm. #[derive(Debug)] @@ -16,6 +16,10 @@ impl Delay { SendWrapper::new(TimeoutFuture::new(dur.as_millis() as u32)) ) } + + /// Resets the timeout. Does nothing on wasm. + #[inline] + pub fn reset(&mut self, at: Instant) {} } impl Future for Delay { From 2746e4d21a3c685e185c16e56aae0c2a82e3323f Mon Sep 17 00:00:00 2001 From: Ashley Date: Wed, 15 Jan 2020 15:53:04 +0100 Subject: [PATCH 19/26] Change reset behavious to panic --- src/delay_wasm.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/delay_wasm.rs b/src/delay_wasm.rs index 82a2ad4..7f0fed8 100644 --- a/src/delay_wasm.rs +++ b/src/delay_wasm.rs @@ -17,9 +17,11 @@ impl Delay { ) } - /// Resets the timeout. Does nothing on wasm. + /// Resets the timeout. Panics on wasm. #[inline] - pub fn reset(&mut self, at: Instant) {} + pub fn reset(&mut self, at: Instant) { + panic!("Resetting the timeout is not supported on wasm.") + } } impl Future for Delay { From 5d737caeacffe24cbdfb0e9b5b481450e612443a Mon Sep 17 00:00:00 2001 From: Ashley Date: Tue, 21 Jan 2020 11:56:55 +0100 Subject: [PATCH 20/26] Remove 'when' from Delay, switch reset to take a Duration --- src/delay.rs | 21 +++++---------------- src/delay_wasm.rs | 6 +++--- tests/smoke.rs | 2 +- 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 328eb2f..d789875 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -25,7 +25,6 @@ use crate::{ScheduledTimer, TimerHandle}; /// at. pub struct Delay { state: Option>>, - when: Instant, } impl Delay { @@ -38,12 +37,6 @@ impl Delay { Delay::new_handle(Instant::now() + dur, Default::default()) } - /// Return the `Instant` when this delay will fire. - #[inline] - pub fn when(&self) -> Instant { - self.when - } - /// Creates a new future which will fire at the time specified by `at`. /// /// The returned instance of `Delay` will be bound to the timer specified by @@ -54,7 +47,6 @@ impl Delay { None => { return Delay { state: None, - when: at, } } }; @@ -72,28 +64,25 @@ impl Delay { if inner.list.push(&state).is_err() { return Delay { state: None, - when: at, }; } inner.waker.wake(); Delay { state: Some(state), - when: at, } } /// Resets this timeout to an new timeout which will fire at the time /// specified by `at`. #[inline] - pub fn reset(&mut self, at: Instant) { - self.when = at; - if self._reset(self.when).is_err() { + pub fn reset(&mut self, delay: Duration) { + if self._reset(delay).is_err() { self.state = None } } - fn _reset(&mut self, at: Instant) -> Result<(), ()> { + fn _reset(&mut self, delay: Duration) -> Result<(), ()> { let state = match self.state { Some(ref state) => state, None => return Err(()), @@ -111,7 +100,7 @@ impl Delay { Err(s) => bits = s, } } - *state.at.lock().unwrap() = Some(at); + *state.at.lock().unwrap() = Some(Instant::now() + delay); // If we fail to push our node then we've become an inert timer, so // we'll want to clear our `state` field accordingly timeouts.list.push(state)?; @@ -165,6 +154,6 @@ impl Drop for Delay { impl fmt::Debug for Delay { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.debug_struct("Delay").field("when", &self.when).finish() + f.debug_struct("Delay").finish() } } diff --git a/src/delay_wasm.rs b/src/delay_wasm.rs index 7f0fed8..7878b10 100644 --- a/src/delay_wasm.rs +++ b/src/delay_wasm.rs @@ -2,7 +2,7 @@ use gloo_timers::future::TimeoutFuture; use send_wrapper::SendWrapper; -use std::{time::{Duration, Instant}, pin::Pin, task::{Context, Poll}, future::Future}; +use std::{time::Duration, pin::Pin, task::{Context, Poll}, future::Future}; /// A version of `Delay` that works on wasm. #[derive(Debug)] @@ -19,8 +19,8 @@ impl Delay { /// Resets the timeout. Panics on wasm. #[inline] - pub fn reset(&mut self, at: Instant) { - panic!("Resetting the timeout is not supported on wasm.") + pub fn reset(&mut self, at: Duration) { + *self = Delay::new(at); } } diff --git a/tests/smoke.rs b/tests/smoke.rs index a8da290..b2f172e 100644 --- a/tests/smoke.rs +++ b/tests/smoke.rs @@ -24,7 +24,7 @@ async fn reset() -> Result<(), Box> { assert!(i.elapsed() > dur); let i = Instant::now(); - d.reset(Instant::now() + dur); + d.reset(dur); d.await; assert!(i.elapsed() > dur); Ok(()) From 4cf9125d5ebdc620c7186c62abfea61bfdd13f11 Mon Sep 17 00:00:00 2001 From: Ashley Date: Tue, 21 Jan 2020 11:58:48 +0100 Subject: [PATCH 21/26] run cargo fmt --- src/delay.rs | 14 +++----------- src/delay_wasm.rs | 37 ++++++++++++++++++++----------------- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index d789875..16b81f9 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -44,11 +44,7 @@ impl Delay { pub(crate) fn new_handle(at: Instant, handle: TimerHandle) -> Delay { let inner = match handle.inner.upgrade() { Some(i) => i, - None => { - return Delay { - state: None, - } - } + None => return Delay { state: None }, }; let state = Arc::new(Node::new(ScheduledTimer { at: Mutex::new(Some(at)), @@ -62,15 +58,11 @@ impl Delay { // timer, meaning that we'll want to immediately return an error from // `poll`. if inner.list.push(&state).is_err() { - return Delay { - state: None, - }; + return Delay { state: None }; } inner.waker.wake(); - Delay { - state: Some(state), - } + Delay { state: Some(state) } } /// Resets this timeout to an new timeout which will fire at the time diff --git a/src/delay_wasm.rs b/src/delay_wasm.rs index 7878b10..ba18913 100644 --- a/src/delay_wasm.rs +++ b/src/delay_wasm.rs @@ -2,32 +2,35 @@ use gloo_timers::future::TimeoutFuture; use send_wrapper::SendWrapper; -use std::{time::Duration, pin::Pin, task::{Context, Poll}, future::Future}; +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, + time::Duration, +}; /// A version of `Delay` that works on wasm. #[derive(Debug)] pub struct Delay(SendWrapper); impl Delay { - /// Creates a new future which will fire at `dur` time into the future. - #[inline] - pub fn new(dur: Duration) -> Delay { - Self( - SendWrapper::new(TimeoutFuture::new(dur.as_millis() as u32)) - ) - } + /// Creates a new future which will fire at `dur` time into the future. + #[inline] + pub fn new(dur: Duration) -> Delay { + Self(SendWrapper::new(TimeoutFuture::new(dur.as_millis() as u32))) + } - /// Resets the timeout. Panics on wasm. - #[inline] - pub fn reset(&mut self, at: Duration) { - *self = Delay::new(at); - } + /// Resets the timeout. Panics on wasm. + #[inline] + pub fn reset(&mut self, at: Duration) { + *self = Delay::new(at); + } } impl Future for Delay { - type Output = (); + type Output = (); - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - Pin::new(&mut *Pin::into_inner(self).0).poll(cx) - } + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + Pin::new(&mut *Pin::into_inner(self).0).poll(cx) + } } From 034ea154bbd0fd78f8a9fb63ea5685c9bdacc109 Mon Sep 17 00:00:00 2001 From: Ashley Date: Mon, 27 Jan 2020 11:56:21 +0100 Subject: [PATCH 22/26] Update src/delay_wasm.rs Co-Authored-By: Yoshua Wuyts --- src/delay_wasm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/delay_wasm.rs b/src/delay_wasm.rs index ba18913..86dd7c7 100644 --- a/src/delay_wasm.rs +++ b/src/delay_wasm.rs @@ -20,7 +20,7 @@ impl Delay { Self(SendWrapper::new(TimeoutFuture::new(dur.as_millis() as u32))) } - /// Resets the timeout. Panics on wasm. + /// Resets the timeout. #[inline] pub fn reset(&mut self, at: Duration) { *self = Delay::new(at); From 0190692eb16159c2ba2563e307b7698f6c9fc690 Mon Sep 17 00:00:00 2001 From: Ashley Date: Mon, 27 Jan 2020 11:56:37 +0100 Subject: [PATCH 23/26] Update src/delay_wasm.rs Co-Authored-By: Yoshua Wuyts --- src/delay_wasm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/delay_wasm.rs b/src/delay_wasm.rs index 86dd7c7..2ec56ac 100644 --- a/src/delay_wasm.rs +++ b/src/delay_wasm.rs @@ -22,7 +22,7 @@ impl Delay { /// Resets the timeout. #[inline] - pub fn reset(&mut self, at: Duration) { + pub fn reset(&mut self, dur: Duration) { *self = Delay::new(at); } } From 511b0a1e080ff2e29199ac37dba2575341b6ce22 Mon Sep 17 00:00:00 2001 From: Ashley Date: Mon, 27 Jan 2020 11:56:58 +0100 Subject: [PATCH 24/26] Update src/delay.rs Co-Authored-By: Yoshua Wuyts --- src/delay.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/delay.rs b/src/delay.rs index 16b81f9..8f3ce70 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -68,7 +68,7 @@ impl Delay { /// Resets this timeout to an new timeout which will fire at the time /// specified by `at`. #[inline] - pub fn reset(&mut self, delay: Duration) { + pub fn reset(&mut self, dur: Duration) { if self._reset(delay).is_err() { self.state = None } From ef8a44120581d874fe8e9d9e735fcd6c8cbb09b8 Mon Sep 17 00:00:00 2001 From: Ashley Date: Mon, 27 Jan 2020 12:04:20 +0100 Subject: [PATCH 25/26] fix clippy, nitpicks --- Cargo.toml | 4 ++-- src/lib.rs | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f795fb3..f7fa63b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,6 @@ futures = "0.3.1" [features] wasm-bindgen = [ - "gloo-timers", - "send_wrapper" + "gloo-timers", + "send_wrapper" ] diff --git a/src/lib.rs b/src/lib.rs index 9a661ee..f9173f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,15 +2,13 @@ //! //! # Examples //! -//! ```no_run -//! # #[async_std::main] -//! # async fn main() { +//! ``` //! use std::time::Duration; //! use futures_timer::Delay; +//! use futures::executor::block_on; //! -//! let now = Delay::new(Duration::from_secs(3)).await; +//! let now = block_on(Delay::new(Duration::from_secs(3))); //! println!("waited for 3 secs"); -//! # } //! ``` #![deny(missing_docs)] From 9f620d7fe3fc62c8545c3c0620bdea01aba02130 Mon Sep 17 00:00:00 2001 From: Ashley Date: Mon, 27 Jan 2020 12:08:32 +0100 Subject: [PATCH 26/26] delay -> dur --- .idea/.gitignore | 2 ++ .idea/futures-timer.iml | 14 ++++++++++++++ .idea/misc.xml | 6 ++++++ .idea/modules.xml | 8 ++++++++ .idea/vcs.xml | 6 ++++++ src/delay.rs | 6 +++--- 6 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/futures-timer.iml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..5c98b42 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,2 @@ +# Default ignored files +/workspace.xml \ No newline at end of file diff --git a/.idea/futures-timer.iml b/.idea/futures-timer.iml new file mode 100644 index 0000000..b7b4242 --- /dev/null +++ b/.idea/futures-timer.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..28a804d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..38e13ee --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/delay.rs b/src/delay.rs index 8f3ce70..a4ab758 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -69,12 +69,12 @@ impl Delay { /// specified by `at`. #[inline] pub fn reset(&mut self, dur: Duration) { - if self._reset(delay).is_err() { + if self._reset(dur).is_err() { self.state = None } } - fn _reset(&mut self, delay: Duration) -> Result<(), ()> { + fn _reset(&mut self, dur: Duration) -> Result<(), ()> { let state = match self.state { Some(ref state) => state, None => return Err(()), @@ -92,7 +92,7 @@ impl Delay { Err(s) => bits = s, } } - *state.at.lock().unwrap() = Some(Instant::now() + delay); + *state.at.lock().unwrap() = Some(Instant::now() + dur); // If we fail to push our node then we've become an inert timer, so // we'll want to clear our `state` field accordingly timeouts.list.push(state)?;