with_lock/
lib.rs

1//! A [`Mutex`](`std::sync::Mutex`) is prone to deadlocks when being used in sync and async code.
2//! There are various causes of deadlocks, primarily due to not dropping the lock correctly.
3//! This crate provides a function that locks for you, and automatically drops the lock. This significantly reduces the risk of deadlocks.
4//!
5//! With this crate, you would convert `.lock()` to simply be `.with_lock(|s| *s)`.
6//!
7//! # Features
8//! - Simple API.
9//! - Provides a Cell like struct powered by a Mutex: [`MutexCell`](struct.MutexCell.html).
10//!
11//! # Caveats
12//! If you manage to find a deadlock, please report it [here](https://github.com/Milo123459/with_lock/issues).
13//!
14//! This snippet would deadlock: `s.with_Lock(|test| s.with_lock(|test2| test2))`
15
16use parking_lot::{const_mutex, Mutex};
17use std::mem;
18use std::ptr;
19
20pub struct WithLock<T> {
21	pub(crate) data: Mutex<T>,
22}
23
24impl<T> WithLock<T> {
25	/// Returns what `Mutex.lock()` would.
26	///
27	/// If you clone the value inside the function provided, everything touching the value will have to be inside the function.
28	pub fn with_lock<F, U>(&self, function: F) -> U
29	where
30		F: FnOnce(&mut T) -> U,
31	{
32		let mut lock = self.data.lock();
33		function(&mut *lock)
34	}
35
36	/// Create a new `WithLock` instance.
37	/// ## Examples
38	/// ```rust
39	/// use with_lock::WithLock;
40	/// WithLock::<i64>::new(123);
41	/// ```
42	pub fn new<F>(data: F) -> WithLock<F> {
43		WithLock {
44			data: const_mutex(data),
45		}
46	}
47}
48
49pub struct MutexCell<T> {
50	pub(crate) data: WithLock<T>,
51}
52
53impl<T> MutexCell<T> {
54	/// Create a new MutexCell with a value.
55	/// ## Example
56	/// ```rust
57	/// use with_lock::MutexCell;
58	/// let mutex = MutexCell::new(23);
59	/// assert_eq!(mutex.get(), 23)
60	/// ```
61	pub fn new(data: T) -> MutexCell<T> {
62		MutexCell {
63			data: WithLock::<T>::new(data),
64		}
65	}
66
67	/// Returns a copy of the contained value.
68	pub fn get(&self) -> T
69	where
70		T: Copy,
71	{
72		self.data.with_lock(|s| *s)
73	}
74
75	/// Returns a mutable reference to the underlying data.
76	pub fn get_mut(&mut self) -> &mut T
77	where
78		T: Copy,
79	{
80		self.data.data.get_mut()
81	}
82
83	/// Sets the contained value.
84	pub fn set(&self, data: T) {
85		self.data.with_lock(|s| *s = data);
86	}
87
88	/// Replaces the contained value with `val`, and returns the old contained value.
89	pub fn replace(&self, val: T) -> T {
90		self.data.with_lock(|old| mem::replace(old, val))
91	}
92
93	/// Swaps the values of two `MutexCell`s.
94	pub fn swap(&self, new: &MutexCell<T>) {
95		if ptr::eq(self, new) {
96			return;
97		}
98		self.data
99			.with_lock(|a| new.data.with_lock(|b| mem::swap(a, b)))
100	}
101
102	/// Takes the value of the cell, leaving `Default::default()` in its place.
103	pub fn take(&self) -> T
104	where
105		T: Default,
106	{
107		self.replace(T::default())
108	}
109
110	/// Unwraps the value.
111	pub fn into_inner(self) -> T {
112		self.data.data.into_inner()
113	}
114}
115
116#[cfg(doctest)]
117#[doc = include_str!("../README.md")]
118mod readme {}
119
120#[cfg(test)]
121mod tests {
122	use crate::*;
123
124	struct SharedData {
125		pub a: i64,
126		pub b: i64,
127	}
128
129	#[test]
130	fn test_with_lock() {
131		let a = WithLock::<i64>::new(2);
132		let b = WithLock::<i64>::new(3);
133
134		let action_and_get = |s: &mut i64| *s;
135		let a_lock = a.with_lock(action_and_get);
136		let b_lock = b.with_lock(action_and_get);
137		assert_eq!(a_lock + b_lock, 5);
138
139		// repeat action with embedded lambda expression
140		let a_lock_2 = a.with_lock(|s| *s);
141		let b_lock_2 = b.with_lock(|s| *s);
142		assert_eq!(a_lock_2 + b_lock_2, 5);
143	}
144
145	#[test]
146	fn test_with_lock_over_struct() {
147		let a = WithLock::<SharedData>::new(SharedData { a: 2, b: 2 });
148		let b = WithLock::<SharedData>::new(SharedData { a: 3, b: 3 });
149
150		let action_and_get = |s: &mut SharedData| (*s).a;
151		let a_lock = a.with_lock(action_and_get);
152		let b_lock = b.with_lock(action_and_get);
153		assert_eq!(a_lock + b_lock, 5);
154
155		// repeat action with embedded lambda expression and member b (avoid dead code warning)
156		let a_lock_2 = a.with_lock(|s| (*s).b);
157		let b_lock_2 = b.with_lock(|s| (*s).b);
158		assert_eq!(a_lock_2 + b_lock_2, 5);
159	}
160
161	#[test]
162	fn test_mutex_cell_no_deadlocks() {
163		let a = MutexCell::new(2);
164		let b = MutexCell::new(3);
165		let a_lock = a.get();
166		let b_lock = b.get();
167		assert_eq!(a_lock + b_lock, 5);
168		let a_lock_2 = a.get();
169		let b_lock_2 = b.get();
170		assert_eq!(a_lock_2 + b_lock_2, 5);
171	}
172
173	#[test]
174	fn test_mutex_cell_mutability() {
175		let cell = MutexCell::new(3);
176		assert_eq!(cell.get(), 3);
177		cell.set(4);
178		assert_eq!(cell.get(), 4);
179	}
180
181	#[test]
182	fn test_mutex_cell_replace() {
183		let cell = MutexCell::new(3);
184		assert_eq!(cell.replace(4), 3);
185		assert_eq!(cell.get(), 4);
186	}
187
188	#[test]
189	fn test_mutex_cell_swap() {
190		let c1 = MutexCell::new(5);
191		let c2 = MutexCell::new(10);
192		c1.swap(&c2);
193		assert_eq!(10, c1.get());
194		assert_eq!(5, c2.get());
195	}
196
197	#[test]
198	fn test_mutex_cell_swap_doesnt_deadlock() {
199		let c1 = MutexCell::new(5);
200		assert_eq!(c1.get(), 5);
201		c1.swap(&c1);
202		assert_eq!(c1.get(), 5);
203	}
204
205	#[test]
206	fn test_mutex_cell_get_mut() {
207		let mut c1 = MutexCell::new(5);
208		assert_eq!(c1.get(), 5);
209		let c2 = c1.get_mut();
210		*c2 += 1;
211		assert_eq!(c1.get(), 6);
212	}
213
214	#[test]
215	fn test_mutex_cell_take() {
216		let c = MutexCell::new(5);
217		let five = c.take();
218
219		assert_eq!(five, 5);
220		assert_eq!(c.into_inner(), 0);
221	}
222
223	#[test]
224	fn test_mutex_cell_into_inner() {
225		let c = MutexCell::new(5);
226		let five = c.into_inner();
227
228		assert_eq!(five, 5);
229	}
230}