mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2025-09-12 04:32:58 +02:00
Compare commits
No commits in common. "f36028b869f95b1a5b8b4afdf0310b205c40fa4b" and "6cd132e207a2bdfed30753c8083812d620b13c30" have entirely different histories.
f36028b869
...
6cd132e207
4 changed files with 26 additions and 179 deletions
2
.envrc
2
.envrc
|
@ -2,6 +2,6 @@
|
||||||
|
|
||||||
dotenv_if_exists
|
dotenv_if_exists
|
||||||
|
|
||||||
# use flake ".#${DIRENV_DEVSHELL:-default}"
|
use flake ".#${DIRENV_DEVSHELL:-default}"
|
||||||
|
|
||||||
PATH_add bin
|
PATH_add bin
|
||||||
|
|
29
Cargo.lock
generated
29
Cargo.lock
generated
|
@ -1659,12 +1659,6 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fixedbitset"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
|
@ -3226,13 +3220,10 @@ version = "0.9.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"petgraph",
|
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thread-id",
|
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -3282,16 +3273,6 @@ version = "2.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
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]]
|
[[package]]
|
||||||
name = "phf"
|
name = "phf"
|
||||||
version = "0.11.3"
|
version = "0.11.3"
|
||||||
|
@ -4913,16 +4894,6 @@ dependencies = [
|
||||||
"syn",
|
"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]]
|
[[package]]
|
||||||
name = "thread_local"
|
name = "thread_local"
|
||||||
version = "1.1.9"
|
version = "1.1.9"
|
||||||
|
|
|
@ -517,7 +517,6 @@ version = "1.0"
|
||||||
|
|
||||||
[workspace.dependencies.parking_lot]
|
[workspace.dependencies.parking_lot]
|
||||||
version = "0.12.4"
|
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
|
# Use this when extending with_lock::WithLock to parking_lot
|
||||||
[workspace.dependencies.lock_api]
|
[workspace.dependencies.lock_api]
|
||||||
|
|
|
@ -1,212 +1,89 @@
|
||||||
//! Traits for explicitly scoping the lifetime of locks.
|
//! Traits for explicitly scoping the lifetime of locks.
|
||||||
|
|
||||||
use std::{
|
use std::sync::{Arc, Mutex};
|
||||||
future::Future,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub trait WithLock<T: ?Sized> {
|
pub trait WithLock<T: ?Sized> {
|
||||||
/// Acquires a lock and executes the given closure with the locked data,
|
/// Acquires a lock and executes the given closure with the locked data.
|
||||||
/// returning the result.
|
fn with_lock<F>(&self, f: F)
|
||||||
fn with_lock<R, F>(&self, f: F) -> R
|
|
||||||
where
|
where
|
||||||
F: FnMut(&mut T) -> R;
|
F: FnMut(&mut T);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> WithLock<T> for Mutex<T> {
|
impl<T> WithLock<T> for Mutex<T> {
|
||||||
fn with_lock<R, F>(&self, mut f: F) -> R
|
fn with_lock<F>(&self, mut f: F)
|
||||||
where
|
where
|
||||||
F: FnMut(&mut T) -> R,
|
F: FnMut(&mut T),
|
||||||
{
|
{
|
||||||
// The locking and unlocking logic is hidden inside this function.
|
// The locking and unlocking logic is hidden inside this function.
|
||||||
let mut data_guard = self.lock().unwrap();
|
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.
|
// Lock is released here when `data_guard` goes out of scope.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> WithLock<T> for Arc<Mutex<T>> {
|
impl<T> WithLock<T> for Arc<Mutex<T>> {
|
||||||
fn with_lock<R, F>(&self, mut f: F) -> R
|
fn with_lock<F>(&self, mut f: F)
|
||||||
where
|
where
|
||||||
F: FnMut(&mut T) -> R,
|
F: FnMut(&mut T),
|
||||||
{
|
{
|
||||||
// The locking and unlocking logic is hidden inside this function.
|
// The locking and unlocking logic is hidden inside this function.
|
||||||
let mut data_guard = self.lock().unwrap();
|
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.
|
// Lock is released here when `data_guard` goes out of scope.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: lock_api::RawMutex, T: ?Sized> WithLock<T> for lock_api::Mutex<R, T> {
|
impl<R: lock_api::RawMutex, T: ?Sized> WithLock<T> for lock_api::Mutex<R, T> {
|
||||||
fn with_lock<Ret, F>(&self, mut f: F) -> Ret
|
fn with_lock<F>(&self, mut f: F)
|
||||||
where
|
where
|
||||||
F: FnMut(&mut T) -> Ret,
|
F: FnMut(&mut T),
|
||||||
{
|
{
|
||||||
// The locking and unlocking logic is hidden inside this function.
|
// The locking and unlocking logic is hidden inside this function.
|
||||||
let mut data_guard = self.lock();
|
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.
|
// Lock is released here when `data_guard` goes out of scope.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: lock_api::RawMutex, T: ?Sized> WithLock<T> for Arc<lock_api::Mutex<R, T>> {
|
impl<R: lock_api::RawMutex, T: ?Sized> WithLock<T> for Arc<lock_api::Mutex<R, T>> {
|
||||||
fn with_lock<Ret, F>(&self, mut f: F) -> Ret
|
fn with_lock<F>(&self, mut f: F)
|
||||||
where
|
where
|
||||||
F: FnMut(&mut T) -> Ret,
|
F: FnMut(&mut T),
|
||||||
{
|
{
|
||||||
// The locking and unlocking logic is hidden inside this function.
|
// The locking and unlocking logic is hidden inside this function.
|
||||||
let mut data_guard = self.lock();
|
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.
|
// Lock is released here when `data_guard` goes out of scope.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait WithLockAsync<T> {
|
pub trait WithLockAsync<T> {
|
||||||
/// Acquires a lock and executes the given closure with the locked data,
|
/// Acquires a lock and executes the given closure with the locked data.
|
||||||
/// returning the result.
|
fn with_lock<F>(&self, f: F) -> impl Future<Output = ()>
|
||||||
fn with_lock<R, F>(&self, f: F) -> impl Future<Output = R>
|
|
||||||
where
|
where
|
||||||
F: FnMut(&mut T) -> R;
|
F: FnMut(&mut T);
|
||||||
|
|
||||||
/// Acquires a lock and executes the given async closure with the locked
|
|
||||||
/// data.
|
|
||||||
fn with_lock_async<R, F>(&self, f: F) -> impl std::future::Future<Output = R>
|
|
||||||
where
|
|
||||||
F: AsyncFnMut(&mut T) -> R;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> WithLockAsync<T> for futures::lock::Mutex<T> {
|
impl<T> WithLockAsync<T> for futures::lock::Mutex<T> {
|
||||||
async fn with_lock<R, F>(&self, mut f: F) -> R
|
async fn with_lock<F>(&self, mut f: F)
|
||||||
where
|
where
|
||||||
F: FnMut(&mut T) -> R,
|
F: FnMut(&mut T),
|
||||||
{
|
{
|
||||||
// The locking and unlocking logic is hidden inside this function.
|
// The locking and unlocking logic is hidden inside this function.
|
||||||
let mut data_guard = self.lock().await;
|
let mut data_guard = self.lock().await;
|
||||||
f(&mut data_guard)
|
f(&mut data_guard);
|
||||||
// Lock is released here when `data_guard` goes out of scope.
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn with_lock_async<R, F>(&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
|
|
||||||
// Lock is released here when `data_guard` goes out of scope.
|
// Lock is released here when `data_guard` goes out of scope.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> WithLockAsync<T> for Arc<futures::lock::Mutex<T>> {
|
impl<T> WithLockAsync<T> for Arc<futures::lock::Mutex<T>> {
|
||||||
async fn with_lock<R, F>(&self, mut f: F) -> R
|
async fn with_lock<F>(&self, mut f: F)
|
||||||
where
|
where
|
||||||
F: FnMut(&mut T) -> R,
|
F: FnMut(&mut T),
|
||||||
{
|
{
|
||||||
// The locking and unlocking logic is hidden inside this function.
|
// The locking and unlocking logic is hidden inside this function.
|
||||||
let mut data_guard = self.lock().await;
|
let mut data_guard = self.lock().await;
|
||||||
f(&mut data_guard)
|
f(&mut data_guard);
|
||||||
// Lock is released here when `data_guard` goes out of scope.
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn with_lock_async<R, F>(&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
|
|
||||||
// Lock is released here when `data_guard` goes out of scope.
|
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue