Function crossbeam_epoch::unprotected

source ·
pub unsafe fn unprotected() -> &'static Guard
Expand description

Returns a reference to a dummy guard that allows unprotected access to Atomics.

This guard should be used in special occasions only. Note that it doesn’t actually keep any thread pinned - it’s just a fake guard that allows loading from Atomics unsafely.

Note that calling defer with a dummy guard will not defer the function - it will just execute the function immediately.

If necessary, it’s possible to create more dummy guards by cloning: unprotected().clone().

§Safety

Loading and dereferencing data from an Atomic using this guard is safe only if the Atomic is not being concurrently modified by other threads.

§Examples

use crossbeam_epoch::{self as epoch, Atomic};
use std::sync::atomic::Ordering::Relaxed;

let a = Atomic::new(7);

unsafe {
    // Load `a` without pinning the current thread.
    a.load(Relaxed, epoch::unprotected());

    // It's possible to create more dummy guards.
    let dummy = epoch::unprotected();

    dummy.defer(move || {
        println!("This gets executed immediately.");
    });

    // Dropping `dummy` doesn't affect the current thread - it's just a noop.
}

The most common use of this function is when constructing or destructing a data structure.

For example, we can use a dummy guard in the destructor of a Treiber stack because at that point no other thread could concurrently modify the Atomics we are accessing.

If we were to actually pin the current thread during destruction, that would just unnecessarily delay garbage collection and incur some performance cost, so in cases like these unprotected is very helpful.

use crossbeam_epoch::{self as epoch, Atomic};
use std::mem::ManuallyDrop;
use std::sync::atomic::Ordering::Relaxed;

struct Stack<T> {
    head: Atomic<Node<T>>,
}

struct Node<T> {
    data: ManuallyDrop<T>,
    next: Atomic<Node<T>>,
}

impl<T> Drop for Stack<T> {
    fn drop(&mut self) {
        unsafe {
            // Unprotected load.
            let mut node = self.head.load(Relaxed, epoch::unprotected());

            while let Some(n) = node.as_ref() {
                // Unprotected load.
                let next = n.next.load(Relaxed, epoch::unprotected());

                // Take ownership of the node, then drop its data and deallocate it.
                let mut o = node.into_owned();
                ManuallyDrop::drop(&mut o.data);
                drop(o);

                node = next;
            }
        }
    }
}