lockable/lockpool.rs
1use std::hash::Hash;
2
3use super::limit::{AsyncLimit, SyncLimit};
4use super::lockable_hash_map::LockableHashMap;
5use super::lockable_trait::Lockable;
6use super::utils::never::InfallibleUnwrap;
7
8/// A pool of locks where individual locks can be locked/unlocked by key.
9/// It initially considers all keys as "unlocked", but they can be locked
10/// and if a second thread tries to acquire a lock for the same key, they will have to wait.
11///
12/// ```
13/// use lockable::LockPool;
14///
15/// let pool = LockPool::new();
16/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
17/// let guard1 = pool.async_lock(4).await;
18/// let guard2 = pool.async_lock(5).await;
19///
20/// // This next line would cause a deadlock or panic because `4` is already locked on this thread
21/// // let guard3 = pool.async_lock(4).await;
22///
23/// // After dropping the corresponding guard, we can lock it again
24/// std::mem::drop(guard1);
25/// let guard3 = pool.async_lock(4).await;
26/// # Ok::<(), lockable::Never>(())}).unwrap();
27/// ```
28///
29/// You can use an arbitrary type to index locks by, as long as that type implements [PartialEq] + [Eq] + [Hash] + [Clone] + [Debug].
30///
31/// ```
32/// use lockable::LockPool;
33///
34/// #[derive(PartialEq, Eq, Hash, Clone, Debug)]
35/// struct CustomLockKey(u32);
36///
37/// let pool = LockPool::new();
38/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
39/// let guard = pool.async_lock(CustomLockKey(4)).await;
40/// # Ok::<(), lockable::Never>(())}).unwrap();
41/// ```
42///
43/// Under the hood, a [LockPool] is a [LockableHashMap] with `()` as a value type, i.e. `LockPool<K>` is just a wrapper
44/// around `LockableHashMap<K, ()>` with a simpler API. If you need more complex functionalities, please look at
45/// [LockableHashMap].
46pub struct LockPool<K>
47where
48 K: Eq + PartialEq + Hash + Clone,
49{
50 map: LockableHashMap<K, ()>,
51}
52
53impl<K> Lockable<K, ()> for LockPool<K>
54where
55 K: Eq + PartialEq + Hash + Clone,
56{
57 type Guard<'a>
58 = <LockableHashMap<K, ()> as Lockable<K, ()>>::Guard<'a>
59 where
60 K: 'a;
61
62 type OwnedGuard = <LockableHashMap<K, ()> as Lockable<K, ()>>::OwnedGuard;
63
64 type SyncLimit<'a, OnEvictFn, E>
65 = <LockableHashMap<K, ()> as Lockable<K, ()>>::SyncLimit<'a, OnEvictFn, E>
66 where
67 OnEvictFn: FnMut(Vec<Self::Guard<'a>>) -> Result<(), E>,
68 K: 'a;
69
70 type SyncLimitOwned<OnEvictFn, E>
71 = <LockableHashMap<K, ()> as Lockable<K, ()>>::SyncLimitOwned<OnEvictFn, E>
72 where
73 OnEvictFn: FnMut(Vec<Self::OwnedGuard>) -> Result<(), E>;
74
75 type AsyncLimit<'a, OnEvictFn, E, F>
76 = <LockableHashMap<K, ()> as Lockable<K, ()>>::AsyncLimit<'a, OnEvictFn, E, F>
77 where
78 F: Future<Output = Result<(), E>>,
79 OnEvictFn: FnMut(Vec<Self::Guard<'a>>) -> F,
80 K: 'a;
81
82 type AsyncLimitOwned<OnEvictFn, E, F>
83 = <LockableHashMap<K, ()> as Lockable<K, ()>>::AsyncLimitOwned<OnEvictFn, E, F>
84 where
85 F: Future<Output = Result<(), E>>,
86 OnEvictFn: FnMut(Vec<Self::OwnedGuard>) -> F;
87}
88
89impl<K> LockPool<K>
90where
91 K: Eq + PartialEq + Hash + Clone,
92{
93 /// Create a new lock pool with no locked keys.
94 ///
95 /// Examples
96 /// -----
97 /// ```
98 /// use lockable::LockPool;
99 ///
100 /// let pool = LockPool::new();
101 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
102 /// let guard = pool.async_lock(4).await;
103 /// # Ok::<(), lockable::Never>(())}).unwrap();
104 /// ```
105 #[inline]
106 pub fn new() -> Self {
107 Self {
108 map: LockableHashMap::new(),
109 }
110 }
111
112 /// Return the number of locked keys in the pool.
113 ///
114 /// Examples
115 /// -----
116 /// ```
117 /// use lockable::LockPool;
118 ///
119 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
120 /// let pool = LockPool::new();
121 ///
122 /// // Lock two entries
123 /// let guard1 = pool
124 /// .async_lock(4)
125 /// .await;
126 /// let guard2 = pool
127 /// .async_lock(5)
128 /// .await;
129 ///
130 /// // Now we have two locked entries
131 /// assert_eq!(2, pool.num_locked());
132 /// # Ok::<(), lockable::Never>(())}).unwrap();
133 /// ```
134 #[inline]
135 pub fn num_locked(&self) -> usize {
136 self.map.num_entries_or_locked()
137 }
138
139 /// Lock a key and return a guard for it.
140 ///
141 /// Locking a key prevents any other threads from locking the same key.
142 /// If the lock with this key is currently locked by a different thread, then the current thread blocks until it becomes available.
143 /// Upon returning, the thread is the only thread with the lock held. A RAII guard is returned to allow scoped unlock
144 /// of the lock. When the guard goes out of scope, the lock will be unlocked.
145 ///
146 /// This function can only be used from non-async contexts and will panic if used from async contexts.
147 ///
148 /// The exact behavior on locking a lock in the thread which already holds the lock is left unspecified.
149 /// However, this function will not return on the second call (it might panic or deadlock, for example).
150 ///
151 /// Panics
152 /// -----
153 /// - This function might panic when called if the lock is already held by the current thread.
154 /// - This function will also panic when called from an `async` context.
155 /// See documentation of [tokio::sync::Mutex] for details.
156 ///
157 /// Examples
158 /// -----
159 /// ```
160 /// use lockable::LockPool;
161 ///
162 /// # (|| {
163 /// let pool = LockPool::new();
164 /// let guard1 = pool.blocking_lock(4);
165 /// let guard2 = pool.blocking_lock(5);
166 ///
167 /// // This next line would cause a deadlock or panic because `4` is already locked on this thread
168 /// // let guard3 = pool.blocking_lock(4);
169 ///
170 /// // After dropping the corresponding guard, we can lock it again
171 /// std::mem::drop(guard1);
172 /// let guard3 = pool.blocking_lock(4);
173 /// # Ok::<(), lockable::Never>(())})().unwrap();
174 /// ```
175 #[inline]
176 pub fn blocking_lock(&self, key: K) -> <Self as Lockable<K, ()>>::Guard<'_> {
177 self.map
178 .blocking_lock(key, SyncLimit::no_limit())
179 .infallible_unwrap()
180 }
181
182 // TOOD Add this
183 // /// Lock a key and return a guard for it.
184 // ///
185 // /// This is identical to [LockPool::blocking_lock], please see documentation for that function for more information.
186 // /// But different to [LockPool::blocking_lock], [LockPool::blocking_lock_owned] works on an `Arc<LockPool>`
187 // /// instead of a [LockPool] and returns a [Lockable::OwnedGuard] that binds its lifetime to the [LockPool] in that [Arc].
188 // /// Such a [Lockable::OwnedGuard] can be more easily moved around or cloned than the [Lockable::Guard] returned by [LockPool::blocking_lock].
189 // ///
190 // /// Examples
191 // /// -----
192 // /// ```
193 // /// use lockable::LockPool;
194 // /// use std::sync::Arc;
195 // ///
196 // /// # (|| {
197 // /// let pool = Arc::new(LockPool::new());
198 // /// let guard1 = pool.blocking_lock_owned(4);
199 // /// let guard2 = pool.blocking_lock_owned(5);
200 // ///
201 // /// // This next line would cause a deadlock or panic because `4` is already locked on this thread
202 // /// // let guard3 = pool.blocking_lock_owned(4);
203 // ///
204 // /// // After dropping the corresponding guard, we can lock it again
205 // /// std::mem::drop(guard1);
206 // /// let guard3 = pool.blocking_lock_owned(4);
207 // /// # Ok::<(), lockable::Never>(())})().unwrap();
208 // /// ```
209 // #[inline]
210 // pub fn blocking_lock_owned(self: &Arc<Self>, key: K) -> <Self as Lockable<K, ()>>::OwnedGuard {
211 // self.map
212 // .blocking_lock_owned(key, SyncLimit::no_limit())
213 // .infallible_unwrap()
214 // }
215
216 /// Attempts to acquire the lock with the given key.
217 /// Any changes to that entry will be persisted in the map.
218 /// Locking a key prevents any other threads from locking the same key, but the action of locking a key doesn't insert
219 /// a map entry by itself. Map entries can be inserted and removed using [Guard::insert](crate::Guard::insert) and
220 /// [Guard::remove](crate::Guard::remove) on the returned entry guard.
221 ///
222 /// If the lock could not be acquired because it is already locked, then [Ok](Ok)([None]) is returned. Otherwise, a RAII guard is returned.
223 /// The lock will be unlocked when the guard is dropped.
224 ///
225 /// This function does not block and can be used from both async and non-async contexts.
226 ///
227 /// Examples
228 /// -----
229 /// ```
230 /// use lockable::LockPool;
231 ///
232 /// # (|| {
233 /// let pool = LockPool::new();
234 /// let guard1 = pool.blocking_lock(4);
235 /// let guard2 = pool.blocking_lock(5);
236 ///
237 /// // This next line cannot acquire the lock because `4` is already locked on this thread
238 /// let guard3 = pool.try_lock(4);
239 /// assert!(guard3.is_none());
240 ///
241 /// // After dropping the corresponding guard, we can lock it again
242 /// std::mem::drop(guard1);
243 /// let guard3 = pool.try_lock(4);
244 /// assert!(guard3.is_some());
245 /// # Ok::<(), lockable::Never>(())})().unwrap();
246 /// ```
247 #[inline]
248 pub fn try_lock(&self, key: K) -> Option<<Self as Lockable<K, ()>>::Guard<'_>> {
249 self.map
250 .try_lock(key, SyncLimit::no_limit())
251 .infallible_unwrap()
252 }
253
254 // TODO Add this
255 // /// Attempts to acquire the lock with the given key.
256 // ///
257 // /// This is identical to [LockPool::try_lock], please see documentation for that function for more information.
258 // /// But different to [LockPool::try_lock], [LockPool::try_lock_owned] works on an `Arc<LockPool>`
259 // /// instead of a [LockPool] and returns a [Lockable::OwnedGuard] that binds its lifetime to the [LockPool] in that [Arc].
260 // /// Such a [Lockable::OwnedGuard] can be more easily moved around or cloned than the [Lockable::Guard] returned by [LockPool::try_lock].
261 // ///
262 // /// Examples
263 // /// -----
264 // /// ```
265 // /// use lockable::LockPool;
266 // /// use std::sync::Arc;
267 // ///
268 // /// # (||{
269 // /// let pool = Arc::new(LockPool::new());
270 // /// let guard1 = pool.blocking_lock(4);
271 // /// let guard2 = pool.blocking_lock(5);
272 // ///
273 // /// // This next line cannot acquire the lock because `4` is already locked on this thread
274 // /// let guard3 = pool.try_lock_owned(4);
275 // /// assert!(guard3.is_none());
276 // ///
277 // /// // After dropping the corresponding guard, we can lock it again
278 // /// std::mem::drop(guard1);
279 // /// let guard3 = pool.try_lock_owned(4);
280 // /// assert!(guard3.is_some());
281 // /// # Ok::<(), lockable::Never>(())})().unwrap();
282 // /// ```
283 // #[inline]
284 // pub fn try_lock_owned(
285 // self: &Arc<Self>,
286 // key: K,
287 // ) -> Option<<Self as Lockable<K, ()>>::OwnedGuard> {
288 // self.map
289 // .try_lock_owned(key, SyncLimit::no_limit())
290 // .infallible_unwrap()
291 // }
292
293 /// Lock a key and return a guard for it.
294 ///
295 /// Locking a key prevents any other tasks from locking the same key.
296 ///
297 /// If the lock with this key is currently locked by a different task, then the current tasks `await`s until it becomes available.
298 /// Upon returning, the task is the only task with the lock held. A RAII guard is returned to allow scoped unlock
299 /// of the lock. When the guard goes out of scope, the lock will be unlocked.
300 ///
301 /// Examples
302 /// -----
303 /// ```
304 /// use lockable::LockPool;
305 ///
306 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
307 /// let pool = LockPool::new();
308 /// let guard1 = pool.async_lock(4).await;
309 /// let guard2 = pool.async_lock(5).await;
310 ///
311 /// // This next line would cause a deadlock or panic because `4` is already locked on this thread
312 /// // let guard3 = pool.async_lock(4).await;
313 ///
314 /// // After dropping the corresponding guard, we can lock it again
315 /// std::mem::drop(guard1);
316 /// let guard3 = pool.async_lock(4).await;
317 /// # Ok::<(), lockable::Never>(())}).unwrap();
318 /// ```
319 #[inline]
320 pub async fn async_lock(&self, key: K) -> <Self as Lockable<K, ()>>::Guard<'_> {
321 self.map
322 .async_lock(key, AsyncLimit::no_limit())
323 .await
324 .infallible_unwrap()
325 }
326
327 // TODO Add this
328 // /// Lock a key and return a guard for it.
329 // ///
330 // /// This is identical to [LockPool::async_lock], please see documentation for that function for more information.
331 // /// But different to [LockPool::async_lock], [LockPool::async_lock_owned] works on an `Arc<LockPool>`
332 // /// instead of a [LockPool] and returns a [Lockable::OwnedGuard] that binds its lifetime to the [LockPool] in that [Arc].
333 // /// Such a [Lockable::OwnedGuard] can be more easily moved around or cloned than the [Lockable::Guard] returned by [LockPool::async_lock].
334 // ///
335 // /// Examples
336 // /// -----
337 // /// ```
338 // /// use lockable::LockPool;
339 // /// use std::sync::Arc;
340 // ///
341 // /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
342 // /// let pool = Arc::new(LockPool::new());
343 // /// let guard1 = pool
344 // /// .async_lock_owned(4)
345 // /// .await;
346 // /// let guard2 = pool
347 // /// .async_lock_owned(5)
348 // /// .await;
349 // ///
350 // /// // This next line would cause a deadlock or panic because `4` is already locked on this thread
351 // /// // let guard3 = pool.async_lock_owned(4).await;
352 // ///
353 // /// // After dropping the corresponding guard, we can lock it again
354 // /// std::mem::drop(guard1);
355 // /// let guard3 = pool
356 // /// .async_lock_owned(4)
357 // /// .await;
358 // /// # Ok::<(), lockable::Never>(())}).unwrap();
359 // /// ```
360 // #[inline]
361 // pub async fn async_lock_owned(
362 // self: &Arc<Self>,
363 // key: K,
364 // ) -> <Self as Lockable<K, ()>>::OwnedGuard {
365 // self.map
366 // .async_lock_owned(key, AsyncLimit::no_limit())
367 // .await
368 // .infallible_unwrap()
369 // }
370
371 /// Returns all of the keys that are currently locked.
372 ///
373 /// This function has a high performance cost because it needs to lock the whole
374 /// map to get a consistent snapshot and clone all the keys.
375 ///
376 /// Examples
377 /// -----
378 /// ```
379 /// use lockable::LockPool;
380 ///
381 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
382 /// let pool = LockPool::new();
383 ///
384 /// // Lock two keys
385 /// let guard1 = pool
386 /// .async_lock(4)
387 /// .await;
388 /// let guard2 = pool
389 /// .async_lock(5)
390 /// .await;
391 ///
392 /// let keys: Vec<i64> = pool.locked_keys();
393 ///
394 /// // `keys` now contains both keys
395 /// assert_eq!(2, keys.len());
396 /// assert!(keys.contains(&4));
397 /// assert!(keys.contains(&5));
398 /// # Ok::<(), lockable::Never>(())}).unwrap();
399 /// ```
400 #[inline]
401 pub fn locked_keys(&self) -> Vec<K> {
402 self.map.keys_with_entries_or_locked()
403 }
404}
405
406impl<K> Default for LockPool<K>
407where
408 K: Eq + PartialEq + Hash + Clone,
409{
410 fn default() -> Self {
411 Self::new()
412 }
413}
414
415// TODO Tests for `LockPool`