Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support multi-threaded Wasm #3236

Merged
merged 1 commit into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 1 addition & 5 deletions crates/epaint/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand All @@ -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]
Expand Down
104 changes: 18 additions & 86 deletions crates/epaint/src/mutex.rs
Original file line number Diff line number Diff line change
@@ -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>);

Expand All @@ -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>);

Expand Down Expand Up @@ -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`].
Expand All @@ -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>);

Expand All @@ -148,7 +153,6 @@ mod rw_lock_impl {
}
}

#[cfg(not(target_arch = "wasm32"))]
#[cfg(feature = "deadlock_detection")]
mod rw_lock_impl {
use std::{
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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};

Expand Down Expand Up @@ -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));
Expand Down
Loading