Replace parking_lot with atomic_refcell

You can still opt-in to use parking_lot if you plan to use the same
egui::Context from multiple threads.
This commit is contained in:
Emil Ernerfeldt 2020-12-26 20:36:25 +01:00
parent 58fcf0f2a1
commit 6f5fd1b9c0
6 changed files with 104 additions and 21 deletions

11
Cargo.lock generated
View file

@ -57,6 +57,12 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407"
[[package]]
name = "atomic_refcell"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bc31dce067eab974c815a9deb95f6217806de7b53685d7fc31f8ccf3fb2539f"
[[package]]
name = "atty"
version = "0.2.14"
@ -579,6 +585,7 @@ name = "egui"
version = "0.5.0"
dependencies = [
"ahash",
"atomic_refcell",
"criterion",
"parking_lot",
"rusttype",
@ -618,7 +625,6 @@ version = "0.5.0"
dependencies = [
"egui",
"js-sys",
"parking_lot",
"serde",
"serde_json",
"wasm-bindgen",
@ -853,9 +859,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]

View file

@ -70,7 +70,7 @@ ui.label(format!("Hello '{}', age {}", name, age));
* Modular: You should be able to use small parts of Egui and combine them in new ways
* Safe: there is no `unsafe` code in Egui
* Minimal dependencies
* Egui uses [`rusttype`](https://crates.io/crates/rusttype) to render text and [`ahash`](https://crates.io/crates/ahash) + [`parking_lot`](https://crates.io/crates/parking_lot) for a speed boost.
* [`rusttype`](https://crates.io/crates/rusttype), [`atomic_refcell`](https://crates.io/crates/atomic_refcell) and [`ahash`](https://crates.io/crates/ahash).
Egui is *not* a framework. Egui is a library you call into, not an environment you program for.

View file

@ -20,7 +20,8 @@ include = [
[dependencies]
ahash = { version = "0.6", features = ["std"], default-features = false }
parking_lot = "0.11" # Using parking_lot over std::sync::Mutex gives 50% speedups in some real-world scenarios
atomic_refcell = { version = "0.1", optional = true } # Used instead of parking_lot when you are always using Egui in a single thread. About as fast as parking_lot.
parking_lot = { version = "0.11", optional = true } # Using parking_lot over std::sync::Mutex gives 50% speedups in some real-world scenarios
rusttype = "0.9"
serde = { version = "1", features = ["derive"], optional = true }
serde_json = { version = "1", optional = true }
@ -28,6 +29,10 @@ serde_json = { version = "1", optional = true }
[dev-dependencies]
criterion = { version = "0.3", default-features = false }
[features]
default = ["atomic_refcell"] # Using the same egui::Context from multiple threads will result in a panic.
multi_threaded = ["parking_lot"] # Only needed if you plan to use the same egui::Context from multiple threads.
[[bench]]
name = "benchmark"
harness = false

View file

@ -2,13 +2,12 @@ use std::sync::Arc;
use {
ahash::AHashMap,
parking_lot::RwLock,
rusttype::{point, Scale},
};
use crate::{
math::{vec2, Vec2},
mutex::Mutex,
mutex::{Mutex, RwLock},
paint::{Galley, Row},
};

View file

@ -1,12 +1,15 @@
//! Helper module for a Mutex that
//! detects double-locking on the same thread in debug mode.
//! Helper module that wraps some Mutex types with different implementations.
// TODO: feature-flag for `parking_lot` vs `std::sync::Mutex`.
// ----------------------------------------------------------------------------
#[cfg(feature = "multi_threaded")]
pub use parking_lot::MutexGuard;
#[cfg(feature = "multi_threaded")]
#[derive(Default)]
pub struct Mutex<T>(parking_lot::Mutex<T>);
#[cfg(feature = "multi_threaded")]
impl<T> Mutex<T> {
#[inline(always)]
pub fn new(val: T) -> Self {
@ -30,6 +33,89 @@ impl<T> Mutex<T> {
}
}
// ---------------------
#[cfg(feature = "multi_threaded")]
pub use parking_lot::{RwLockReadGuard, RwLockWriteGuard};
#[cfg(feature = "multi_threaded")]
#[derive(Default)]
pub struct RwLock<T>(parking_lot::RwLock<T>);
#[cfg(feature = "multi_threaded")]
impl<T> RwLock<T> {
#[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()
}
}
// ----------------------------------------------------------------------------
// `atomic_refcell` will panic if multiple threads try to access the same value
#[cfg(not(feature = "multi_threaded"))]
pub use atomic_refcell::AtomicRefMut as MutexGuard;
#[cfg(not(feature = "multi_threaded"))]
#[derive(Default)]
pub struct Mutex<T>(atomic_refcell::AtomicRefCell<T>);
#[cfg(not(feature = "multi_threaded"))]
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()
}
}
// ---------------------
#[cfg(not(feature = "multi_threaded"))]
pub use {
atomic_refcell::AtomicRef as RwLockReadGuard, atomic_refcell::AtomicRefMut as RwLockWriteGuard,
};
#[cfg(not(feature = "multi_threaded"))]
#[derive(Default)]
pub struct RwLock<T>(atomic_refcell::AtomicRefCell<T>);
#[cfg(not(feature = "multi_threaded"))]
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()
}
}
// ----------------------------------------------------------------------------
impl<T> Clone for Mutex<T>
where
T: Clone,
@ -38,12 +124,3 @@ where
Self::new(self.lock().clone())
}
}
// impl<T> PartialEq for Mutex<T>
// where
// T: PartialEq,
// {
// fn eq(&self, other: &Self) -> bool {
// std::ptr::eq(self, other) || self.lock().eq(&other.lock())
// }
// }

View file

@ -18,7 +18,6 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
egui = { version = "0.5.0", path = "../egui", features = ["serde"] }
js-sys = "0.3"
parking_lot = { version = "0.11", features = ["wasm-bindgen"] }
serde = "1"
serde_json = "1"
wasm-bindgen = "0.2"