diff --git a/.envrc b/.envrc index bad73b75..952ec2f8 100644 --- a/.envrc +++ b/.envrc @@ -2,6 +2,6 @@ dotenv_if_exists -# use flake ".#${DIRENV_DEVSHELL:-default}" +use flake ".#${DIRENV_DEVSHELL:-default}" PATH_add bin diff --git a/Cargo.lock b/Cargo.lock index 5dce9c59..ed9be6d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1659,12 +1659,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - [[package]] name = "flate2" version = "1.1.2" @@ -3226,13 +3220,10 @@ version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ - "backtrace", "cfg-if", "libc", - "petgraph", "redox_syscall", "smallvec", - "thread-id", "windows-targets 0.52.6", ] @@ -3282,16 +3273,6 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "petgraph" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" -dependencies = [ - "fixedbitset", - "indexmap 2.9.0", -] - [[package]] name = "phf" version = "0.11.3" @@ -4913,16 +4894,6 @@ dependencies = [ "syn", ] -[[package]] -name = "thread-id" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe8f25bbdd100db7e1d34acf7fd2dc59c4bf8f7483f505eaa7d4f12f76cc0ea" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "thread_local" version = "1.1.9" diff --git a/Cargo.toml b/Cargo.toml index ab6a9e8a..54f7ae82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -517,7 +517,6 @@ version = "1.0" [workspace.dependencies.parking_lot] version = "0.12.4" -features = ["hardware-lock-elision", "deadlock_detection"] # TODO: Check if deadlock_detection has a perf impact, if it does only enable with debug_assertions # Use this when extending with_lock::WithLock to parking_lot [workspace.dependencies.lock_api] diff --git a/src/core/utils/with_lock.rs b/src/core/utils/with_lock.rs index 91e8e8d1..914749de 100644 --- a/src/core/utils/with_lock.rs +++ b/src/core/utils/with_lock.rs @@ -1,212 +1,89 @@ //! Traits for explicitly scoping the lifetime of locks. -use std::{ - future::Future, - sync::{Arc, Mutex}, -}; +use std::sync::{Arc, Mutex}; pub trait WithLock { - /// Acquires a lock and executes the given closure with the locked data, - /// returning the result. - fn with_lock(&self, f: F) -> R + /// Acquires a lock and executes the given closure with the locked data. + fn with_lock(&self, f: F) where - F: FnMut(&mut T) -> R; + F: FnMut(&mut T); } impl WithLock for Mutex { - fn with_lock(&self, mut f: F) -> R + fn with_lock(&self, mut f: F) where - F: FnMut(&mut T) -> R, + F: FnMut(&mut T), { // The locking and unlocking logic is hidden inside this function. let mut data_guard = self.lock().unwrap(); - f(&mut data_guard) + f(&mut data_guard); // Lock is released here when `data_guard` goes out of scope. } } impl WithLock for Arc> { - fn with_lock(&self, mut f: F) -> R + fn with_lock(&self, mut f: F) where - F: FnMut(&mut T) -> R, + F: FnMut(&mut T), { // The locking and unlocking logic is hidden inside this function. let mut data_guard = self.lock().unwrap(); - f(&mut data_guard) + f(&mut data_guard); // Lock is released here when `data_guard` goes out of scope. } } impl WithLock for lock_api::Mutex { - fn with_lock(&self, mut f: F) -> Ret + fn with_lock(&self, mut f: F) where - F: FnMut(&mut T) -> Ret, + F: FnMut(&mut T), { // The locking and unlocking logic is hidden inside this function. let mut data_guard = self.lock(); - f(&mut data_guard) + f(&mut data_guard); // Lock is released here when `data_guard` goes out of scope. } } impl WithLock for Arc> { - fn with_lock(&self, mut f: F) -> Ret + fn with_lock(&self, mut f: F) where - F: FnMut(&mut T) -> Ret, + F: FnMut(&mut T), { // The locking and unlocking logic is hidden inside this function. let mut data_guard = self.lock(); - f(&mut data_guard) + f(&mut data_guard); // Lock is released here when `data_guard` goes out of scope. } } pub trait WithLockAsync { - /// Acquires a lock and executes the given closure with the locked data, - /// returning the result. - fn with_lock(&self, f: F) -> impl Future + /// Acquires a lock and executes the given closure with the locked data. + fn with_lock(&self, f: F) -> impl Future where - F: FnMut(&mut T) -> R; - - /// Acquires a lock and executes the given async closure with the locked - /// data. - fn with_lock_async(&self, f: F) -> impl std::future::Future - where - F: AsyncFnMut(&mut T) -> R; + F: FnMut(&mut T); } impl WithLockAsync for futures::lock::Mutex { - async fn with_lock(&self, mut f: F) -> R + async fn with_lock(&self, mut f: F) where - F: FnMut(&mut T) -> R, + F: FnMut(&mut T), { // The locking and unlocking logic is hidden inside this function. let mut data_guard = self.lock().await; - f(&mut data_guard) - // Lock is released here when `data_guard` goes out of scope. - } - - async fn with_lock_async(&self, mut f: F) -> R - where - F: AsyncFnMut(&mut T) -> R, - { - // The locking and unlocking logic is hidden inside this function. - let mut data_guard = self.lock().await; - f(&mut data_guard).await + f(&mut data_guard); // Lock is released here when `data_guard` goes out of scope. } } impl WithLockAsync for Arc> { - async fn with_lock(&self, mut f: F) -> R + async fn with_lock(&self, mut f: F) where - F: FnMut(&mut T) -> R, + F: FnMut(&mut T), { // The locking and unlocking logic is hidden inside this function. let mut data_guard = self.lock().await; - f(&mut data_guard) - // Lock is released here when `data_guard` goes out of scope. - } - - async fn with_lock_async(&self, mut f: F) -> R - where - F: AsyncFnMut(&mut T) -> R, - { - // The locking and unlocking logic is hidden inside this function. - let mut data_guard = self.lock().await; - f(&mut data_guard).await + f(&mut data_guard); // Lock is released here when `data_guard` goes out of scope. } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_with_lock_return_value() { - let mutex = Mutex::new(5); - let result = mutex.with_lock(|v| { - *v += 1; - *v * 2 - }); - assert_eq!(result, 12); - let value = mutex.lock().unwrap(); - assert_eq!(*value, 6); - } - - #[test] - fn test_with_lock_unit_return() { - let mutex = Mutex::new(10); - mutex.with_lock(|v| { - *v += 2; - }); - let value = mutex.lock().unwrap(); - assert_eq!(*value, 12); - } - - #[test] - fn test_with_lock_arc_mutex() { - let mutex = Arc::new(Mutex::new(1)); - let result = mutex.with_lock(|v| { - *v *= 10; - *v - }); - assert_eq!(result, 10); - assert_eq!(*mutex.lock().unwrap(), 10); - } - - #[tokio::test] - async fn test_with_lock_async_return_value() { - use futures::lock::Mutex as AsyncMutex; - let mutex = AsyncMutex::new(7); - let result = mutex - .with_lock(|v| { - *v += 3; - *v * 2 - }) - .await; - assert_eq!(result, 20); - let value = mutex.lock().await; - assert_eq!(*value, 10); - } - - #[tokio::test] - async fn test_with_lock_async_unit_return() { - use futures::lock::Mutex as AsyncMutex; - let mutex = AsyncMutex::new(100); - mutex - .with_lock(|v| { - *v -= 50; - }) - .await; - let value = mutex.lock().await; - assert_eq!(*value, 50); - } - - #[tokio::test] - async fn test_with_lock_async_closure() { - use futures::lock::Mutex as AsyncMutex; - let mutex = AsyncMutex::new(1); - mutex - .with_lock_async(async |v| { - *v += 9; - }) - .await; - let value = mutex.lock().await; - assert_eq!(*value, 10); - } - - #[tokio::test] - async fn test_with_lock_async_arc_mutex() { - use futures::lock::Mutex as AsyncMutex; - let mutex = Arc::new(AsyncMutex::new(2)); - mutex - .with_lock_async(async |v: &mut i32| { - *v *= 5; - }) - .await; - let value = mutex.lock().await; - assert_eq!(*value, 10); - } -}