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>
impl<K: Eq + Hash, V> LockMap<K, V>
Sourcepub fn with_capacity(capacity: usize) -> Self
pub fn with_capacity(capacity: usize) -> Self
Creates a new LockMap with the specified initial capacity.
Sourcepub fn with_capacity_and_shard_amount(
capacity: usize,
shard_amount: usize,
) -> Self
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>
impl<K: Eq + Hash, V, S: BuildHasher> LockMap<K, V, S>
Sourcepub fn with_hasher(hasher: S) -> Self
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));Sourcepub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> Self
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.
Sourcepub fn with_capacity_and_shard_amount_and_hasher(
capacity: usize,
shard_amount: usize,
hasher: S,
) -> Self
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: Eq + Hash, V, S: BuildHasher> LockMap<K, V, S>
impl<K: Eq + Hash, V, S: BuildHasher> LockMap<K, V, S>
Sourcepub fn entry(&self, key: K) -> Entry<'_, K, V, S>
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);
}Sourcepub fn try_entry(&self, key: K) -> Option<Entry<'_, K, V, S>>
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());Sourcepub fn entry_by_ref<Q>(&self, key: &Q) -> Entry<'_, K, V, S>
pub fn entry_by_ref<Q>(&self, key: &Q) -> Entry<'_, K, V, S>
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);
}Sourcepub fn try_entry_by_ref<Q>(&self, key: &Q) -> Option<Entry<'_, K, V, S>>
pub fn try_entry_by_ref<Q>(&self, key: &Q) -> Option<Entry<'_, K, V, S>>
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());Sourcepub fn get<Q>(&self, key: &Q) -> Option<V>
pub fn get<Q>(&self, key: &Q) -> Option<V>
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);Sourcepub fn insert(&self, key: K, value: V) -> Option<V>
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));Sourcepub fn insert_by_ref<Q>(&self, key: &Q, value: V) -> Option<V>
pub fn insert_by_ref<Q>(&self, key: &Q, value: V) -> Option<V>
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));Sourcepub fn contains_key<Q>(&self, key: &Q) -> bool
pub fn contains_key<Q>(&self, key: &Q) -> bool
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"));Sourcepub fn remove<Q>(&self, key: &Q) -> Option<V>
pub fn remove<Q>(&self, key: &Q) -> Option<V>
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);Sourcepub fn batch_lock<'a, M>(&'a self, keys: BTreeSet<K>) -> M
pub fn batch_lock<'a, M>(&'a self, keys: BTreeSet<K>) -> M
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>
impl<K: Eq + Hash, V, S> LockMap<K, V, S>
Sourcepub fn clear(&self)
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());Sourcepub fn for_each<F>(&self, f: F)
pub fn for_each<F>(&self, f: F)
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);Sourcepub fn retain<F>(&self, f: F)
pub fn retain<F>(&self, f: F)
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);