pub struct Mutex<T, L = DefaultMutex>where
L: ScopedRawMutex,
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 blocking::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 blocking::Mutex
and futures_util::lock::Mutex
, which
will never guarantee fairness.
Finally, this mutex does not implement poisoning3, unlike
std::sync::Mutex
.
Overriding the blocking mutex
This type uses a blocking Mutex
internally to
synchronize access to its wait list. By default, the DefaultMutex
type
is used as the underlying mutex implementation. To use an alternative
ScopedRawMutex
implementation, use the
new_with_raw_mutex
constructor. See the documentation
on overriding mutex
implementations for more
details.
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§
§impl<T> Mutex<T>
impl<T> Mutex<T>
pub const fn new(data: T) -> Mutex<T>
pub const fn new(data: T) -> Mutex<T>
Returns a new Mutex
protecting the provided data
.
The returned Mutex
will be in the unlocked state and is ready for
use.
This constructor returns a Mutex
that uses a DefaultMutex
as the
underlying blocking mutex implementation. To use an alternative
ScopedRawMutex
implementation, use the Mutex::new_with_raw_mutex
constructor instead. See the documentation on overriding mutex
implementations
for more details.
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);
§impl<T, L> Mutex<T, L>where
L: ScopedRawMutex,
impl<T, L> Mutex<T, L>where
L: ScopedRawMutex,
pub const fn new_with_raw_mutex(data: T, lock: L) -> Mutex<T, L>
pub const fn new_with_raw_mutex(data: T, lock: L) -> Mutex<T, L>
Returns a new Mutex
protecting the provided data
, using the provided
ScopedRawMutex
implementation as the raw mutex.
The returned Mutex
will be in the unlocked state and is ready for
use.
This constructor allows a Mutex
to be constructed with any type that
implements ScopedRawMutex
as the underlying raw blocking mutex
implementation. See the documentation on overriding mutex
implementations
for more details.
§impl<T, L> Mutex<T, L>where
L: ScopedRawMutex,
impl<T, L> Mutex<T, L>where
L: ScopedRawMutex,
pub fn into_inner(self) -> T
pub fn into_inner(self) -> T
Consumes this Mutex
, returning the guarded data.
§impl<T, L> Mutex<T, L>where
L: ScopedRawMutex,
T: ?Sized,
impl<T, L> Mutex<T, L>where
L: ScopedRawMutex,
T: ?Sized,
pub fn lock(&self) -> Lock<'_, T, L> ⓘ
pub fn lock(&self) -> Lock<'_, T, L> ⓘ
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;
}
pub fn try_lock(&self) -> Option<MutexGuard<'_, T, L>>
pub fn try_lock(&self) -> Option<MutexGuard<'_, T, L>>
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);
§impl<T, L> Mutex<T, L>where
L: ScopedRawMutex,
T: ?Sized,
impl<T, L> Mutex<T, L>where
L: ScopedRawMutex,
T: ?Sized,
pub async fn lock_owned(self: Arc<Mutex<T, L>>) -> OwnedMutexGuard<T, L>
Available on crate feature alloc
only.
pub async fn lock_owned(self: Arc<Mutex<T, L>>) -> OwnedMutexGuard<T, L>
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;
}
pub fn try_lock_owned(
self: Arc<Mutex<T, L>>
) -> Result<OwnedMutexGuard<T, L>, Arc<Mutex<T, L>>>
Available on crate feature alloc
only.
pub fn try_lock_owned( self: Arc<Mutex<T, L>> ) -> Result<OwnedMutexGuard<T, L>, Arc<Mutex<T, L>>>
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 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);
}