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 poisoning3, unlike
std::sync::Mutex
.
And therefore require an operating system to manage threading. ↩
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
. ↩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>
impl<T> Mutex<T>
Sourcepub const fn new(data: T) -> Self
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§impl<T: ?Sized> Mutex<T>
impl<T: ?Sized> Mutex<T>
Sourcepub fn lock(&self) -> Lock<'_, T> ⓘ
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;
}
Sourcepub fn try_lock(&self) -> Option<MutexGuard<'_, T>>
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 lockedNone
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§impl<T: ?Sized> Mutex<T>
impl<T: ?Sized> Mutex<T>
Sourcepub async fn lock_owned(self: Arc<Self>) -> OwnedMutexGuard<T>
pub async fn lock_owned(self: Arc<Self>) -> OwnedMutexGuard<T>
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;
}
Sourcepub fn try_lock_owned(self: Arc<Self>) -> Result<OwnedMutexGuard<T>, Arc<Self>>
pub fn try_lock_owned(self: Arc<Self>) -> Result<OwnedMutexGuard<T>, Arc<Self>>
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 thanNone
so that the sameArc
clone may be reused (such as by callingtry_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);
}