Skip to main content

LockMap

Struct LockMap 

Source
pub struct LockMap<K, V, S = RandomState> { /* private fields */ }
Expand description

A thread-safe hashmap that supports locking entries at the key level.

LockMap provides a concurrent hashmap with fine-grained per-key locking. Each key can be independently locked, so operations on different keys can proceed in parallel. The map is internally sharded to reduce contention on the map structure itself.

§Storage Design

The key and its pre-computed hash are stored together in the internal entry state, so each operation hashes the key only once. The full hash is also reused for shard selection and table probing.

§Examples

use lockmap::LockMap;

let map = LockMap::<String, u32>::new();

// Basic operations
map.insert("key1".to_string(), 42);
assert_eq!(map.get("key1"), Some(42));

// Entry API for exclusive access
{
    let mut entry = map.entry("key2".to_string());
    entry.insert(123);
}

// Remove a value
assert_eq!(map.remove("key1"), Some(42));
assert_eq!(map.get("key1"), None);

Implementations§

Source§

impl<K: Eq + Hash, V> LockMap<K, V>

Source

pub fn new() -> Self

Creates a new LockMap with the default number of shards.

Source

pub fn with_capacity(capacity: usize) -> Self

Creates a new LockMap with the specified initial capacity.

Source

pub fn with_capacity_and_shard_amount( capacity: usize, shard_amount: usize, ) -> Self

Creates a new LockMap with the specified initial capacity and number of shards.

Source§

impl<K: Eq + Hash, V, S: BuildHasher> LockMap<K, V, S>

Source

pub fn with_hasher(hasher: S) -> Self

Creates a new LockMap using the given hash builder.

§Examples
use lockmap::LockMap;
use std::collections::hash_map::RandomState;

let map = LockMap::<String, u32, _>::with_hasher(RandomState::new());
map.insert("key".to_string(), 42);
assert_eq!(map.get("key"), Some(42));
Source

pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> Self

Creates a new LockMap with the specified initial capacity, using the given hash builder.

Source

pub fn with_capacity_and_shard_amount_and_hasher( capacity: usize, shard_amount: usize, hasher: S, ) -> Self

Creates a new LockMap with the specified initial capacity and number of shards, using the given hash builder.

Source§

impl<K, V, S> LockMap<K, V, S>

Source

pub fn len(&self) -> usize

Returns the number of elements in the map.

Source

pub fn is_empty(&self) -> bool

Returns true if the map contains no elements.

Source§

impl<K: Eq + Hash, V, S: BuildHasher> LockMap<K, V, S>

Source

pub fn entry(&self, key: K) -> Entry<'_, K, V, S>

Gets exclusive access to an entry in the map.

The returned Entry provides exclusive access to the key and its associated value until it is dropped.

Locking behaviour: Deadlock if called when holding the same entry.

§Examples
let map = LockMap::<String, u32>::new();
{
    let mut entry = map.entry("key".to_string());
    entry.insert(42);
}
Source

pub fn try_entry(&self, key: K) -> Option<Entry<'_, K, V, S>>

Attempts to get exclusive access to an entry without blocking.

Returns None if another thread currently holds the entry for this key. Unlike entry, this method never blocks on the per-key lock (it may still wait briefly on the internal shard lock).

§Examples
let map = LockMap::<String, u32>::new();
let entry = map.entry("key".to_string());
// The key is held by `entry`, so `try_entry` fails:
assert!(map.try_entry("key".to_string()).is_none());
drop(entry);
assert!(map.try_entry("key".to_string()).is_some());
Source

pub fn entry_by_ref<Q>(&self, key: &Q) -> Entry<'_, K, V, S>
where K: Borrow<Q> + for<'c> From<&'c Q>, Q: Eq + Hash + ?Sized,

Gets exclusive access to an entry by reference.

Locking behaviour: Deadlock if called when holding the same entry.

§Examples
let map = LockMap::<String, u32>::new();
{
    let mut entry = map.entry_by_ref("key");
    entry.insert(42);
}
Source

pub fn try_entry_by_ref<Q>(&self, key: &Q) -> Option<Entry<'_, K, V, S>>
where K: Borrow<Q> + for<'c> From<&'c Q>, Q: Eq + Hash + ?Sized,

Attempts to get exclusive access to an entry by reference without blocking.

Returns None if another thread currently holds the entry for this key. Unlike entry_by_ref, this method never blocks on the per-key lock (it may still wait briefly on the internal shard lock).

§Examples
let map = LockMap::<String, u32>::new();
let entry = map.entry_by_ref("key");
assert!(map.try_entry_by_ref("key").is_none());
drop(entry);
assert!(map.try_entry_by_ref("key").is_some());
Source

pub fn get<Q>(&self, key: &Q) -> Option<V>
where K: Borrow<Q>, V: Clone, Q: Eq + Hash + ?Sized,

Gets the value associated with the given key.

If other threads are currently accessing the key, this will wait until exclusive access is available before returning.

§Performance Note

When no other thread holds an entry for this key, the clone() operation is performed while holding the shard lock. If V::clone() is expensive, consider using entry() or entry_by_ref() combined with Entry::get() to avoid blocking other keys in the same shard.

Locking behaviour: Deadlock if called when holding the same entry.

§Examples
use lockmap::LockMap;

let map = LockMap::<String, u32>::new();
map.insert("key".to_string(), 42);
assert_eq!(map.get("key"), Some(42));
assert_eq!(map.get("missing"), None);
Source

pub fn insert(&self, key: K, value: V) -> Option<V>

Sets a value in the map, returning the previous value if any.

Locking behaviour: Deadlock if called when holding the same entry.

§Examples
use lockmap::LockMap;

let map = LockMap::<String, u32>::new();
assert_eq!(map.insert("key".to_string(), 42), None);
assert_eq!(map.insert("key".to_string(), 123), Some(42));
Source

pub fn insert_by_ref<Q>(&self, key: &Q, value: V) -> Option<V>
where K: Borrow<Q> + for<'c> From<&'c Q>, Q: Eq + Hash + ?Sized,

Sets a value in the map by reference key.

Locking behaviour: Deadlock if called when holding the same entry.

§Examples
use lockmap::LockMap;

let map = LockMap::<String, u32>::new();
map.insert_by_ref("key", 42);
assert_eq!(map.get("key"), Some(42));
Source

pub fn contains_key<Q>(&self, key: &Q) -> bool
where K: Borrow<Q>, Q: Eq + Hash + ?Sized,

Checks if the map contains a key.

Locking behaviour: Deadlock if called when holding the same entry.

§Examples
use lockmap::LockMap;

let map = LockMap::new();
map.insert("key", 42);
assert!(map.contains_key("key"));
assert!(!map.contains_key("non_existent_key"));
Source

pub fn remove<Q>(&self, key: &Q) -> Option<V>
where K: Borrow<Q>, Q: Eq + Hash + ?Sized,

Removes a key from the map.

Locking behaviour: Deadlock if called when holding the same entry.

§Examples
use lockmap::LockMap;

let map = LockMap::<String, u32>::new();
map.insert("key".to_string(), 42);
assert_eq!(map.remove("key"), Some(42));
assert_eq!(map.get("key"), None);
Source

pub fn batch_lock<'a, M>(&'a self, keys: BTreeSet<K>) -> M
where K: Clone, M: FromIterator<(K, Entry<'a, K, V, S>)>,

Acquires exclusive locks for a batch of keys in a deadlock-safe manner.

Takes a BTreeSet of keys, ensuring they are processed and locked in a consistent, sorted order across all threads.

§Examples
use lockmap::LockMap;
use std::collections::BTreeSet;

let map = LockMap::<u32, u32>::new();
map.insert(1, 100);
map.insert(2, 200);
map.insert(3, 300);

let mut keys = BTreeSet::new();
keys.insert(3);
keys.insert(1);
keys.insert(2);

let mut locked_entries = map.batch_lock::<std::collections::HashMap<_, _>>(keys);

locked_entries.get_mut(&1).and_then(|entry| entry.insert(101));
locked_entries.get_mut(&2).and_then(|entry| entry.insert(201));
locked_entries.get_mut(&3).and_then(|entry| entry.insert(301));

drop(locked_entries);

assert_eq!(map.get(&1), Some(101));
assert_eq!(map.get(&2), Some(201));
assert_eq!(map.get(&3), Some(301));
Source§

impl<K: Eq + Hash, V, S> LockMap<K, V, S>

Source

pub fn clear(&self)

Removes all key-value pairs from the map.

Entries that are currently held by an Entry guard are cleared by waiting for the guard to be released, exactly as remove would.

Locking behaviour: Deadlock if called when holding any entry of this map.

§Examples
use lockmap::LockMap;

let map = LockMap::<String, u32>::new();
map.insert("a".to_string(), 1);
map.insert("b".to_string(), 2);
map.clear();
assert!(map.is_empty());
Source

pub fn for_each<F>(&self, f: F)
where F: FnMut(&K, &V),

Calls f for every key-value pair in the map.

Entries that are not currently held are visited under the internal shard lock; entries held by an Entry guard are visited afterwards by waiting for the guard to be released. Consequently the iteration is not an atomic snapshot: entries may be inserted or removed concurrently.

Locking behaviour: Deadlock if f accesses this map or if called when holding any entry of this map.

§Examples
use lockmap::LockMap;

let map = LockMap::<u32, u32>::new();
map.insert(1, 10);
map.insert(2, 20);

let mut sum = 0;
map.for_each(|_, v| sum += v);
assert_eq!(sum, 30);
Source

pub fn retain<F>(&self, f: F)
where F: FnMut(&K, &mut V) -> bool,

Retains only the key-value pairs for which f returns true.

Entries that are not currently held are visited under the internal shard lock; entries held by an Entry guard are visited afterwards by waiting for the guard to be released.

Locking behaviour: Deadlock if f accesses this map or if called when holding any entry of this map.

§Examples
use lockmap::LockMap;

let map = LockMap::<u32, u32>::new();
for i in 0..10 {
    map.insert(i, i);
}
map.retain(|_, v| *v % 2 == 0);
assert_eq!(map.len(), 5);

Trait Implementations§

Source§

impl<K, V, S> Debug for LockMap<K, V, S>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<K: Eq + Hash, V, S: BuildHasher + Default> Default for LockMap<K, V, S>

Source§

fn default() -> Self

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

Auto Trait Implementations§

§

impl<K, V, S = RandomState> !RefUnwindSafe for LockMap<K, V, S>

§

impl<K, V, S = RandomState> !UnwindSafe for LockMap<K, V, S>

§

impl<K, V, S> Freeze for LockMap<K, V, S>
where S: Freeze,

§

impl<K, V, S> Send for LockMap<K, V, S>
where S: Send, K: Send, V: Send,

§

impl<K, V, S> Sync for LockMap<K, V, S>
where S: Sync, K: Send, V: Send,

§

impl<K, V, S> Unpin for LockMap<K, V, S>
where S: Unpin,

§

impl<K, V, S> UnsafeUnpin for LockMap<K, V, S>
where S: UnsafeUnpin,

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, 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.