pub struct RangeMutex<T, B: RangeMutexBackingStorage<T>> { /* private fields */ }
Expand description
A Mutex
-like type for slices and slice-like containers.
This type acts similarly to std::sync::Mutex<[T]>
,
except that nonoverlapping ranges of the slice can be locked separately.
§Example
use std::sync::Arc;
use std::thread;
use range_mutex::RangeMutex;
const N: usize = 10;
// Spawn a few threads to increment ranges of a shared vector (non-atomically).
let mut data = RangeMutex::new(vec![0; N + 1]);
thread::scope(|scope| {
let data = &data;
for i in 0..N {
scope.spawn(move || {
// The shared state can only be accessed once the lock is held.
// Our non-atomic increment is safe because we're the only thread
// which can access our range of the shared state when the lock is held.
let mut data = data.lock(i..=i+1);
data[0] += 1;
data[1] += 1;
// the lock is unlocked here when `data` goes out of scope.
});
}
});
assert_eq!(data.get_mut(), [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1]);
§Zero-Length Ranges
Attempts to lock zero-length ranges of a RangeMutex
will always succeed
(assuming they are not out-of-bounds). Zero-length ranges are not considered
to overlap with any other ranges, including themselves. For example, having
a lock on the (half-open) range 2..6
will not block an attempt to lock
the (half-open) range 4..4
, and vice versa, since the range 4..4
is
zero-length, and thus empty.
Implementations§
Source§impl<T, B: RangeMutexBackingStorage<T>> RangeMutex<T, B>
impl<T, B: RangeMutexBackingStorage<T>> RangeMutex<T, B>
Sourcepub fn into_inner(self) -> B
pub fn into_inner(self) -> B
Consumes this RangeMutex
, returning the underlying data
Sourcepub fn get_mut(&mut self) -> &mut [T]
pub fn get_mut(&mut self) -> &mut [T]
Returns a mutable reference to the underlying data.
Since this call borrows the Mutex mutably, no actual locking needs to take place – the mutable borrow statically guarantees no locks exist.
Sourcepub fn undo_leak(&mut self) -> &mut [T]
pub fn undo_leak(&mut self) -> &mut [T]
Undo the effect of leaked guards on the borrow state of the
RangeMutex
.
This call is similar to get_mut
but more specialized. It borrows
RangeMutex
mutably to ensure no locks exist and then resets the
state tracking locks. This is relevant if some RangeMutexGuard
s have
been leaked.
Sourcepub fn try_lock(
&self,
range: impl RangeBounds<usize>,
) -> Option<RangeMutexGuard<'_, T>>
pub fn try_lock( &self, range: impl RangeBounds<usize>, ) -> Option<RangeMutexGuard<'_, T>>
Attempts to acquire a lock for a range of this slice.
If the lock could not be acquired at this time, then None
is returned.
Otherwise, an RAII guard is returned. The lock will be unlocked when the
guard is dropped.
This function does not block.
§Panics
Panics if the starting point is greater than the end point or if the end point is greater than the length of the slice.
Sourcepub fn lock(&self, range: impl RangeBounds<usize>) -> RangeMutexGuard<'_, T>
pub fn lock(&self, range: impl RangeBounds<usize>) -> RangeMutexGuard<'_, T>
Acquires a lock for a range of this slice, blocking the current thread until it is able to do so.
This function will block the local thread until it is available to acquire the lock. Upon returning, the thread is the only thread with the lock held for the given range. An RAII guard is returned to allow scoped unlock of the lock. When the guard goes out of scope, the lock will be unlocked.
The exact behavior on locking a range in a thread which already holds a lock on an overlapping range is left unspecified. However, this function will not return on the second call (it might panic or deadlock, for example).
Mutual attempts between mutiple threads to lock overlapping ranges may result in a deadlock. To avoid this, have all threads lock ranges in ascending or descending order consistently.
// Thread 1:
let _g1 = mutex.lock(0..=2);
let _g2 = mutex.lock(3..=5); // Thread 1 may deadlock here if thread 1 holds 0..=2 and thread 2 holds 4..=7.
// Thread 2:
let _g1 = mutex.lock(4..=7);
let _g2 = mutex.lock(0..=3); // Thread 2 may deadlock here if thread 1 holds 0..=2 and thread 2 holds 4..=7.
// Thread 1:
let _g1 = mutex.lock(0..=2); // Either thread 1 will get 0..=2 first, or thread 2 will get 0..=3 first, and then that thread will continue.
let _g2 = mutex.lock(3..=5);
// Thread 2:
let _g1 = mutex.lock(0..=3); // Either thread 1 will get 0..=2 first, or thread 2 will get 0..=3 first, and then that thread will continue.
let _g2 = mutex.lock(4..=7);
§Panics
Panics if the starting point is greater than the end point or if the end point is greater than the length of the slice.
Sourcepub fn lock_async(
&self,
range: impl RangeBounds<usize>,
) -> impl Future<Output = RangeMutexGuard<'_, T>>
pub fn lock_async( &self, range: impl RangeBounds<usize>, ) -> impl Future<Output = RangeMutexGuard<'_, T>>
Asynchronously acquires a lock for a range of this slice, blocking the current task until it is able to do so.
This function will block the local task until it is available to acquire the lock. Upon returning, the task is the only task with the lock held for the given range. An RAII guard is returned to allow scoped unlock of the lock. When the guard goes out of scope, the lock will be unlocked.
The exact behavior on locking a range in a task which already holds a lock on an overlapping range is left unspecified. However, this function will not return on the second call (it might panic or deadlock, for example).
Mutual attempts between mutiple tasks to lock overlapping ranges may
result in a deadlock. To avoid this, have all tasks lock ranges in
ascending or descending order consistently. See lock
for examples of this.
§Panics
Panics if the starting point is greater than the end point or if the end point is greater than the length of the slice.