closeable_mutex/lib.rs
1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Provides a mutex that can be closed for long-term access.
5//!
6//! This is useful if you have an object that is in one of two states: a
7//! concurrent state, where it can be accessed by multiple users, and a
8//! non-concurrent state, where it can only be accessed by one user.
9//!
10//! In the non-concurrent state, you can close the mutex guarding the object so
11//! that it can be accessed freely without additional locking, allowing it to be
12//! used in `async` functions (for example). When the object is to reenter the
13//! concurrent state, you can open the mutex, allowing normal mutex operations.
14//!
15//! Something similar to this can be achieved with an ordinary mutex by holding
16//! the lock for the lifetime of the non-concurrent state, but this means that
17//! any other attempt to lock the mutex will hang for an indefinite period of
18//! time, possibly deadlocking. `try_lock` cannot be used to overcome this,
19//! because it would also fail while in the concurrent state with multiple
20//! concurrent accessors competing for the lock.
21
22// UNSAFETY: unsafe needed to implement interior mutability to locked values.
23#![expect(unsafe_code)]
24
25use parking_lot::Mutex;
26use parking_lot::MutexGuard;
27use std::cell::UnsafeCell;
28use std::mem::ManuallyDrop;
29use std::ops::Deref;
30use std::ops::DerefMut;
31use std::sync::Arc;
32
33/// A mutex that can be _closed_.
34///
35/// A closed mutex can be accessed freely by the owner, but while closed it
36/// cannot be locked by anyone else.
37pub struct CloseableMutex<T: ?Sized> {
38 mutex: Mutex<bool>,
39 value: UnsafeCell<T>,
40}
41
42// SAFETY: `mutex` ensures that there is only a single concurrent access to
43// `value`, providing `Sync` as long as `T` is `Send`.
44unsafe impl<T: ?Sized + Send> Sync for CloseableMutex<T> {}
45
46impl<T> CloseableMutex<T> {
47 /// Returns a new instance wrapping the given value.
48 pub fn new(value: T) -> Self {
49 Self {
50 mutex: Mutex::new(false),
51 value: value.into(),
52 }
53 }
54}
55
56impl<T: ?Sized> CloseableMutex<T> {
57 /// Closes the mutex, returning a guard that can be used to access the
58 /// underlying value.
59 ///
60 /// When the guard is dropped, the mutex is re-opened.
61 ///
62 /// While the mutex is closed, calls to `lock_if_open` will return `None`,
63 /// and calls to `lock` will panic.
64 pub fn close(self: Arc<Self>) -> ClosedGuard<T> {
65 {
66 let mut closed = self.mutex.lock();
67 assert!(!*closed, "object is already closed");
68 *closed = true;
69 }
70 ClosedGuard(ManuallyDrop::new(self))
71 }
72
73 /// If the lock is open, waits for it to become available and returns a
74 /// guard that can be used to access the underlying value.
75 ///
76 /// If the lock is closed, returns `None`.
77 pub fn lock_if_open(&self) -> Option<OpenGuard<'_, T>> {
78 let closed = self.mutex.lock();
79 if *closed {
80 return None;
81 }
82 MutexGuard::leak(closed);
83 Some(OpenGuard(self))
84 }
85
86 /// Waits for the lock to become available and returns a guard that can be
87 /// used to access the underlying value.
88 ///
89 /// # Panics
90 /// Panics if the lock is closed. To avoid this, use `lock_if_open`.
91 #[track_caller]
92 pub fn lock(&self) -> OpenGuard<'_, T> {
93 self.lock_if_open().expect("lock should not be closed")
94 }
95}
96
97/// A guard that can be used to access the underlying value of a
98/// [`CloseableMutex`].
99#[must_use]
100pub struct OpenGuard<'a, T: ?Sized>(&'a CloseableMutex<T>);
101
102impl<T: ?Sized> Drop for OpenGuard<'_, T> {
103 fn drop(&mut self) {
104 // SAFETY: the mutex is known to be locked.
105 unsafe {
106 self.0.mutex.force_unlock();
107 }
108 }
109}
110
111impl<T: ?Sized> Deref for OpenGuard<'_, T> {
112 type Target = T;
113
114 fn deref(&self) -> &Self::Target {
115 // SAFETY: the mutex is known to be locked.
116 unsafe { &*self.0.value.get() }
117 }
118}
119
120impl<T: ?Sized> DerefMut for OpenGuard<'_, T> {
121 fn deref_mut(&mut self) -> &mut Self::Target {
122 // SAFETY: the mutex is known to be locked.
123 unsafe { &mut *self.0.value.get() }
124 }
125}
126
127/// A guard that can be used to access the underlying value of a
128/// [`CloseableMutex`] while it is closed.
129///
130/// This wraps an [`Arc`] so that you can keep the mutex closed
131/// for an unbounded period without having to deal with a lifetime.
132// TODO: if this Arc-based functionality is not used or is otherwise
133// inconvenient, then replace or augment this with a standard
134// lifetime-based lock.
135#[must_use]
136pub struct ClosedGuard<T: ?Sized>(ManuallyDrop<Arc<CloseableMutex<T>>>);
137
138impl<T: ?Sized> Drop for ClosedGuard<T> {
139 fn drop(&mut self) {
140 // SAFETY: this has not been called yet
141 unsafe { self.release_ownership() };
142 }
143}
144
145impl<T: ?Sized> ClosedGuard<T> {
146 /// Opens the mutex, returning the inner instance.
147 pub fn open(mut self) -> Arc<CloseableMutex<T>> {
148 // SAFETY: this has not yet been called and will not be called again due
149 // to the `forget`.
150 let v = unsafe { self.release_ownership() };
151 std::mem::forget(self);
152 v
153 }
154
155 /// # Safety
156 ///
157 /// This must be called exactly once.
158 unsafe fn release_ownership(&mut self) -> Arc<CloseableMutex<T>> {
159 let was_owned = std::mem::replace(&mut *self.0.mutex.lock(), false);
160 assert!(was_owned);
161 // SAFETY: this is called exactly once.
162 unsafe { ManuallyDrop::take(&mut self.0) }
163 }
164}
165
166impl<T: ?Sized> Deref for ClosedGuard<T> {
167 type Target = T;
168
169 fn deref(&self) -> &Self::Target {
170 // SAFETY: the mutex is known to be closed.
171 unsafe { &*self.0.value.get() }
172 }
173}
174
175impl<T: ?Sized> DerefMut for ClosedGuard<T> {
176 fn deref_mut(&mut self) -> &mut Self::Target {
177 // SAFETY: the mutex is known to be closed.
178 unsafe { &mut *self.0.value.get() }
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use crate::CloseableMutex;
185 use std::sync::Arc;
186
187 #[test]
188 fn test_mutex() {
189 let x = Arc::new(CloseableMutex::new(0));
190 *x.lock() = 5;
191 *x.lock() = 6;
192 assert_eq!(*x.lock(), 6);
193
194 // Close the mutex, make sure locks are disallowed.
195 {
196 let mut c = x.clone().close();
197 *c = 7;
198 assert!(x.lock_if_open().is_none());
199 }
200
201 // Locks are allowed again.
202 assert_eq!(*x.lock_if_open().unwrap(), 7);
203 assert_eq!(*x.lock(), 7);
204 }
205
206 #[test]
207 #[should_panic]
208 fn test_closed_mutex_panics() {
209 let x = Arc::new(CloseableMutex::new(0));
210 let _c = x.clone().close();
211 let _ = x.lock();
212 }
213}