wasm_safe_mutex

A suite of WebAssembly-safe synchronization primitives that paper over platform-specific locking constraints.
The Core Problem
WebAssembly's main thread cannot use blocking locks - attempting to do so will panic with "cannot block on the main thread". This is a fundamental limitation of the browser environment where blocking the main thread would freeze the entire UI.
However, blocking locks ARE allowed in:
- WebAssembly worker threads (where
Atomics.waitis available) - Native platforms (both main and worker threads)
- Most non-WASM contexts (traditional OS threads have no such restrictions)
The Solution
This crate provides synchronization primitives that automatically adapt their locking strategy based on the runtime environment:
- Native: Uses efficient thread parking (
thread::park) - WASM with
Atomics.wait: UsesAtomics.waitfor proper blocking - WASM without
Atomics.wait: Falls back to spinning (e.g., browser main thread)
This means you can write code once and have it work correctly across all platforms, without worrying about whether you're on the main thread, a worker thread, native or WASM.
Primitives
This crate provides the following primitives, all of which support the adaptive behavior:
Mutex: A mutual exclusion primitive for protecting shared data.RwLock: A reader-writer lock that allows multiple concurrent readers or one exclusive writer.Condvar: A condition variable for blocking a thread while waiting for an event.Spinlock: A spinlock for short-lived critical sections (primarily for internal use).mpsc: A multi-producer, single-consumer channel for message passing between threads.
Features
- Transparent adaptation: Automatically detects and uses the best available locking mechanism
- Main thread safe: Won't panic on WASM main thread (uses spinning instead)
- Worker thread optimized: Uses proper blocking when available for efficiency
- Native performance: Full thread parking on native platforms
- Async support: Non-blocking async methods that work everywhere
- Multiple strategies: Try-lock, spin-lock, blocking lock, and async lock
Installation
Add this to your Cargo.toml:
[]
= "0.2.0"
Examples
Mutex
use Mutex;
let mutex = new;
let mut guard = mutex.lock_sync;
*guard = 100;
drop;
assert_eq!;
RwLock
use RwLock;
let rwlock = new;
// Multiple readers
let r1 = rwlock.lock_sync_read;
let r2 = rwlock.lock_sync_read;
assert_eq!;
assert_eq!;
drop;
drop;
// Exclusive writer
let mut w = rwlock.lock_sync_write;
w.push;
Channel (mpsc)
The channel() function creates a Sender and Receiver pair.
The Sender can be cloned to create multiple producers, while the Receiver can be used directly
or consumed as an iterator via IntoIter.
use channel;
let = channel;
// Send values from multiple producers
tx.send_sync.unwrap;
tx.send_sync.unwrap;
// Receive values
assert_eq!;
assert_eq!;
The receiver can also be used as an iterator:
use channel;
use thread;
let = channel;
// Spawn a thread to send values
spawn;
// Iterate over received values until channel closes
for value in rx
Async Usage
use Mutex;
let mutex = new;
// Async lock works everywhere, including WASM main thread
let mut guard = mutex.lock_async.await;
*guard += 1;
Platform Behavior
The primitives transparently handle platform differences:
- Native: Full blocking with thread parking
- WASM with
Atomics.wait: Blocks usingAtomics.wait - WASM without
Atomics.wait: Falls back to spinning (e.g., browser main thread)
This automatic adaptation means your code works everywhere without modification.