Name Description Size
atomic
backoff.rs 8936
cache_padded.rs 6204
lib.rs Miscellaneous tools for concurrent programming. ## Atomics * [`AtomicCell`], a thread-safe mutable memory location. * [`AtomicConsume`], for reading from primitive atomic types with "consume" ordering. ## Thread synchronization * [`Parker`], a thread parking primitive. * [`ShardedLock`], a sharded reader-writer lock with fast concurrent reads. * [`WaitGroup`], for synchronizing the beginning or end of some computation. ## Utilities * [`Backoff`], for exponential backoff in spin loops. * [`CachePadded`], for padding and aligning a value to the length of a cache line. * [`scope`], for spawning threads that borrow local variables from the stack. [`AtomicCell`]: atomic::AtomicCell [`AtomicConsume`]: atomic::AtomicConsume [`Parker`]: sync::Parker [`ShardedLock`]: sync::ShardedLock [`WaitGroup`]: sync::WaitGroup [`scope`]: thread::scope 3348
sync
thread.rs Threads that can borrow variables from the stack. Create a scope when spawned threads need to access variables on the stack: ``` use crossbeam_utils::thread; let people = vec![ "Alice".to_string(), "Bob".to_string(), "Carol".to_string(), ]; thread::scope(|s| { for person in &people { s.spawn(move |_| { println!("Hello, {}!", person); }); } }).unwrap(); ``` # Why scoped threads? Suppose we wanted to re-write the previous example using plain threads: ```compile_fail,E0597 use std::thread; let people = vec![ "Alice".to_string(), "Bob".to_string(), "Carol".to_string(), ]; let mut threads = Vec::new(); for person in &people { threads.push(thread::spawn(move || { println!("Hello, {}!", person); })); } for thread in threads { thread.join().unwrap(); } ``` This doesn't work because the borrow checker complains about `people` not living long enough: ```text error[E0597]: `people` does not live long enough --> src/main.rs:12:20 | 12 | for person in &people { | ^^^^^^ borrowed value does not live long enough ... 21 | } | - borrowed value only lives until here | = note: borrowed value must be valid for the static lifetime... ``` The problem here is that spawned threads are not allowed to borrow variables on stack because the compiler cannot prove they will be joined before `people` is destroyed. Scoped threads are a mechanism to guarantee to the compiler that spawned threads will be joined before the scope ends. # How scoped threads work If a variable is borrowed by a thread, the thread must complete before the variable is destroyed. Threads spawned using [`std::thread::spawn`] can only borrow variables with the `'static` lifetime because the borrow checker cannot be sure when the thread will complete. A scope creates a clear boundary between variables outside the scope and threads inside the scope. Whenever a scope spawns a thread, it promises to join the thread before the scope ends. This way we guarantee to the borrow checker that scoped threads only live within the scope and can safely access variables outside it. # Nesting scoped threads Sometimes scoped threads need to spawn more threads within the same scope. This is a little tricky because argument `s` lives *inside* the invocation of `thread::scope()` and as such cannot be borrowed by scoped threads: ```compile_fail,E0373,E0521 use crossbeam_utils::thread; thread::scope(|s| { s.spawn(|_| { // Not going to compile because we're trying to borrow `s`, // which lives *inside* the scope! :( s.spawn(|_| println!("nested thread")); }); }); ``` Fortunately, there is a solution. Every scoped thread is passed a reference to its scope as an argument, which can be used for spawning nested threads: ``` use crossbeam_utils::thread; thread::scope(|s| { // Note the `|s|` here. s.spawn(|s| { // Yay, this works because we're using a fresh argument `s`! :) s.spawn(|_| println!("nested thread")); }); }).unwrap(); ``` 18660