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}