lockable

Struct LockPool

Source
pub struct LockPool<K>
where K: Eq + PartialEq + Hash + Clone,
{ /* 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>
where K: Eq + PartialEq + Hash + Clone,

Source

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;
Source

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());
Source

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 async context. 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);
Source

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());
Source

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;
Source

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> Default for LockPool<K>
where K: Eq + PartialEq + Hash + Clone,

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<K> Lockable<K, ()> for LockPool<K>
where K: Eq + PartialEq + Hash + Clone,

Source§

type Guard<'a> = <LockableHashMap<K, ()> as Lockable<K, ()>>::Guard<'a> where K: 'a

A non-owning guard holding a lock for an entry in a LockableHashMap or a LockableLruCache. This guard is created via LockableHashMap::blocking_lock, LockableHashMap::async_lock or LockableHashMap::try_lock, or the corresponding LockableLruCache methods, and its lifetime is bound to the lifetime of the LockableHashMap/LockableLruCache. Read more
Source§

type OwnedGuard = <LockableHashMap<K, ()> as Lockable<K, ()>>::OwnedGuard

A owning guard holding a lock for an entry in a LockableHashMap or a LockableLruCache. This guard is created via LockableHashMap::blocking_lock_owned, LockableHashMap::async_lock_owned or LockableHashMap::try_lock_owned, or the corresponding LockableLruCache methods, and its lifetime is bound to the lifetime of the LockableHashMap/LockableLruCache within its Arc. Read more
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

TODO Documentation
Source§

type SyncLimitOwned<OnEvictFn, E> = <LockableHashMap<K, ()> as Lockable<K, ()>>::SyncLimitOwned<OnEvictFn, E> where OnEvictFn: FnMut(Vec<Self::OwnedGuard>) -> Result<(), E>

TODO Documentation
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

TODO Documentation
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

TODO Documentation

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>
where K: Sync + Send,

§

impl<K> Unpin for LockPool<K>
where K: Unpin,

§

impl<K> UnwindSafe for LockPool<K>
where K: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.