| Crates.io | xutex |
| lib.rs | xutex |
| version | 0.2.1 |
| created_at | 2025-11-18 16:49:20.619219+00 |
| updated_at | 2026-01-04 14:03:35.578093+00 |
| description | an extremely fast async mutex with alternative sync API |
| homepage | |
| repository | https://github.com/fereidani/xutex |
| max_upload_size | |
| id | 1938683 |
| size | 114,266 |
Xutex is a high-performance mutex that seamlessly bridges synchronous and asynchronous Rust code with a single type and unified internal representation. Designed for extremely low-latency lock acquisition under minimal contention, it achieves near-zero overhead on the fast path while remaining runtime-agnostic.
AtomicPtr on 64-bit platforms (guarded data stored separately)std::task::Wakerno_std environments, relying only on core and alloc[dependencies]
xutex = "0.2"
Or via cargo:
# with std
cargo add xutex
# for no-std environments
cargo add xutex --no-default-features
#[cfg(feature = "std")]
fn example() {
use xutex::Mutex;
let mutex = Mutex::new(0);
{
let mut guard = mutex.lock();
*guard += 1;
} // automatically unlocked on drop
assert_eq!(*mutex.lock(), 1);
}
use xutex::AsyncMutex;
async fn increment(mutex: &AsyncMutex<i32>) {
let mut guard = mutex.lock().await;
*guard += 1;
}
Convert seamlessly between sync and async:
#[cfg(feature = "std")]
use xutex::Mutex;
#[cfg(feature = "std")]
async fn example(mutex: &Mutex<i32>) {
let async_ref = mutex.as_async();
let guard = async_ref.lock().await;
}
#[cfg(feature = "std")]
fn example(){
use xutex::{Mutex, AsyncMutex};
// Async → Sync
let async_mutex = AsyncMutex::new(5);
let sync_ref: &Mutex<_> = async_mutex.as_sync();
let guard = sync_ref.lock();
drop(guard);
// Block on async mutex from sync context
let guard = async_mutex.lock_sync();
}
Atomic state machine: Three states encoded in a single pointer:
UNLOCKED (null): Lock is freeLOCKED (sentinel): Lock held, no waitersUPDATING: Queue initialization in progressQUEUE_PTR: Lock held with waiting tasks/threadsLock-free fast path: Uncontended acquisition uses a single compare_exchange
Lazy queue allocation: Wait queue created only when contention occurs
Pointer tagging: LSB tagging prevents race conditions during queue modifications
Stack-allocated waiters: Signal nodes live on the stack, forming an intrusive linked list
Optimized memory ordering: Careful use of Acquire/Release semantics
Adaptive backoff: Exponential backoff reduces cache thrashing under contention
Minimal heap allocation: At most one allocation per contended lock via pooled queue reuse, additional waiters require zero allocations
Run benchmarks on your machine:
cargo bench
Expected Performance (varies by hardware):
tokio::sync::Mutex in async contextsstd::sync::Mutex with minimal overhead from queue pointer checks under high contention; matches parking_lot performance in low-contention scenarios┌─────────────────────────────────────────────┐
│ Mutex<T> / AsyncMutex<T> │
│ ┌───────────────────────────────────────┐ │
│ │ MutexInternal<T> │ │
│ │ • queue: AtomicPtr<QueueStructure> │ │
│ │ • inner: UnsafeCell<T> │ │
│ └───────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
│
├─ UNLOCKED (null) ──────────────► Lock available
│
├─ LOCKED (sentinel) ─────────────► Lock held, no waiters
│
└─ Queue pointer ─────────────────► Lock held, waiters queued
│
▼
┌─────────────────┐
│ SignalQueue │
│ (linked list) │
└─────────────────┘
│
▼
┌─────────────────┐ ┌─────────────────┐
│ Signal │────►│ Signal │────► ...
│ • waker │ │ • waker │
│ • value │ │ • value │
└─────────────────┘ └─────────────────┘
Each waiter tracks its state through atomic transitions:
SIGNAL_UNINIT (0): Initial stateSIGNAL_INIT_WAITING (1): Enqueued and waitingSIGNAL_SIGNALED (2): Lock grantedSIGNAL_RETURNED (!0): Guard has been returnedunsafe blocks for:
Mutex<T>| Method | Description |
|---|---|
new(data: T) |
Create a new synchronous mutex |
lock() |
Acquire the lock (blocks current thread) |
try_lock() |
Attempt non-blocking acquisition |
lock_async() |
Acquire asynchronously (returns Future) |
as_async() |
View as &AsyncMutex<T> |
to_async() |
Convert to AsyncMutex<T> |
to_async_arc() |
Convert Arc<Mutex<T>> to Arc<AsyncMutex<T>> |
AsyncMutex<T>| Method | Description |
|---|---|
new(data: T) |
Create a new asynchronous mutex |
lock() |
Acquire the lock (returns Future) |
try_lock() |
Attempt non-blocking acquisition |
lock_sync() |
Acquire synchronously (blocks current thread) |
as_sync() |
View as &Mutex<T> |
to_sync() |
Convert to Mutex<T> |
to_sync_arc() |
Convert Arc<AsyncMutex<T>> to Arc<Mutex<T>> |
MutexGuard<'a, T>Implements Deref<Target = T> and DerefMut for transparent access to the protected data. Automatically releases the lock on drop.
std::sync::Mutex may offer slightly better performance due to lower abstraction overheadRwLock implementations (e.g., std::sync::RwLock or tokio::sync::RwLock) that allow multiple concurrent readersstd::sync::Mutex poisoning semantics are requiredT stored separatelystd::sync::Mutex, panics don't poison the lockstd::sync::Mutex in pure-sync scenarios (~1-5%)Run the test suite:
# Standard tests
cargo test
# With Miri (undefined behavior detection)
cargo +nightly miri test
# Benchmarks
cargo bench
RwLock variant with shared/exclusive lockingContributions are welcome! Please:
cargo +nightly fmt and cargo clippy before submittingcargo miri test passesno-std compatible; use core and alloc instead of std. Ensure cargo test and cargo test --no-default-features run without warnings.Licensed under the MIT License.
Author: Khashayar Fereidani
Repository: github.com/fereidani/xutex