From 84db2c93606b219b5e77fa17c7ae7b3971e33524 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 11 Aug 2023 14:54:34 +0200 Subject: [PATCH] Support multi-threaded Wasm Replace `atomic_refcell` with `parking_lot` on wasm32. `parking_lot` has had problems running on wasm32 before (https://github.com/emilk/egui/issues/1401) but it works these days. If we have problems again we can always switch to `std::sync::Mutex`. Closes https://github.com/emilk/egui/issues/3102 --- Cargo.lock | 7 --- crates/epaint/Cargo.toml | 6 +-- crates/epaint/src/mutex.rs | 104 +++++++------------------------------ 3 files changed, 19 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 37dd41bc6c3..d7e47d1c92a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -326,12 +326,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "atomic_refcell" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79d6dc922a2792b006573f60b2648076355daeae5ce9cb59507e5908c9625d31" - [[package]] name = "atspi" version = "0.10.1" @@ -1394,7 +1388,6 @@ version = "0.22.0" dependencies = [ "ab_glyph", "ahash 0.8.3", - "atomic_refcell", "backtrace", "bytemuck", "criterion", diff --git a/crates/epaint/Cargo.toml b/crates/epaint/Cargo.toml index 5a4c7424261..747c69b15dd 100644 --- a/crates/epaint/Cargo.toml +++ b/crates/epaint/Cargo.toml @@ -79,6 +79,7 @@ ahash = { version = "0.8.1", default-features = false, features = [ "std", ] } nohash-hasher = "0.2" +parking_lot = "0.12" # Using parking_lot over std::sync::Mutex gives 50% speedups in some real-world scenarios. #! ### Optional dependencies bytemuck = { version = "1.7.2", optional = true, features = ["derive"] } @@ -94,11 +95,6 @@ serde = { version = "1", optional = true, features = ["derive", "rc"] } # native: [target.'cfg(not(target_arch = "wasm32"))'.dependencies] backtrace = { version = "0.3", optional = true } -parking_lot = "0.12" # Using parking_lot over std::sync::Mutex gives 50% speedups in some real-world scenarios. - -# web: -[target.'cfg(target_arch = "wasm32")'.dependencies] -atomic_refcell = "0.1" # Used instead of parking_lot on on wasm. See https://github.com/emilk/egui/issues/1401 [dev-dependencies] diff --git a/crates/epaint/src/mutex.rs b/crates/epaint/src/mutex.rs index ce2bf7c931c..050fe9dfa3d 100644 --- a/crates/epaint/src/mutex.rs +++ b/crates/epaint/src/mutex.rs @@ -1,13 +1,14 @@ -//! Helper module that wraps some Mutex types with different implementations. +//! Helper module that adds extra checks when the `deadlock_detection` feature is turned on. // ---------------------------------------------------------------------------- -#[cfg(not(target_arch = "wasm32"))] -#[cfg(not(debug_assertions))] +#[cfg(not(feature = "deadlock_detection"))] mod mutex_impl { /// Provides interior mutability. /// - /// Uses `parking_lot` crate on native targets, and `atomic_refcell` on `wasm32` targets. + /// This is a thin wrapper around [`parking_lot::Mutex`], except if + /// the feature `deadlock_detection` is turned enabled, in which case + /// extra checks are added to detect deadlocks. #[derive(Default)] pub struct Mutex(parking_lot::Mutex); @@ -27,12 +28,13 @@ mod mutex_impl { } } -#[cfg(not(target_arch = "wasm32"))] -#[cfg(debug_assertions)] +#[cfg(feature = "deadlock_detection")] mod mutex_impl { /// Provides interior mutability. /// - /// Uses `parking_lot` crate on native targets, and `atomic_refcell` on `wasm32` targets. + /// This is a thin wrapper around [`parking_lot::Mutex`], except if + /// the feature `deadlock_detection` is turned enabled, in which case + /// extra checks are added to detect deadlocks. #[derive(Default)] pub struct Mutex(parking_lot::Mutex); @@ -115,7 +117,8 @@ mod mutex_impl { } } -#[cfg(not(target_arch = "wasm32"))] +// ---------------------------------------------------------------------------- + #[cfg(not(feature = "deadlock_detection"))] mod rw_lock_impl { /// The lock you get from [`RwLock::read`]. @@ -126,7 +129,9 @@ mod rw_lock_impl { /// Provides interior mutability. /// - /// Uses `parking_lot` crate on native targets, and `atomic_refcell` on `wasm32` targets. + /// This is a thin wrapper around [`parking_lot::RwLock`], except if + /// the feature `deadlock_detection` is turned enabled, in which case + /// extra checks are added to detect deadlocks. #[derive(Default)] pub struct RwLock(parking_lot::RwLock); @@ -148,7 +153,6 @@ mod rw_lock_impl { } } -#[cfg(not(target_arch = "wasm32"))] #[cfg(feature = "deadlock_detection")] mod rw_lock_impl { use std::{ @@ -246,7 +250,9 @@ mod rw_lock_impl { /// Provides interior mutability. /// - /// Uses `parking_lot` crate on native targets, and `atomic_refcell` on `wasm32` targets. + /// This is a thin wrapper around [`parking_lot::RwLock`], except if + /// the feature `deadlock_detection` is turned enabled, in which case + /// extra checks are added to detect deadlocks. #[derive(Default)] pub struct RwLock { lock: parking_lot::RwLock, @@ -352,80 +358,6 @@ mod rw_lock_impl { // ---------------------------------------------------------------------------- -#[cfg(target_arch = "wasm32")] -mod mutex_impl { - // `atomic_refcell` will panic if multiple threads try to access the same value - - /// Provides interior mutability. - /// - /// Uses `parking_lot` crate on native targets, and `atomic_refcell` on `wasm32` targets. - #[derive(Default)] - pub struct Mutex(atomic_refcell::AtomicRefCell); - - /// The lock you get from [`Mutex`]. - pub use atomic_refcell::AtomicRefMut as MutexGuard; - - impl Mutex { - #[inline(always)] - pub fn new(val: T) -> Self { - Self(atomic_refcell::AtomicRefCell::new(val)) - } - - /// Panics if already locked. - #[inline(always)] - pub fn lock(&self) -> MutexGuard<'_, T> { - self.0.borrow_mut() - } - - #[inline(always)] - pub fn into_inner(self) -> T { - self.0.into_inner() - } - } -} - -#[cfg(target_arch = "wasm32")] -mod rw_lock_impl { - // `atomic_refcell` will panic if multiple threads try to access the same value - - /// The lock you get from [`RwLock::read`]. - pub use atomic_refcell::AtomicRef as RwLockReadGuard; - - /// The lock you get from [`RwLock::write`]. - pub use atomic_refcell::AtomicRefMut as RwLockWriteGuard; - - /// Provides interior mutability. - /// - /// Uses `parking_lot` crate on native targets, and `atomic_refcell` on `wasm32` targets. - #[derive(Default)] - pub struct RwLock(atomic_refcell::AtomicRefCell); - - impl RwLock { - #[inline(always)] - pub fn new(val: T) -> Self { - Self(atomic_refcell::AtomicRefCell::new(val)) - } - - #[inline(always)] - pub fn read(&self) -> RwLockReadGuard<'_, T> { - self.0.borrow() - } - - /// Panics if already locked. - #[inline(always)] - pub fn write(&self) -> RwLockWriteGuard<'_, T> { - self.0.borrow_mut() - } - - #[inline(always)] - pub fn into_inner(self) -> T { - self.0.into_inner() - } - } -} - -// ---------------------------------------------------------------------------- - pub use mutex_impl::{Mutex, MutexGuard}; pub use rw_lock_impl::{RwLock, RwLockReadGuard, RwLockWriteGuard}; @@ -469,7 +401,7 @@ mod tests { let other_thread = { let one = Arc::clone(&one); std::thread::spawn(move || { - let _ = one.lock(); + let _lock = one.lock(); }) }; std::thread::sleep(Duration::from_millis(200));