From b4c20460586b7764f5f5790f80e55f640fc6bbdd Mon Sep 17 00:00:00 2001 From: officeyutong Date: Thu, 21 Nov 2024 22:08:07 +0800 Subject: [PATCH 1/4] fix: use global() to get performance API on wasm32-unknown-unknown --- src/clocks/monotonic/wasm.rs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/clocks/monotonic/wasm.rs b/src/clocks/monotonic/wasm.rs index 39a742b..0d2594d 100644 --- a/src/clocks/monotonic/wasm.rs +++ b/src/clocks/monotonic/wasm.rs @@ -1,6 +1,12 @@ -const WASM_WRONG_ENV: &str = "failed to find the global `window` object: the `wasm32-unknown-unknown` implementation only supports running in web browsers; wse `wasm32-wasi` to run elsewhere"; -const WASM_MISSING_WINDOW_PERF: &str = "failed to find `window.performance`"; +use web_sys::{ + js_sys::Reflect, + wasm_bindgen::{JsCast, JsValue}, + Performance, +}; +const WASM_MISSING_GLOBAL_THIS_PERF: &str = "failed to find `globalThis.performance`"; +const WASM_UNABLE_TO_CAST_PERF: &str = + "Unable to cast `globalThis.performance` to Performance type"; #[derive(Clone, Copy, Debug, Default)] pub struct Monotonic { _default: (), @@ -9,12 +15,15 @@ pub struct Monotonic { #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] impl Monotonic { pub fn now(&self) -> u64 { - let now = web_sys::window() - .expect(WASM_WRONG_ENV) - .performance() - .expect(WASM_MISSING_WINDOW_PERF) - .now(); - // `window.performance.now()` returns the time in milliseconds. + let now = Reflect::get( + &web_sys::js_sys::global(), + &JsValue::from_str("performance"), + ) + .expect(WASM_MISSING_GLOBAL_THIS_PERF) + .dyn_ref::() + .expect(WASM_UNABLE_TO_CAST_PERF) + .now(); + // `performance.now()` returns the time in milliseconds. return f64::trunc(now * 1_000_000.0) as u64; } } From fc259ac2c8d510e12b8669d06729a9641f7a5676 Mon Sep 17 00:00:00 2001 From: officeyutong Date: Wed, 27 Nov 2024 16:45:28 +0800 Subject: [PATCH 2/4] update: avoid using Reflection to get Performance each time --- src/clocks/monotonic/mod.rs | 14 +++++--- src/clocks/monotonic/wasm.rs | 36 ------------------- src/clocks/monotonic/wasm_browser.rs | 54 ++++++++++++++++++++++++++++ src/clocks/monotonic/wasm_wasi.rs | 10 ++++++ 4 files changed, 74 insertions(+), 40 deletions(-) delete mode 100644 src/clocks/monotonic/wasm.rs create mode 100644 src/clocks/monotonic/wasm_browser.rs create mode 100644 src/clocks/monotonic/wasm_wasi.rs diff --git a/src/clocks/monotonic/mod.rs b/src/clocks/monotonic/mod.rs index b746333..4442224 100644 --- a/src/clocks/monotonic/mod.rs +++ b/src/clocks/monotonic/mod.rs @@ -3,10 +3,16 @@ mod windows; #[cfg(target_os = "windows")] pub use self::windows::Monotonic; -#[cfg(target_arch = "wasm32")] -mod wasm; -#[cfg(target_arch = "wasm32")] -pub use self::wasm::Monotonic; +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +mod wasm_browser; +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +pub use self::wasm_browser::Monotonic; + +#[cfg(all(target_arch = "wasm32", target_os = "wasi"))] +mod wasm_wasi; +#[cfg(all(target_arch = "wasm32", target_os = "wasi"))] +pub use self::wasm_wasi::Monotonic; + #[cfg(not(any(target_os = "windows", target_arch = "wasm32")))] mod unix; diff --git a/src/clocks/monotonic/wasm.rs b/src/clocks/monotonic/wasm.rs deleted file mode 100644 index 0d2594d..0000000 --- a/src/clocks/monotonic/wasm.rs +++ /dev/null @@ -1,36 +0,0 @@ -use web_sys::{ - js_sys::Reflect, - wasm_bindgen::{JsCast, JsValue}, - Performance, -}; - -const WASM_MISSING_GLOBAL_THIS_PERF: &str = "failed to find `globalThis.performance`"; -const WASM_UNABLE_TO_CAST_PERF: &str = - "Unable to cast `globalThis.performance` to Performance type"; -#[derive(Clone, Copy, Debug, Default)] -pub struct Monotonic { - _default: (), -} - -#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] -impl Monotonic { - pub fn now(&self) -> u64 { - let now = Reflect::get( - &web_sys::js_sys::global(), - &JsValue::from_str("performance"), - ) - .expect(WASM_MISSING_GLOBAL_THIS_PERF) - .dyn_ref::() - .expect(WASM_UNABLE_TO_CAST_PERF) - .now(); - // `performance.now()` returns the time in milliseconds. - return f64::trunc(now * 1_000_000.0) as u64; - } -} - -#[cfg(all(target_arch = "wasm32", target_os = "wasi"))] -impl Monotonic { - pub fn now(&self) -> u64 { - unsafe { wasi::clock_time_get(wasi::CLOCKID_MONOTONIC, 1).expect("failed to get time") } - } -} diff --git a/src/clocks/monotonic/wasm_browser.rs b/src/clocks/monotonic/wasm_browser.rs new file mode 100644 index 0000000..cacc1c2 --- /dev/null +++ b/src/clocks/monotonic/wasm_browser.rs @@ -0,0 +1,54 @@ +/// Since threads (WebWorkers) in browser don't shared the same memory space, so no objects would have a chance to be sended to other threads, thus it's safe to implement Send for objects here +use web_sys::{ + js_sys::Reflect, + wasm_bindgen::{JsCast, JsValue}, + Performance, +}; + +const WASM_MISSING_GLOBAL_THIS_PERF: &str = "failed to find `globalThis.performance`"; +const WASM_UNABLE_TO_CAST_PERF: &str = + "Unable to cast `globalThis.performance` to Performance type"; + +struct PerformanceClassWrapper(Performance); + +unsafe impl Send for PerformanceClassWrapper {} +unsafe impl Sync for PerformanceClassWrapper {} + +static GLOBAL_PERFORMANCE_INSTANCE: std::sync::OnceLock = + std::sync::OnceLock::new(); + +#[derive(Clone, Copy, Debug)] +pub struct Monotonic { + performance: &'static Performance, +} + +unsafe impl Send for Monotonic {} +unsafe impl Sync for Monotonic {} + +impl Default for Monotonic { + fn default() -> Self { + let performance = GLOBAL_PERFORMANCE_INSTANCE.get_or_init(|| { + PerformanceClassWrapper( + Reflect::get( + &web_sys::js_sys::global(), + &JsValue::from_str("performance"), + ) + .expect(WASM_MISSING_GLOBAL_THIS_PERF) + .dyn_into::() + .expect(WASM_UNABLE_TO_CAST_PERF), + ) + }); + + Self { + performance: &performance.0, + } + } +} + +impl Monotonic { + pub fn now(&self) -> u64 { + let now = self.performance.now(); + // `performance.now()` returns the time in milliseconds. + return f64::trunc(now * 1_000_000.0) as u64; + } +} diff --git a/src/clocks/monotonic/wasm_wasi.rs b/src/clocks/monotonic/wasm_wasi.rs new file mode 100644 index 0000000..f230f68 --- /dev/null +++ b/src/clocks/monotonic/wasm_wasi.rs @@ -0,0 +1,10 @@ +#[derive(Clone, Copy, Debug, Default)] +pub struct Monotonic { + _default: (), +} + +impl Monotonic { + pub fn now(&self) -> u64 { + unsafe { wasi::clock_time_get(wasi::CLOCKID_MONOTONIC, 1).expect("failed to get time") } + } +} From cf90e5a00b5ce8eff69ec9fdc91ffdcba67dd974 Mon Sep 17 00:00:00 2001 From: officeyutong Date: Thu, 5 Dec 2024 23:50:17 +0800 Subject: [PATCH 3/4] fix: use thread_local for performance instance --- src/clocks/monotonic/wasm_browser.rs | 43 +++++++++------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/src/clocks/monotonic/wasm_browser.rs b/src/clocks/monotonic/wasm_browser.rs index cacc1c2..4cea8c6 100644 --- a/src/clocks/monotonic/wasm_browser.rs +++ b/src/clocks/monotonic/wasm_browser.rs @@ -1,4 +1,5 @@ -/// Since threads (WebWorkers) in browser don't shared the same memory space, so no objects would have a chance to be sended to other threads, thus it's safe to implement Send for objects here +use std::cell::OnceCell; + use web_sys::{ js_sys::Reflect, wasm_bindgen::{JsCast, JsValue}, @@ -9,45 +10,29 @@ const WASM_MISSING_GLOBAL_THIS_PERF: &str = "failed to find `globalThis.performa const WASM_UNABLE_TO_CAST_PERF: &str = "Unable to cast `globalThis.performance` to Performance type"; -struct PerformanceClassWrapper(Performance); - -unsafe impl Send for PerformanceClassWrapper {} -unsafe impl Sync for PerformanceClassWrapper {} - -static GLOBAL_PERFORMANCE_INSTANCE: std::sync::OnceLock = - std::sync::OnceLock::new(); +thread_local! { + static GLOBAL_PERFORMANCE_INSTANCE: OnceCell = OnceCell::new(); +} -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Default)] pub struct Monotonic { - performance: &'static Performance, + _default: (), } -unsafe impl Send for Monotonic {} -unsafe impl Sync for Monotonic {} - -impl Default for Monotonic { - fn default() -> Self { - let performance = GLOBAL_PERFORMANCE_INSTANCE.get_or_init(|| { - PerformanceClassWrapper( +impl Monotonic { + pub fn now(&self) -> u64 { + let now = GLOBAL_PERFORMANCE_INSTANCE.with(|value| { + let performance_instance = value.get_or_init(|| { Reflect::get( &web_sys::js_sys::global(), &JsValue::from_str("performance"), ) .expect(WASM_MISSING_GLOBAL_THIS_PERF) .dyn_into::() - .expect(WASM_UNABLE_TO_CAST_PERF), - ) + .expect(WASM_UNABLE_TO_CAST_PERF) + }); + performance_instance.now() }); - - Self { - performance: &performance.0, - } - } -} - -impl Monotonic { - pub fn now(&self) -> u64 { - let now = self.performance.now(); // `performance.now()` returns the time in milliseconds. return f64::trunc(now * 1_000_000.0) as u64; } From 538a4bdf5fefcc72bbc2cf2978386cc2e329852f Mon Sep 17 00:00:00 2001 From: officeyutong Date: Fri, 6 Dec 2024 09:46:27 +0800 Subject: [PATCH 4/4] Make cargo clippy happy --- Cargo.toml | 2 +- src/clocks/monotonic/mod.rs | 1 - src/clocks/monotonic/wasm_browser.rs | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e38f090..a242cac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "quanta" version = "0.12.3" authors = ["Toby Lawrence "] edition = "2021" -rust-version = "1.60" +rust-version = "1.70" license = "MIT" diff --git a/src/clocks/monotonic/mod.rs b/src/clocks/monotonic/mod.rs index 4442224..80cbf89 100644 --- a/src/clocks/monotonic/mod.rs +++ b/src/clocks/monotonic/mod.rs @@ -13,7 +13,6 @@ mod wasm_wasi; #[cfg(all(target_arch = "wasm32", target_os = "wasi"))] pub use self::wasm_wasi::Monotonic; - #[cfg(not(any(target_os = "windows", target_arch = "wasm32")))] mod unix; #[cfg(not(any(target_os = "windows", target_arch = "wasm32")))] diff --git a/src/clocks/monotonic/wasm_browser.rs b/src/clocks/monotonic/wasm_browser.rs index 4cea8c6..b3f94a9 100644 --- a/src/clocks/monotonic/wasm_browser.rs +++ b/src/clocks/monotonic/wasm_browser.rs @@ -11,7 +11,7 @@ const WASM_UNABLE_TO_CAST_PERF: &str = "Unable to cast `globalThis.performance` to Performance type"; thread_local! { - static GLOBAL_PERFORMANCE_INSTANCE: OnceCell = OnceCell::new(); + static GLOBAL_PERFORMANCE_INSTANCE: OnceCell = const { OnceCell::new() }; } #[derive(Clone, Copy, Debug, Default)] @@ -34,6 +34,6 @@ impl Monotonic { performance_instance.now() }); // `performance.now()` returns the time in milliseconds. - return f64::trunc(now * 1_000_000.0) as u64; + f64::trunc(now * 1_000_000.0) as u64 } }