pub struct LockPool<K>{ /* private fields */ }Expand description
A pool of locks where individual locks can be locked/unlocked by key. It initially considers all keys as “unlocked”, but they can be locked and if a second thread tries to acquire a lock for the same key, they will have to wait.
use lockable::LockPool;
let pool = LockPool::new();
let guard1 = pool.async_lock(4).await;
let guard2 = pool.async_lock(5).await;
// This next line would cause a deadlock or panic because `4` is already locked on this thread
// let guard3 = pool.async_lock(4).await;
// After dropping the corresponding guard, we can lock it again
std::mem::drop(guard1);
let guard3 = pool.async_lock(4).await;You can use an arbitrary type to index locks by, as long as that type implements PartialEq + Eq + Hash + Clone + Debug.
use lockable::LockPool;
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
struct CustomLockKey(u32);
let pool = LockPool::new();
let guard = pool.async_lock(CustomLockKey(4)).await;Under the hood, a LockPool is a LockableHashMap with () as a value type, i.e. LockPool<K> is just a wrapper
around LockableHashMap<K, ()> with a simpler API. If you need more complex functionalities, please look at
LockableHashMap.
Implementations§
Source§impl<K> LockPool<K>
impl<K> LockPool<K>
Sourcepub fn new() -> Self
pub fn new() -> Self
Create a new lock pool with no locked keys.
§Examples
use lockable::LockPool;
let pool = LockPool::new();
let guard = pool.async_lock(4).await;Sourcepub fn num_locked(&self) -> usize
pub fn num_locked(&self) -> usize
Return the number of locked keys in the pool.
§Examples
use lockable::LockPool;
let pool = LockPool::new();
// Lock two entries
let guard1 = pool
.async_lock(4)
.await;
let guard2 = pool
.async_lock(5)
.await;
// Now we have two locked entries
assert_eq!(2, pool.num_locked());Sourcepub fn blocking_lock(&self, key: K) -> <Self as Lockable<K, ()>>::Guard<'_>
pub fn blocking_lock(&self, key: K) -> <Self as Lockable<K, ()>>::Guard<'_>
Lock a key and return a guard for it.
Locking a key prevents any other threads from locking the same key. If the lock with this key is currently locked by a different thread, then the current thread blocks until it becomes available. Upon returning, the thread is the only thread with the lock held. A RAII guard is returned to allow scoped unlock of the lock. When the guard goes out of scope, the lock will be unlocked.
This function can only be used from non-async contexts and will panic if used from async contexts.
The exact behavior on locking a lock in the thread which already holds the lock is left unspecified. However, this function will not return on the second call (it might panic or deadlock, for example).
§Panics
- This function might panic when called if the lock is already held by the current thread.
- This function will also panic when called from an
asynccontext. See documentation of tokio::sync::Mutex for details.
§Examples
use lockable::LockPool;
let pool = LockPool::new();
let guard1 = pool.blocking_lock(4);
let guard2 = pool.blocking_lock(5);
// This next line would cause a deadlock or panic because `4` is already locked on this thread
// let guard3 = pool.blocking_lock(4);
// After dropping the corresponding guard, we can lock it again
std::mem::drop(guard1);
let guard3 = pool.blocking_lock(4);Sourcepub fn try_lock(&self, key: K) -> Option<<Self as Lockable<K, ()>>::Guard<'_>>
pub fn try_lock(&self, key: K) -> Option<<Self as Lockable<K, ()>>::Guard<'_>>
Attempts to acquire the lock with the given key. Any changes to that entry will be persisted in the map. Locking a key prevents any other threads from locking the same key, but the action of locking a key doesn’t insert a map entry by itself. Map entries can be inserted and removed using Guard::insert and Guard::remove on the returned entry guard.
If the lock could not be acquired because it is already locked, then Ok(None) is returned. Otherwise, a RAII guard is returned. The lock will be unlocked when the guard is dropped.
This function does not block and can be used from both async and non-async contexts.
§Examples
use lockable::LockPool;
let pool = LockPool::new();
let guard1 = pool.blocking_lock(4);
let guard2 = pool.blocking_lock(5);
// This next line cannot acquire the lock because `4` is already locked on this thread
let guard3 = pool.try_lock(4);
assert!(guard3.is_none());
// After dropping the corresponding guard, we can lock it again
std::mem::drop(guard1);
let guard3 = pool.try_lock(4);
assert!(guard3.is_some());Sourcepub async fn async_lock(&self, key: K) -> <Self as Lockable<K, ()>>::Guard<'_>
pub async fn async_lock(&self, key: K) -> <Self as Lockable<K, ()>>::Guard<'_>
Lock a key and return a guard for it.
Locking a key prevents any other tasks from locking the same key.
If the lock with this key is currently locked by a different task, then the current tasks awaits until it becomes available.
Upon returning, the task is the only task with the lock held. A RAII guard is returned to allow scoped unlock
of the lock. When the guard goes out of scope, the lock will be unlocked.
§Examples
use lockable::LockPool;
let pool = LockPool::new();
let guard1 = pool.async_lock(4).await;
let guard2 = pool.async_lock(5).await;
// This next line would cause a deadlock or panic because `4` is already locked on this thread
// let guard3 = pool.async_lock(4).await;
// After dropping the corresponding guard, we can lock it again
std::mem::drop(guard1);
let guard3 = pool.async_lock(4).await;Sourcepub fn locked_keys(&self) -> Vec<K>
pub fn locked_keys(&self) -> Vec<K>
Returns all of the keys that are currently locked.
This function has a high performance cost because it needs to lock the whole map to get a consistent snapshot and clone all the keys.
§Examples
use lockable::LockPool;
let pool = LockPool::new();
// Lock two keys
let guard1 = pool
.async_lock(4)
.await;
let guard2 = pool
.async_lock(5)
.await;
let keys: Vec<i64> = pool.locked_keys();
// `keys` now contains both keys
assert_eq!(2, keys.len());
assert!(keys.contains(&4));
assert!(keys.contains(&5));Trait Implementations§
Source§impl<K> Lockable<K, ()> for LockPool<K>
impl<K> Lockable<K, ()> for LockPool<K>
Source§type Guard<'a> = <LockableHashMap<K, ()> as Lockable<K, ()>>::Guard<'a>
where
K: 'a
type Guard<'a> = <LockableHashMap<K, ()> as Lockable<K, ()>>::Guard<'a> where K: 'a
Source§type OwnedGuard = <LockableHashMap<K, ()> as Lockable<K, ()>>::OwnedGuard
type OwnedGuard = <LockableHashMap<K, ()> as Lockable<K, ()>>::OwnedGuard
Source§type SyncLimit<'a, OnEvictFn, E> = <LockableHashMap<K, ()> as Lockable<K, ()>>::SyncLimit<'a, OnEvictFn, E>
where
OnEvictFn: FnMut(Vec<Self::Guard<'a>>) -> Result<(), E>,
K: 'a
type SyncLimit<'a, OnEvictFn, E> = <LockableHashMap<K, ()> as Lockable<K, ()>>::SyncLimit<'a, OnEvictFn, E> where OnEvictFn: FnMut(Vec<Self::Guard<'a>>) -> Result<(), E>, K: 'a
Source§type SyncLimitOwned<OnEvictFn, E> = <LockableHashMap<K, ()> as Lockable<K, ()>>::SyncLimitOwned<OnEvictFn, E>
where
OnEvictFn: FnMut(Vec<Self::OwnedGuard>) -> Result<(), E>
type SyncLimitOwned<OnEvictFn, E> = <LockableHashMap<K, ()> as Lockable<K, ()>>::SyncLimitOwned<OnEvictFn, E> where OnEvictFn: FnMut(Vec<Self::OwnedGuard>) -> Result<(), E>
Source§type AsyncLimit<'a, OnEvictFn, E, F> = <LockableHashMap<K, ()> as Lockable<K, ()>>::AsyncLimit<'a, OnEvictFn, E, F>
where
F: Future<Output = Result<(), E>>,
OnEvictFn: FnMut(Vec<Self::Guard<'a>>) -> F,
K: 'a
type AsyncLimit<'a, OnEvictFn, E, F> = <LockableHashMap<K, ()> as Lockable<K, ()>>::AsyncLimit<'a, OnEvictFn, E, F> where F: Future<Output = Result<(), E>>, OnEvictFn: FnMut(Vec<Self::Guard<'a>>) -> F, K: 'a
Source§type AsyncLimitOwned<OnEvictFn, E, F> = <LockableHashMap<K, ()> as Lockable<K, ()>>::AsyncLimitOwned<OnEvictFn, E, F>
where
F: Future<Output = Result<(), E>>,
OnEvictFn: FnMut(Vec<Self::OwnedGuard>) -> F
type AsyncLimitOwned<OnEvictFn, E, F> = <LockableHashMap<K, ()> as Lockable<K, ()>>::AsyncLimitOwned<OnEvictFn, E, F> where F: Future<Output = Result<(), E>>, OnEvictFn: FnMut(Vec<Self::OwnedGuard>) -> F
Auto Trait Implementations§
impl<K> !Freeze for LockPool<K>
impl<K> RefUnwindSafe for LockPool<K>where
K: RefUnwindSafe,
impl<K> Send for LockPool<K>where
K: Send,
impl<K> Sync for LockPool<K>
impl<K> Unpin for LockPool<K>where
K: Unpin,
impl<K> UnwindSafe for LockPool<K>where
K: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more