diff --git a/epaint/src/mutex.rs b/epaint/src/mutex.rs index 2d8e4cb2..bd2cef4a 100644 --- a/epaint/src/mutex.rs +++ b/epaint/src/mutex.rs @@ -7,201 +7,205 @@ compile_error!("Either feature \"single_threaded\" or \"multi_threaded\" must be // ---------------------------------------------------------------------------- -/// Provides interior mutability. Only thread-safe if the `multi_threaded` feature is enabled. -#[cfg(feature = "multi_threaded")] -#[derive(Default)] -pub struct Mutex(parking_lot::Mutex); - -/// The lock you get from [`Mutex`]. #[cfg(feature = "multi_threaded")] #[cfg(not(debug_assertions))] -pub use parking_lot::MutexGuard; +mod mutex_impl { + /// Provides interior mutability. Only thread-safe if the `multi_threaded` feature is enabled. + #[derive(Default)] + pub struct Mutex(parking_lot::Mutex); + + /// The lock you get from [`Mutex`]. + pub use parking_lot::MutexGuard; + + impl Mutex { + #[inline(always)] + pub fn new(val: T) -> Self { + Self(parking_lot::Mutex::new(val)) + } + + #[inline(always)] + pub fn lock(&self) -> MutexGuard<'_, T> { + self.0.lock() + } + } +} -/// The lock you get from [`Mutex`]. #[cfg(feature = "multi_threaded")] #[cfg(debug_assertions)] -pub struct MutexGuard<'a, T>(parking_lot::MutexGuard<'a, T>, *const ()); +mod mutex_impl { + /// Provides interior mutability. Only thread-safe if the `multi_threaded` feature is enabled. + #[derive(Default)] + pub struct Mutex(parking_lot::Mutex); -#[cfg(all(debug_assertions, feature = "multi_threaded"))] -#[derive(Default)] -struct HeldLocks(Vec<*const ()>); + /// The lock you get from [`Mutex`]. + pub struct MutexGuard<'a, T>(parking_lot::MutexGuard<'a, T>, *const ()); -#[cfg(all(debug_assertions, feature = "multi_threaded"))] -impl HeldLocks { - fn insert(&mut self, lock: *const ()) { - // Very few locks will ever be held at the same time, so a linear search is fast - assert!( - !self.0.contains(&lock), - "Recursively locking a Mutex in the same thread is not supported" - ); - self.0.push(lock); + #[derive(Default)] + struct HeldLocks(Vec<*const ()>); + + impl HeldLocks { + #[inline(always)] + fn insert(&mut self, lock: *const ()) { + // Very few locks will ever be held at the same time, so a linear search is fast + assert!( + !self.0.contains(&lock), + "Recursively locking a Mutex in the same thread is not supported" + ); + self.0.push(lock); + } + + #[inline(always)] + fn remove(&mut self, lock: *const ()) { + self.0.retain(|&ptr| ptr != lock); + } } - fn remove(&mut self, lock: *const ()) { - self.0.retain(|&ptr| ptr != lock); + thread_local! { + static HELD_LOCKS_TLS: std::cell::RefCell = Default::default(); + } + + impl Mutex { + #[inline(always)] + pub fn new(val: T) -> Self { + Self(parking_lot::Mutex::new(val)) + } + + pub fn lock(&self) -> MutexGuard<'_, T> { + // Detect if we are recursively taking out a lock on this mutex. + + // use a pointer to the inner data as an id for this lock + let ptr = (&self.0 as *const parking_lot::Mutex<_>).cast::<()>(); + + // Store it in thread local storage while we have a lock guard taken out + HELD_LOCKS_TLS.with(|held_locks| { + held_locks.borrow_mut().insert(ptr); + }); + + MutexGuard(self.0.lock(), ptr) + } + } + + impl Drop for MutexGuard<'_, T> { + fn drop(&mut self) { + let ptr = self.1; + HELD_LOCKS_TLS.with(|held_locks| { + held_locks.borrow_mut().remove(ptr); + }); + } + } + + impl std::ops::Deref for MutexGuard<'_, T> { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl std::ops::DerefMut for MutexGuard<'_, T> { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } } } -#[cfg(all(debug_assertions, feature = "multi_threaded"))] -thread_local! { - static HELD_LOCKS_TLS: std::cell::RefCell = Default::default(); -} - #[cfg(feature = "multi_threaded")] -impl Mutex { - #[inline(always)] - pub fn new(val: T) -> Self { - Self(parking_lot::Mutex::new(val)) - } +mod rw_lock_impl { + /// The lock you get from [`RwLock::read`]. + pub use parking_lot::RwLockReadGuard; - #[cfg(debug_assertions)] - pub fn lock(&self) -> MutexGuard<'_, T> { - // Detect if we are recursively taking out a lock on this mutex. + /// The lock you get from [`RwLock::write`]. + pub use parking_lot::RwLockWriteGuard; - // use a pointer to the inner data as an id for this lock - let ptr = (&self.0 as *const parking_lot::Mutex<_>).cast::<()>(); + /// Provides interior mutability. Only thread-safe if the `multi_threaded` feature is enabled. + #[derive(Default)] + pub struct RwLock(parking_lot::RwLock); - // Store it in thread local storage while we have a lock guard taken out - HELD_LOCKS_TLS.with(|held_locks| { - held_locks.borrow_mut().insert(ptr); - }); + impl RwLock { + #[inline(always)] + pub fn new(val: T) -> Self { + Self(parking_lot::RwLock::new(val)) + } - MutexGuard(self.0.lock(), ptr) - } + #[inline(always)] + pub fn read(&self) -> RwLockReadGuard<'_, T> { + self.0.read() + } - #[inline(always)] - #[cfg(not(debug_assertions))] - pub fn lock(&self) -> MutexGuard<'_, T> { - self.0.lock() - } -} - -#[cfg(debug_assertions)] -#[cfg(feature = "multi_threaded")] -impl Drop for MutexGuard<'_, T> { - fn drop(&mut self) { - let ptr = self.1; - HELD_LOCKS_TLS.with(|held_locks| { - held_locks.borrow_mut().remove(ptr); - }); - } -} - -#[cfg(debug_assertions)] -#[cfg(feature = "multi_threaded")] -impl std::ops::Deref for MutexGuard<'_, T> { - type Target = T; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -#[cfg(debug_assertions)] -#[cfg(feature = "multi_threaded")] -impl std::ops::DerefMut for MutexGuard<'_, T> { - #[inline(always)] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -// --------------------- - -/// The lock you get from [`RwLock::read`]. -#[cfg(feature = "multi_threaded")] -pub use parking_lot::RwLockReadGuard; - -/// The lock you get from [`RwLock::write`]. -#[cfg(feature = "multi_threaded")] -pub use parking_lot::RwLockWriteGuard; - -/// Provides interior mutability. Only thread-safe if the `multi_threaded` feature is enabled. -#[cfg(feature = "multi_threaded")] -#[derive(Default)] -pub struct RwLock(parking_lot::RwLock); - -#[cfg(feature = "multi_threaded")] -impl RwLock { - #[inline(always)] - pub fn new(val: T) -> Self { - Self(parking_lot::RwLock::new(val)) - } - - #[inline(always)] - pub fn read(&self) -> RwLockReadGuard<'_, T> { - self.0.read() - } - - #[inline(always)] - pub fn write(&self) -> RwLockWriteGuard<'_, T> { - self.0.write() + #[inline(always)] + pub fn write(&self) -> RwLockWriteGuard<'_, T> { + self.0.write() + } } } // ---------------------------------------------------------------------------- -// `atomic_refcell` will panic if multiple threads try to access the same value - -/// The lock you get from [`Mutex`]. -#[cfg(not(feature = "multi_threaded"))] -pub use atomic_refcell::AtomicRefMut as MutexGuard; - -/// Provides interior mutability. Only thread-safe if the `multi_threaded` feature is enabled. -#[cfg(not(feature = "multi_threaded"))] -#[derive(Default)] -pub struct Mutex(atomic_refcell::AtomicRefCell); #[cfg(not(feature = "multi_threaded"))] -impl Mutex { - #[inline(always)] - pub fn new(val: T) -> Self { - Self(atomic_refcell::AtomicRefCell::new(val)) - } +mod mutex_impl { + // `atomic_refcell` will panic if multiple threads try to access the same value - /// Panics if already locked. - #[inline(always)] - pub fn lock(&self) -> MutexGuard<'_, T> { - self.0.borrow_mut() + /// Provides interior mutability. Only thread-safe if the `multi_threaded` feature is enabled. + #[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() + } } } -// --------------------- - -/// The lock you get from [`RwLock::read`]. #[cfg(not(feature = "multi_threaded"))] -pub use atomic_refcell::AtomicRef as RwLockReadGuard; +mod rw_lock_impl { + // `atomic_refcell` will panic if multiple threads try to access the same value -/// The lock you get from [`RwLock::write`]. -#[cfg(not(feature = "multi_threaded"))] -pub use atomic_refcell::AtomicRefMut as RwLockWriteGuard; + /// The lock you get from [`RwLock::read`]. + pub use atomic_refcell::AtomicRef as RwLockReadGuard; -/// Provides interior mutability. Only thread-safe if the `multi_threaded` feature is enabled. -#[cfg(not(feature = "multi_threaded"))] -#[derive(Default)] -pub struct RwLock(atomic_refcell::AtomicRefCell); + /// The lock you get from [`RwLock::write`]. + pub use atomic_refcell::AtomicRefMut as RwLockWriteGuard; -#[cfg(not(feature = "multi_threaded"))] -impl RwLock { - #[inline(always)] - pub fn new(val: T) -> Self { - Self(atomic_refcell::AtomicRefCell::new(val)) - } + /// Provides interior mutability. Only thread-safe if the `multi_threaded` feature is enabled. + #[derive(Default)] + pub struct RwLock(atomic_refcell::AtomicRefCell); - #[inline(always)] - pub fn read(&self) -> RwLockReadGuard<'_, T> { - self.0.borrow() - } + impl RwLock { + #[inline(always)] + pub fn new(val: T) -> Self { + Self(atomic_refcell::AtomicRefCell::new(val)) + } - /// Panics if already locked. - #[inline(always)] - pub fn write(&self) -> RwLockWriteGuard<'_, T> { - self.0.borrow_mut() + #[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() + } } } // ---------------------------------------------------------------------------- +pub use mutex_impl::{Mutex, MutexGuard}; +pub use rw_lock_impl::{RwLock, RwLockReadGuard, RwLockWriteGuard}; + impl Clone for Mutex where T: Clone, @@ -211,6 +215,8 @@ where } } +// ---------------------------------------------------------------------------- + #[cfg(test)] mod tests { use crate::mutex::Mutex; diff --git a/sh/check.sh b/sh/check.sh index 873958ae..76140f0e 100755 --- a/sh/check.sh +++ b/sh/check.sh @@ -20,6 +20,9 @@ cargo doc -p egui_web --target wasm32-unknown-unknown --lib --no-deps --all-feat (cd emath && cargo check --no-default-features) (cd epaint && cargo check --no-default-features --features "single_threaded") +(cd epaint && cargo check --no-default-features --features "multi_threaded") +(cd epaint && cargo check --no-default-features --features "single_threaded" --release) +(cd epaint && cargo check --no-default-features --features "multi_threaded" --release) (cd egui && cargo check --no-default-features --features "multi_threaded") (cd eframe && cargo check --no-default-features --features "egui_glow") (cd epi && cargo check --no-default-features)