Rust · Busy Waiting

Busy Waiting

use forkunion::BasicSpinMutex;use forkunion::BasicSpinMutexGuard;use forkunion::*; 4.0.0-rc.1 · d927445f

Here’s what “busy waiting” looks like in C++:

while (!has_work_to_do())
    std::this_thread::yield();

On Linux, the std::this_thread::yield() translates into a sched_yield system call, which means context switching to the kernel and back. Instead, you can replace the standard_yield_t wrapper with a platform-specific micro-wait instruction, which is much cheaper. Those instructions, like WFET on Arm, generally hint the CPU to transition to a low-power state.

WrapperISAInstructionPrivileges
x86_pause_tx86PAUSER3
x86_tpause_tx86+WAITPKGUMONITOR+UMWAITR3
arm64_yield_tAArch64YIELDEL0
arm64_wfe_tAArch64LDAXR+WFEEL0
arm64_wfet_tAArch64+WFXTWFETEL0
riscv64_pause_tRISC-VPAUSEU
riscv64_wrs_tRISC-V+ZawrsWRS.STO/WRS.NTOU

No kernel calls. No futexes. Works in tight loops.

The UMWAIT, WFE, WFET, and WRS wrappers go a step further than a spin hint: they sleep the core until a store to the watched line, capped by a timeout only where the loop guards more than one word, so the pool parks a worker in a low-power state rather than burning the core. spin_mutex<fu::preferred_yield_t> takes the same wrappers, while the default spin_mutex_t yields to the scheduler and Rust’s SpinMutex spins on core::hint::spin_loop - a futex-free std::mutex alternative, and the lock used in the NUMA example above. On WebAssembly threads the waiter is wasm_wait_t: a 32- or 64-bit watch sleeps in memory.atomic.wait until a wasm_atomic_ref releasing write notifies or 1 µs passes, so pair the two; uncapped waits and 1- or 2-byte watches spin.

API

Types

SpinMutexA type alias for the most commonly used spin mutex configuration.
types.rs:465 ↗
pub type SpinMutex<T> = BasicSpinMutex<T, true>;

This is equivalent to BasicSpinMutex<T, true>, which enables CPU-specific pause instructions for efficient busy-waiting.

Examples

use forkunion::*;

let mutex = SpinMutex::new(42);
let mut guard = mutex.lock();
*guard = 100;
StructBasicSpinMutexA generic spin mutex that uses CPU-specific pause instructions for efficient busy-waiting.6
types.rs:248 ↗
pub struct BasicSpinMutex<T, const PAUSE: bool>

This is a low-level synchronization primitive that spins on a busy loop rather than blocking the thread. It's most appropriate for very short critical sections where the cost of context switching would be higher than busy-waiting.

The generic parameter P allows customization of the pause behavior:

  • true enables CPU-specific pause instructions, recommended for most use cases
  • false disables pause instructions, which may be useful in specialized scenarios

Examples

use forkunion::*;

// Create a spin mutex with pause instructions enabled
let mutex = BasicSpinMutex::<i32, true>::new(42);

// Lock, access data, and unlock
{
    let mut guard = mutex.lock();
    *guard = 100;
} // Lock is automatically released when guard goes out of scope

// Verify the value was changed
assert_eq!(*mutex.lock(), 100);

Fast for short critical sections but spins continuously. Use when latency matters more than CPU usage. Avoid for long critical sections or high contention scenarios.

Implements

Send, Sync
newCreates a new spin mutex in the unlocked state.
types.rs:273 ↗
pub const fn new(data: T) -> Self

Arguments

  • data - The value to be protected by the mutex

Examples

use forkunion::*;

let mutex = BasicSpinMutex::<i32, true>::new(0);
lockAcquires the lock, returning a guard that provides access to the protected data.
types.rs:295 ↗
pub fn lock(&self) -> BasicSpinMutexGuard<'_, T, PAUSE>

This method will spin until the lock is acquired. If the lock is already held, it will busy-wait using CPU-specific pause instructions when PAUSE is true.

Examples

use forkunion::*;

let mutex = BasicSpinMutex::<i32, true>::new(0);
let mut guard = mutex.lock();
*guard = 42;
lock_if_freeAttempts to acquire the lock without blocking.
types.rs:329 ↗
pub fn lock_if_free(&self) -> Option<BasicSpinMutexGuard<'_, T, PAUSE>>

Returns Some(guard) if the lock was successfully acquired, or None if the lock is currently held by another thread.

Examples

use forkunion::*;

let mutex = BasicSpinMutex::<i32, true>::new(0);

if let Some(mut guard) = mutex.lock_if_free() {
    *guard = 42;
    println!("Lock acquired and value set");
} else {
    println!("Lock is currently held by another thread");
};
is_lockedChecks if the mutex is currently locked.
types.rs:358 ↗
pub fn is_locked(&self) -> bool

This method provides a non-blocking way to check the lock state, but should be used carefully as the state can change immediately after this call returns.

Examples

use forkunion::*;

let mutex = BasicSpinMutex::<i32, true>::new(0);
assert!(!mutex.is_locked());

{
    let _guard = mutex.lock();
    assert!(mutex.is_locked());
}

assert!(!mutex.is_locked());
into_innerConsumes the mutex and returns the protected data.
types.rs:376 ↗
pub fn into_inner(self) -> T

This method bypasses the locking mechanism entirely, since we already own it exclusively.

Examples

use forkunion::*;

let mutex = BasicSpinMutex::<i32, true>::new(42);
let data = mutex.into_inner();
assert_eq!(data, 42);
get_mutGets a mutable reference to the protected data.
types.rs:395 ↗
pub fn get_mut(&mut self) -> &mut T

Since this requires a mutable reference to the mutex, no locking is needed as we have exclusive access.

Examples

use forkunion::*;

let mut mutex = BasicSpinMutex::<i32, true>::new(0);
*mutex.get_mut() = 42;
assert_eq!(*mutex.lock(), 42);
StructBasicSpinMutexGuardA guard providing access to the data protected by a BasicSpinMutex.2
types.rs:409 ↗
pub struct BasicSpinMutexGuard<'a, T, const PAUSE: bool>

The lock is automatically released when this guard is dropped.

Implements

Deref, DerefMut, Drop
getReturns a reference to the protected data.
types.rs:418 ↗
pub fn get(&self) -> &T

This method is rarely needed since the guard implements Deref.

get_mutReturns a mutable reference to the protected data.
types.rs:426 ↗
pub fn get_mut(&mut self) -> &mut T

This method is rarely needed since the guard implements DerefMut.