Support multi-threaded Wasm (#3236)
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
This commit is contained in:
parent
08fb447fb5
commit
dd5285cccb
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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<T>(parking_lot::Mutex<T>);
|
||||
|
||||
|
|
@ -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<T>(parking_lot::Mutex<T>);
|
||||
|
||||
|
|
@ -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<T>(parking_lot::RwLock<T>);
|
||||
|
||||
|
|
@ -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<T> {
|
||||
lock: parking_lot::RwLock<T>,
|
||||
|
|
@ -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<T>(atomic_refcell::AtomicRefCell<T>);
|
||||
|
||||
/// The lock you get from [`Mutex`].
|
||||
pub use atomic_refcell::AtomicRefMut as MutexGuard;
|
||||
|
||||
impl<T> Mutex<T> {
|
||||
#[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<T>(atomic_refcell::AtomicRefCell<T>);
|
||||
|
||||
impl<T> RwLock<T> {
|
||||
#[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));
|
||||
|
|
|
|||
Loading…
Reference in New Issue