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`