Struct Backoff

struct Backoff { ... }

Performs exponential backoff in spin loops.

Backing off in spin loops reduces contention and improves overall performance.

This primitive can execute YIELD and PAUSE instructions, yield the current thread to the OS scheduler, and tell when is a good time to block the thread using a different synchronization mechanism. Each step of the back off procedure takes roughly twice as long as the previous step.

Examples

Backing off in a lock-free loop:

use crossbeam_utils::Backoff;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;

fn fetch_mul(a: &AtomicUsize, b: usize) -> usize {
    let backoff = Backoff::new();
    loop {
        let val = a.load(SeqCst);
        if a.compare_exchange(val, val.wrapping_mul(b), SeqCst, SeqCst).is_ok() {
            return val;
        }
        backoff.spin();
    }
}

Waiting for an AtomicBool to become true:

use crossbeam_utils::Backoff;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering::SeqCst;

fn spin_wait(ready: &AtomicBool) {
    let backoff = Backoff::new();
    while !ready.load(SeqCst) {
        backoff.snooze();
    }
}

Waiting for an AtomicBool to become true and parking the thread after a long wait. Note that whoever sets the atomic variable to true must notify the parked thread by calling unpark():

use crossbeam_utils::Backoff;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering::SeqCst;
use std::thread;

fn blocking_wait(ready: &AtomicBool) {
    let backoff = Backoff::new();
    while !ready.load(SeqCst) {
        if backoff.is_completed() {
            thread::park();
        } else {
            backoff.snooze();
        }
    }
}

Implementations

impl Backoff

fn new() -> Self

Creates a new Backoff.

Examples

use crossbeam_utils::Backoff;

let backoff = Backoff::new();
fn reset(self: &Self)

Resets the Backoff.

Examples

use crossbeam_utils::Backoff;

let backoff = Backoff::new();
backoff.reset();
fn spin(self: &Self)

Backs off in a lock-free loop.

This method should be used when we need to retry an operation because another thread made progress.

The processor may yield using the YIELD or PAUSE instruction.

Examples

Backing off in a lock-free loop:

use crossbeam_utils::Backoff;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;

fn fetch_mul(a: &AtomicUsize, b: usize) -> usize {
    let backoff = Backoff::new();
    loop {
        let val = a.load(SeqCst);
        if a.compare_exchange(val, val.wrapping_mul(b), SeqCst, SeqCst).is_ok() {
            return val;
        }
        backoff.spin();
    }
}

let a = AtomicUsize::new(7);
assert_eq!(fetch_mul(&a, 8), 7);
assert_eq!(a.load(SeqCst), 56);
fn snooze(self: &Self)

Backs off in a blocking loop.

This method should be used when we need to wait for another thread to make progress.

The processor may yield using the YIELD or PAUSE instruction and the current thread may yield by giving up a timeslice to the OS scheduler.

In #[no_std] environments, this method is equivalent to spin.

If possible, use is_completed to check when it is advised to stop using backoff and block the current thread using a different synchronization mechanism instead.

Examples

Waiting for an AtomicBool to become true:

use crossbeam_utils::Backoff;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering::SeqCst;
use std::thread;
use std::time::Duration;

fn spin_wait(ready: &AtomicBool) {
    let backoff = Backoff::new();
    while !ready.load(SeqCst) {
        backoff.snooze();
    }
}

let ready = Arc::new(AtomicBool::new(false));
let ready2 = ready.clone();

thread::spawn(move || {
    thread::sleep(Duration::from_millis(100));
    ready2.store(true, SeqCst);
});

assert_eq!(ready.load(SeqCst), false);
spin_wait(&ready);
assert_eq!(ready.load(SeqCst), true);
# std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371
fn is_completed(self: &Self) -> bool

Returns true if exponential backoff has completed and blocking the thread is advised.

Examples

Waiting for an AtomicBool to become true and parking the thread after a long wait:

use crossbeam_utils::Backoff;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering::SeqCst;
use std::thread;
use std::time::Duration;

fn blocking_wait(ready: &AtomicBool) {
    let backoff = Backoff::new();
    while !ready.load(SeqCst) {
        if backoff.is_completed() {
            thread::park();
        } else {
            backoff.snooze();
        }
    }
}

let ready = Arc::new(AtomicBool::new(false));
let ready2 = ready.clone();
let waiter = thread::current();

thread::spawn(move || {
    thread::sleep(Duration::from_millis(100));
    ready2.store(true, SeqCst);
    waiter.unpark();
});

assert_eq!(ready.load(SeqCst), false);
blocking_wait(&ready);
assert_eq!(ready.load(SeqCst), true);
# std::thread::sleep(std::time::Duration::from_millis(500)); // wait for background threads closed: https://github.com/rust-lang/miri/issues/1371

impl Debug for Backoff

fn fmt(self: &Self, f: &mut fmt::Formatter<'_>) -> fmt::Result

impl Default for Backoff

fn default() -> Backoff

impl Freeze for Backoff

impl RefUnwindSafe for Backoff

impl Send for Backoff

impl Sync for Backoff

impl Unpin for Backoff

impl UnwindSafe for Backoff

impl<T> Any for Backoff

fn type_id(self: &Self) -> TypeId

impl<T> Borrow for Backoff

fn borrow(self: &Self) -> &T

impl<T> BorrowMut for Backoff

fn borrow_mut(self: &mut Self) -> &mut T

impl<T> From for Backoff

fn from(t: T) -> T

Returns the argument unchanged.

impl<T, U> Into for Backoff

fn into(self: Self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of [From]<T> for U chooses to do.

impl<T, U> TryFrom for Backoff

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

impl<T, U> TryInto for Backoff

fn try_into(self: Self) -> Result<U, <U as TryFrom<T>>::Error>