pub struct AsyncMutex<T> { /* private fields */ }Expand description
An asynchronous mutex that provides exclusive access to shared data.
This mutex is optimized for async/await contexts and can yield to the async
runtime instead of blocking threads when contention occurs. It maintains the
same high-performance characteristics as Mutex<T> while providing
seamless integration with async code.
§Performance Characteristics
AsyncMutex is designed for optimal performance in async environments:
- Fast Path: When uncontended, acquiring the lock requires only a single atomic compare-and-swap operation
- Async-Aware: Uses async wakers instead of thread parking, allowing the runtime to schedule other tasks while waiting
- Zero-Cost Conversion: Can be converted to/from
Mutex<T>without allocation due to identical memory layout - Extreme Efficiency During Contention: Dynamically allocates a wait queue only with no additional heap overhead after contention occurs
§Memory Layout
The struct uses #[repr(C)] to ensure a stable memory layout that is
identical to Mutex<T>. This enables safe zero-cost conversions between
synchronous and asynchronous mutex types.
§Contention Handling
When multiple tasks attempt to acquire the lock simultaneously:
- The first task to encounter contention allocates a wait queue
- Subsequent tasks register themselves in the queue with their async wakers
- When the lock is released, the next waiting task is awakened via its waker
- The queue is automatically deallocated when contention subsides
§Thread Safety
This type is Send + Sync when T: Send. The mutex ensures that only one
task can access the protected data at any time through the MutexGuard.
§Cancellation Safety
Lock acquisition futures returned by lock() are
cancellation-safe. If a future is dropped while waiting, it will either:
- Successfully remove itself from the wait queue, or
- Wait synchronously for the lock signal and immediately drop the guard
This ensures no dangling pointers remain in the queue after cancellation.
§Conversion to Sync
This async mutex can be seamlessly converted to a Mutex<T> using
as_sync(), to_sync(), or
lock_sync() without any allocation overhead.
§Examples
Basic usage:
use xutex::AsyncMutex;
use swait::*;
async fn example() {
let mutex = AsyncMutex::new(0);
{
let mut guard = mutex.lock().await;
*guard += 1;
}
assert_eq!(*mutex.lock().await, 1);
}
example().swait();Sharing between tasks:
use xutex::AsyncMutex;
use std::sync::Arc;
use swait::*;
async fn example() {
let mutex = Arc::new(AsyncMutex::new(0));
let mut handles = Vec::new();
for _ in 0..10 {
let mutex = Arc::clone(&mutex);
handles.push(async move {
let mut guard = mutex.lock().await;
*guard += 1;
});
}
for handle in handles {
handle.await;
}
assert_eq!(*mutex.lock().await, 10);
}
example().swait();Converting to synchronous mutex:
use xutex::AsyncMutex;
use swait::*;
#[cfg(feature = "std")]
async fn example() {
let async_mutex = AsyncMutex::new(42);
// Use sync method on async mutex
let guard = async_mutex.lock_sync();
assert_eq!(*guard, 42);
drop(guard);
// Convert to sync mutex
let sync_mutex = async_mutex.to_sync();
let guard = sync_mutex.lock();
assert_eq!(*guard, 42);
}
#[cfg(feature = "std")]
example().swait();§Performance Notes
- Async Contexts: Significantly faster than blocking mutexes in async code since it doesn’t block threads
- Sync Contexts: Slight overhead compared to
std::sync::Mutexdue to async infrastructure, but the difference is minimal - Hybrid Usage: Ideal for libraries that need to work in both sync and async contexts without maintaining separate data structures
Implementations§
Source§impl<T> AsyncMutex<T>
impl<T> AsyncMutex<T>
Sourcepub fn new(data: T) -> Self
pub fn new(data: T) -> Self
Creates a new asynchronous mutex wrapping the supplied data.
This mirrors Mutex::new but returns an AsyncMutex. The underlying
MutexInternal is allocated immediately; no heap allocation is
performed until contention forces a queue to be created.
Sourcepub fn lock(&self) -> AsyncLockRequest<'_, T> ⓘ
pub fn lock(&self) -> AsyncLockRequest<'_, T> ⓘ
Returns a future that resolves to a MutexGuard when the lock becomes
available.
The future is represented by AsyncLockRequest. It holds a reference to
the internal mutex, a fresh Signal (initially without a waker),
and a PhantomPinned to ensure the request is !Unpin.
Sourcepub fn try_lock(&self) -> Option<MutexGuard<'_, T>>
pub fn try_lock(&self) -> Option<MutexGuard<'_, T>>
Attempts to acquire the lock without blocking.
Returns Some(MutexGuard) if the fast‑path succeeds, otherwise None.
This simply forwards to the internal MutexInternal::try_lock.
Sourcepub fn as_sync(&self) -> &Mutex<T>
pub fn as_sync(&self) -> &Mutex<T>
Casts a reference to AsyncMutex<T> to a reference to the synchronous
Mutex<T>.
Both structs are #[repr(C)] and have identical layout, so this
unsafe conversion is sound. It enables the async mutex to reuse
the synchronous lock implementation when needed (e.g., lock_sync).
Sourcepub fn to_sync(self: AsyncMutex<T>) -> Mutex<T>
pub fn to_sync(self: AsyncMutex<T>) -> Mutex<T>
Converts this AsyncMutex<T> into a Mutex<T> without allocating.W
Both structs are #[repr(C)] and have identical layout, so the
conversion is sound.
§Example
use std::sync::Arc;
use xutex::{Mutex, AsyncMutex};
fn example() {
// Create an asynchronous mutex.
let async_mutex = AsyncMutex::new(10);
// Convert it into a synchronous mutex without any allocation.
let sync_mutex: Mutex<i32> = async_mutex.to_sync();
// The data is still accessible via the sync mutex.
let guard = sync_mutex.lock();
assert_eq!(*guard, 10);
}
example();Sourcepub fn to_sync_arc(self: Arc<Self>) -> Arc<Mutex<T>>
pub fn to_sync_arc(self: Arc<Self>) -> Arc<Mutex<T>>
Converts an Arc<AsyncMutex<T>> into an Arc<Mutex<T>> without
allocating.
The underlying pointer is re‑interpreted because the two types share the same layout.
§Example
use std::sync::Arc;
use xutex::{Mutex, AsyncMutex};
fn example() {
// Wrap an asynchronous mutex in an `Arc`.
let async_arc: Arc<AsyncMutex<u32>> = Arc::new(AsyncMutex::new(10));
// Convert the `Arc<AsyncMutex<_>>` into an `Arc<Mutex<_>>` without allocation.
let sync_arc: Arc<Mutex<u32>> = async_arc.clone().to_sync_arc();
// The data is still accessible via the sync mutex.
let guard = sync_arc.lock();
assert_eq!(*guard, 10);
}
example();Sourcepub fn clone_sync(self: &Arc<Self>) -> Arc<Mutex<T>>
pub fn clone_sync(self: &Arc<Self>) -> Arc<Mutex<T>>
Clones an Arc<AsyncMutex<T>> and returns it as an Arc<Mutex<T>>
without allocating.
This is a convenience method that combines Arc::clone with
to_sync_arc. It’s useful when you want to share the mutex with
sync code while keeping the original Arc<AsyncMutex<T>> for
asynchronous code.
§Example
use std::sync::Arc;
use xutex::{Mutex, AsyncMutex};
use swait::*;
async fn example() {
let async_arc: Arc<AsyncMutex<u32>> = Arc::new(AsyncMutex::new(10));
// Clone and convert to sync without consuming the original
let sync_arc: Arc<Mutex<u32>> = async_arc.clone_sync();
// Both can be used independently
*sync_arc.lock() = 20;
let guard = async_arc.lock().await;
assert_eq!(*guard, 20);
}
example().swait();Sourcepub fn lock_sync(&self) -> MutexGuard<'_, T>
pub fn lock_sync(&self) -> MutexGuard<'_, T>
Acquires the lock synchronously, blocking the current thread.
Internally it converts self to a &Mutex<T> via as_sync and then
calls the regular Mutex::lock method.