access_cell/lib.rs
1//! A cell that allows mutable access with re-entrancy support.
2//!
3//! When you call [`access`](AccessCell::access) with a closure that itself calls `access`
4//! on the same cell, those nested calls are queued and run after the current closure
5//! finishes, avoiding deadlock.
6
7use std::{
8 cell::{Cell, UnsafeCell},
9 collections::VecDeque,
10 sync::Mutex,
11};
12
13/// A cell holding a value of type `T` with re-entrant mutable access.
14///
15/// The first caller of [`access`](AccessCell::access) runs immediately; any further
16/// calls to `access` from within that closure (or its queue) are enqueued and
17/// executed in order after the current work completes.
18pub struct AccessCell<T> {
19 value: UnsafeCell<T>,
20 running: Cell<bool>,
21 queue: Mutex<VecDeque<Box<dyn FnOnce(&mut T)>>>,
22}
23
24impl<T> AccessCell<T> {
25 /// Creates a new `AccessCell` wrapping `value`.
26 pub fn new(value: T) -> Self {
27 Self {
28 value: UnsafeCell::new(value),
29 running: Cell::new(false),
30 queue: Mutex::new(VecDeque::new()),
31 }
32 }
33
34 /// Runs `f` with exclusive mutable access to the inner value.
35 ///
36 /// If called while another `access` closure is running (re-entrant call),
37 /// `f` is queued and run after the current closure and any other queued
38 /// closures finish.
39 pub fn access(&self, f: impl FnOnce(&mut T) + 'static) {
40 // already inside → enqueue
41 if self.running.get() {
42 self.queue.lock().unwrap().push_back(Box::new(f));
43
44 return;
45 }
46
47 // first entrant = executor
48 self.running.set(true);
49
50 // call current
51 f(self.access_mut());
52
53 // drain queued re-entrant calls
54 while let Some(job) = self.queue.lock().unwrap().pop_front() {
55 let value = self.access_mut();
56 job(value);
57 }
58
59 self.running.set(false);
60 }
61
62 /// Returns a mutable reference to the inner value.
63 ///
64 /// # Safety
65 /// Only call this while you are inside an `access` closure (or while draining
66 /// the queue). Otherwise you may create aliasing mutable references.
67 pub fn access_mut(&self) -> &mut T {
68 unsafe { &mut *self.value.get() }
69 }
70
71 /// Returns an immutable reference to the inner value.
72 ///
73 /// Safe to call at any time; no closure is running when you use this for
74 /// read-only access.
75 pub fn access_ref(&self) -> &T {
76 unsafe { &*self.value.get() }
77 }
78}
79
80mod tests {
81 #[test]
82 fn test_access() {
83 let value = std::sync::Arc::new(crate::AccessCell::new(0));
84
85 value.access({
86 let value = value.clone();
87
88 move |_| {
89 // normally this would cause a deadlock
90 value.access(|v| {
91 *v = 10;
92 })
93 }
94 });
95
96 assert_eq!(*value.access_ref(), 10);
97 }
98}