From 6b071aef7baaaa074852dd1b6969692ce5c6abdd Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 3 Jan 2020 11:25:20 +0100 Subject: [PATCH 1/4] Fix timers to work in workers too. --- crates/timers/Cargo.toml | 1 + crates/timers/src/callback.rs | 61 ++++++++++++++++++++++------------- 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/crates/timers/Cargo.toml b/crates/timers/Cargo.toml index f590a424..1cec68b8 100644 --- a/crates/timers/Cargo.toml +++ b/crates/timers/Cargo.toml @@ -20,6 +20,7 @@ futures-channel = { version = "0.3", optional = true } version = "0.3.19" features = [ "Window", + "WorkerGlobalScope", ] [features] diff --git a/crates/timers/src/callback.rs b/crates/timers/src/callback.rs index e872340c..e9276a6c 100644 --- a/crates/timers/src/callback.rs +++ b/crates/timers/src/callback.rs @@ -1,8 +1,25 @@ //! Callback-style timer APIs. +use js_sys::Reflect; use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use web_sys::window; +use wasm_bindgen::{throw_val, JsCast, JsValue}; +use web_sys::{Window, WorkerGlobalScope}; + +macro_rules! global { + ($global:ident, $fun:expr) => {{ + let global: JsValue = js_sys::global().into(); + + if Reflect::has(&global, &String::from("Window").into()).unwrap_throw() { + let $global: Window = global.into(); + $fun + } else if Reflect::has(&global, &String::from("WorkerGlobalScope").into()).unwrap_throw() { + let $global: WorkerGlobalScope = global.into(); + $fun + } else { + throw_val(global.into()) + } + }}; +} /// A scheduled timeout. /// @@ -20,9 +37,7 @@ pub struct Timeout { impl Drop for Timeout { fn drop(&mut self) { if let Some(id) = self.id { - window() - .unwrap_throw() - .clear_timeout_with_handle(id); + global!(global, global.clear_timeout_with_handle(id)); } } } @@ -46,13 +61,15 @@ impl Timeout { { let closure = Closure::once(callback); - let id = window() - .unwrap_throw() - .set_timeout_with_callback_and_timeout_and_arguments_0( - closure.as_ref().unchecked_ref::(), - millis as i32 - ) - .unwrap_throw(); + let id = global!( + global, + global + .set_timeout_with_callback_and_timeout_and_arguments_0( + closure.as_ref().unchecked_ref::(), + millis as i32, + ) + .unwrap_throw() + ); Timeout { id: Some(id), @@ -124,9 +141,7 @@ pub struct Interval { impl Drop for Interval { fn drop(&mut self) { if let Some(id) = self.id { - window() - .unwrap_throw() - .clear_interval_with_handle(id); + global!(global, global.clear_interval_with_handle(id)); } } } @@ -149,13 +164,15 @@ impl Interval { { let closure = Closure::wrap(Box::new(callback) as Box); - let id = window() - .unwrap_throw() - .set_interval_with_callback_and_timeout_and_arguments_0( - closure.as_ref().unchecked_ref::(), - millis as i32, - ) - .unwrap_throw(); + let id = global!( + global, + global + .set_interval_with_callback_and_timeout_and_arguments_0( + closure.as_ref().unchecked_ref::(), + millis as i32, + ) + .unwrap_throw() + ); Interval { id: Some(id), From 13fadcd8f97964caf26f3b843c56be758e3e5661 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sat, 18 Jan 2020 14:35:01 +0100 Subject: [PATCH 2/4] Replace macro by enum `WindowOrWorker`. --- crates/timers/src/callback.rs | 57 ++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/crates/timers/src/callback.rs b/crates/timers/src/callback.rs index e9276a6c..b353c0ce 100644 --- a/crates/timers/src/callback.rs +++ b/crates/timers/src/callback.rs @@ -1,26 +1,51 @@ //! Callback-style timer APIs. -use js_sys::Reflect; +use js_sys::{Function, Reflect}; use wasm_bindgen::prelude::*; use wasm_bindgen::{throw_val, JsCast, JsValue}; use web_sys::{Window, WorkerGlobalScope}; -macro_rules! global { - ($global:ident, $fun:expr) => {{ +thread_local! { + static GLOBAL: WindowOrWorker = WindowOrWorker::new(); +} + +enum WindowOrWorker { + Window(Window), + Worker(WorkerGlobalScope), +} + +impl WindowOrWorker { + fn new() -> Self { let global: JsValue = js_sys::global().into(); if Reflect::has(&global, &String::from("Window").into()).unwrap_throw() { - let $global: Window = global.into(); - $fun + WindowOrWorker::Window(global.into()) } else if Reflect::has(&global, &String::from("WorkerGlobalScope").into()).unwrap_throw() { - let $global: WorkerGlobalScope = global.into(); - $fun + WindowOrWorker::Worker(global.into()) } else { - throw_val(global.into()) + throw_val(global) } - }}; + } } +macro_rules! impl_window_or_worker { + ($name:ident, ($($par_name:ident: $par_type:ty),*)$(, $return:ty)?) => { + impl WindowOrWorker { + fn $name(&self, $($par_name: $par_type),*) $( -> $return)? { + match self { + Self::Window(window) => window.$name($($par_name),*), + Self::Worker(worker) => worker.$name($($par_name),*), + } + } + } + }; +} + +impl_window_or_worker!(set_timeout_with_callback_and_timeout_and_arguments_0, (handler: &Function, timeout: i32), Result); +impl_window_or_worker!(clear_timeout_with_handle, (handle: i32)); +impl_window_or_worker!(clear_interval_with_handle, (handle: i32)); +impl_window_or_worker!(set_interval_with_callback_and_timeout_and_arguments_0, (handler: &Function, timeout: i32), Result); + /// A scheduled timeout. /// /// See `Timeout::new` for scheduling new timeouts. @@ -37,7 +62,7 @@ pub struct Timeout { impl Drop for Timeout { fn drop(&mut self) { if let Some(id) = self.id { - global!(global, global.clear_timeout_with_handle(id)); + GLOBAL.with(|global| global.clear_timeout_with_handle(id)); } } } @@ -61,15 +86,14 @@ impl Timeout { { let closure = Closure::once(callback); - let id = global!( - global, + let id = GLOBAL.with(|global| { global .set_timeout_with_callback_and_timeout_and_arguments_0( closure.as_ref().unchecked_ref::(), millis as i32, ) .unwrap_throw() - ); + }); Timeout { id: Some(id), @@ -141,7 +165,7 @@ pub struct Interval { impl Drop for Interval { fn drop(&mut self) { if let Some(id) = self.id { - global!(global, global.clear_interval_with_handle(id)); + GLOBAL.with(|global| global.clear_interval_with_handle(id)); } } } @@ -164,15 +188,14 @@ impl Interval { { let closure = Closure::wrap(Box::new(callback) as Box); - let id = global!( - global, + let id = GLOBAL.with(|global| { global .set_interval_with_callback_and_timeout_and_arguments_0( closure.as_ref().unchecked_ref::(), millis as i32, ) .unwrap_throw() - ); + }); Interval { id: Some(id), From 943da56ea7f7ae0cb7df34817f06d07ad10bf322 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sat, 18 Jan 2020 18:30:55 +0100 Subject: [PATCH 3/4] Change macro implementation. --- crates/timers/src/callback.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/crates/timers/src/callback.rs b/crates/timers/src/callback.rs index b353c0ce..d944962a 100644 --- a/crates/timers/src/callback.rs +++ b/crates/timers/src/callback.rs @@ -29,22 +29,26 @@ impl WindowOrWorker { } macro_rules! impl_window_or_worker { - ($name:ident, ($($par_name:ident: $par_type:ty),*)$(, $return:ty)?) => { + ($(fn $name:ident($($par_name:ident: $par_type:ty),*)$( -> $return:ty)?;)+) => { impl WindowOrWorker { - fn $name(&self, $($par_name: $par_type),*) $( -> $return)? { - match self { - Self::Window(window) => window.$name($($par_name),*), - Self::Worker(worker) => worker.$name($($par_name),*), + $( + fn $name(&self, $($par_name: $par_type),*)$( -> $return)? { + match self { + Self::Window(window) => window.$name($($par_name),*), + Self::Worker(worker) => worker.$name($($par_name),*), + } } - } + )+ } }; } -impl_window_or_worker!(set_timeout_with_callback_and_timeout_and_arguments_0, (handler: &Function, timeout: i32), Result); -impl_window_or_worker!(clear_timeout_with_handle, (handle: i32)); -impl_window_or_worker!(clear_interval_with_handle, (handle: i32)); -impl_window_or_worker!(set_interval_with_callback_and_timeout_and_arguments_0, (handler: &Function, timeout: i32), Result); +impl_window_or_worker! { + fn set_timeout_with_callback_and_timeout_and_arguments_0(handler: &Function, timeout: i32) -> Result; + fn set_interval_with_callback_and_timeout_and_arguments_0(handler: &Function, timeout: i32) -> Result; + fn clear_timeout_with_handle(handle: i32); + fn clear_interval_with_handle(handle: i32); +} /// A scheduled timeout. /// From 994d683f64fc04380f495fafb356521f346eff5f Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sat, 18 Jan 2020 20:10:48 +0100 Subject: [PATCH 4/4] Change implementation of getting new global. --- crates/timers/src/callback.rs | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/crates/timers/src/callback.rs b/crates/timers/src/callback.rs index d944962a..b0454170 100644 --- a/crates/timers/src/callback.rs +++ b/crates/timers/src/callback.rs @@ -1,8 +1,8 @@ //! Callback-style timer APIs. -use js_sys::{Function, Reflect}; +use js_sys::Function; use wasm_bindgen::prelude::*; -use wasm_bindgen::{throw_val, JsCast, JsValue}; +use wasm_bindgen::{JsCast, JsValue}; use web_sys::{Window, WorkerGlobalScope}; thread_local! { @@ -16,14 +16,25 @@ enum WindowOrWorker { impl WindowOrWorker { fn new() -> Self { - let global: JsValue = js_sys::global().into(); + #[wasm_bindgen] + extern "C" { + type Global; - if Reflect::has(&global, &String::from("Window").into()).unwrap_throw() { - WindowOrWorker::Window(global.into()) - } else if Reflect::has(&global, &String::from("WorkerGlobalScope").into()).unwrap_throw() { - WindowOrWorker::Worker(global.into()) + #[wasm_bindgen(method, getter, js_name = Window)] + fn window(this: &Global) -> JsValue; + + #[wasm_bindgen(method, getter, js_name = WorkerGlobalScope)] + fn worker(this: &Global) -> JsValue; + } + + let global: Global = js_sys::global().unchecked_into(); + + if !global.window().is_undefined() { + Self::Window(global.unchecked_into()) + } else if !global.worker().is_undefined() { + Self::Worker(global.unchecked_into()) } else { - throw_val(global) + panic!("Only supported in a browser or web worker"); } } }