Struct RangeMutex

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

Source

pub fn new(values: B) -> Self

Creates a new RamgeMutex in an unlocked state ready for use.

Source

pub fn into_inner(self) -> B

Consumes this RangeMutex, returning the underlying data

Source

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.

Source

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 RangeMutexGuards have been leaked.

Source

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.

Source

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.

Source

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.

Source

pub fn len(&self) -> usize

Returns the number of elements in the slice.

Source

pub fn is_empty(&self) -> bool

Returns true if the slice has a length of 0.

Trait Implementations§

Auto Trait Implementations§

§

impl<T, B> !Freeze for RangeMutex<T, B>

§

impl<T, B> !RefUnwindSafe for RangeMutex<T, B>

§

impl<T, B> Unpin for RangeMutex<T, B>

§

impl<T, B> UnwindSafe for RangeMutex<T, B>

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.