diff --git a/README.md b/README.md index df7afc5..c48a14a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,19 @@ # access-cell -A synchronous, single-threaded Mutex<T> anti-locking system +A synchronous, single-threaded Mutex anti-locking system + +```rust +let value = Arc::new(AccessCell::new(0)); + +value.access({ + let value = value.clone(); + + move |_| { + // normally this would cause a deadlock + value.access(|v| { + *v = 10; + }) + } +}); + +assert_eq!(*value.access_ref(), 10); +``` diff --git a/src/lib.rs b/src/lib.rs index b93cf3f..60938c1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,72 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right +use std::{ + cell::{Cell, UnsafeCell}, + collections::VecDeque, + sync::Mutex, +}; + +pub struct AccessCell { + value: UnsafeCell, + running: Cell, + queue: Mutex>>, } -#[cfg(test)] -mod tests { - use super::*; +impl AccessCell { + pub fn new(value: T) -> Self { + Self { + value: UnsafeCell::new(value), + running: Cell::new(false), + queue: Mutex::new(VecDeque::new()), + } + } + pub fn access(&self, f: impl FnOnce(&mut T) + 'static) { + // already inside → enqueue + if self.running.get() { + self.queue.lock().unwrap().push_back(Box::new(f)); + + return; + } + + // first entrant = executor + self.running.set(true); + + // call current + f(self.access_mut()); + + // drain queued re-entrant calls + while let Some(job) = self.queue.lock().unwrap().pop_front() { + let value = self.access_mut(); + job(value); + } + + self.running.set(false); + } + + pub fn access_mut(&self) -> &mut T { + unsafe { &mut *self.value.get() } + } + + pub fn access_ref(&self) -> &T { + unsafe { &*self.value.get() } + } +} + +mod tests { #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); + fn test_access() { + let value = std::sync::Arc::new(crate::AccessCell::new(0)); + + value.access({ + let value = value.clone(); + + move |_| { + // normally this would cause a deadlock + value.access(|v| { + *v = 10; + }) + } + }); + + assert_eq!(*value.access_ref(), 10); } }