Struct maitake_sync::mutex::Mutex

source ·
pub struct Mutex<T: ?Sized> { /* private fields */ }
Expand description

An asynchronous mutual exclusion lock for protecting shared data.

The data can only be accessed through the RAII guards returned from lock and try_lock, which guarantees that the data is only ever accessed when the mutex is locked.

§Comparison With Other Mutices

This is an asynchronous mutex. When the shared data is locked, the lock method will wait by causing the current task to yield until the shared data is available. This is in contrast to blocking mutices, such as std::sync::Mutex, which wait by blocking the current thread1, or spinlock based mutices, such as spin::Mutex, which wait by spinning in a busy loop.

The futures-util crate also provides an implementation of an asynchronous mutex, futures_util::lock::Mutex. However, this mutex requires the Rust standard library, and is thus unsuitable for use in environments where the standard library is unavailable. In addition, the futures-util mutex requires an additional allocation for every task that is waiting to acquire the lock, while maitake’s mutex is based on an intrusive linked list, and therefore can be used without allocation2. This makes maitake’s mutex suitable for environments where heap allocations must be minimized or cannot be used at all.

In addition, this is a fairly queued mutex. This means that the lock is always acquired in a first-in, first-out order — if a task acquires and then releases the lock, and then wishes to acquire the lock again, it will not acquire the lock until every other task ahead of it in the queue has had a chance to lock the shared data. Again, this is in contrast to std::sync::Mutex, where fairness depends on the underlying OS’ locking primitives; and spin::Mutex and futures_util::lock::Mutex, which will never guarantee fairness.

Finally, this mutex does not implement [poisoning]3, unlike std::sync::Mutex.


  1. And therefore require an operating system to manage threading. 

  2. The tasks themselves must, of course, be stored somewhere, but this need not be a heap allocation in systems with a fixed set of statically-allocated tasks. And, when tasks are heap-allocated, these allocations need not be provided by liballoc

  3. In fact, this mutex cannot implement poisoning, as poisoning requires support for unwinding, and maitake assumes that panics are invariably fatal

Implementations§

source§

impl<T> Mutex<T>

source

pub const fn new(data: T) -> Self

Returns a new Mutex protecting the provided data.

The returned Mutex will be in the unlocked state and is ready for use.

§Examples
use maitake_sync::Mutex;

let lock = Mutex::new(42);

As this is a const fn, it may be used in a static initializer:

use maitake_sync::Mutex;

static GLOBAL_LOCK: Mutex<usize> = Mutex::new(42);
source

pub fn into_inner(self) -> T

Consumes this Mutex, returning the guarded data.

source§

impl<T: ?Sized> Mutex<T>

source

pub fn lock(&self) -> Lock<'_, T>

Locks this mutex.

This returns a Lock future that will wait until no other task is accessing the shared data. If the shared data is not locked, this future will complete immediately. When the lock has been acquired, this future will return a MutexGuard.

§Examples
use maitake_sync::Mutex;

async fn example() {
    let mutex = Mutex::new(1);

    let mut guard = mutex.lock().await;
    *guard = 2;
}
source

pub fn try_lock(&self) -> Option<MutexGuard<'_, T>>

Attempts to lock the mutex without waiting, returning None if the mutex is already locked locked.

§Returns
  • Some(MutexGuard)` if the mutex was not already locked
  • None if the mutex is currently locked and locking it would require waiting
§Examples
use maitake_sync::Mutex;

let mutex = Mutex::new(1);

let n = mutex.try_lock()?;
assert_eq!(*n, 1);
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.

§Examples
let mut lock = maitake_sync::spin::Mutex::new(0);
*lock.get_mut() = 10;
assert_eq!(*lock.try_lock().unwrap(), 10);
source§

impl<T: ?Sized> Mutex<T>

source

pub async fn lock_owned(self: Arc<Self>) -> OwnedMutexGuard<T>

Available on crate feature alloc only.

Locks this mutex, returning an owned RAII guard.

This function will that will wait until no other task is accessing the shared data. If the shared data is not locked, this future will complete immediately. When the lock has been acquired, this future will return a OwnedMutexGuard.

This method is similar to Mutex::lock, except that (rather than borrowing the Mutex) the returned guard owns an Arc clone, incrememting its reference count. Therefore, this method is only available when the Mutex is wrapped in an Arc, and the returned guard is valid for the 'static lifetime.

§Examples
use maitake_sync::Mutex;
use alloc::sync::Arc;

async fn example() {
    let mutex = Arc::new(Mutex::new(1));

    let mut guard = mutex.clone().lock_owned().await;
    *guard = 2;
}
source

pub fn try_lock_owned(self: Arc<Self>) -> Result<OwnedMutexGuard<T>, Arc<Self>>

Available on crate feature alloc only.

Attempts this mutex without waiting, returning an owned RAII guard, or Err if the mutex is already locked.

This method is similar to Mutex::try_lock, except that (rather than borrowing the Mutex) the returned guard owns an Arc clone, incrememting its reference count. Therefore, this method is only available when the Mutex is wrapped in an Arc, and the returned guard is valid for the 'static lifetime.

§Returns
  • Ok(OwnedMutexGuard)` if the mutex was not already locked

  • Err(Arc<Mutex<T>>) if the mutex is currently locked and locking it would require waiting.

    This returns an Err rather than None so that the same Arc clone may be reused (such as by calling try_lock_owned again) without having to decrement and increment the reference count again.

§Examples
use maitake_sync::Mutex;
use alloc::sync::Arc;

let mutex = Arc::new(Mutex::new(1));

if let Ok(guard) = mutex.clone().try_lock_owned() {
    assert_eq!(*guard, 1);
}

Trait Implementations§

source§

impl<T: ?Sized + Debug> Debug for Mutex<T>

source§

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

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

impl<T: Default> Default for Mutex<T>

source§

fn default() -> Self

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

impl<T> Send for Mutex<T>
where T: Send,

source§

impl<T> Sync for Mutex<T>
where T: Send,

Auto Trait Implementations§

§

impl<T> !Freeze for Mutex<T>

§

impl<T> !RefUnwindSafe for Mutex<T>

§

impl<T> Unpin for Mutex<T>
where T: Unpin + ?Sized,

§

impl<T> !UnwindSafe for Mutex<T>

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

§

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

§

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.