AsyncMutex

Struct AsyncMutex 

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

  1. The first task to encounter contention allocates a wait queue
  2. Subsequent tasks register themselves in the queue with their async wakers
  3. When the lock is released, the next waiting task is awakened via its waker
  4. 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:

  1. Successfully remove itself from the wait queue, or
  2. 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::Mutex due 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>

Source

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.

Source

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.

Source

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.

Source

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).

Source

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();
Source

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();
Source

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();
Source

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.

Trait Implementations§

Source§

impl<T: Send> Send for AsyncMutex<T>

Source§

impl<T: Send> Sync for AsyncMutex<T>

Auto Trait Implementations§

§

impl<T> !Freeze for AsyncMutex<T>

§

impl<T> !RefUnwindSafe for AsyncMutex<T>

§

impl<T> Unpin for AsyncMutex<T>
where T: Unpin,

§

impl<T> !UnwindSafe for AsyncMutex<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>,

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.