1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
//! Contains [`Rendezvous`]
#![forbid(unsafe_code)]
use alloc::sync::Arc;
use core::hint::spin_loop;
use core::sync::atomic::AtomicUsize;
use core::sync::atomic::Ordering::{Acquire, Release};
/// Synchronise execution between threads.
/// # Example: Sync thread execution
/// ```rust
/// use rendezvous_swap::Rendezvous;
/// use std::thread;
///
/// let (mut my_rendezvous, mut their_rendezvous) = Rendezvous::new();
/// thread::spawn(move || {
/// for i in 1..5 {
/// println!("{i}");
/// their_rendezvous.wait();
/// }
/// });
/// for i in 1..5 {
/// println!("{i}");
/// my_rendezvous.wait();
/// }
/// ```
/// This prints:
/// ```text
/// 1
/// 1
/// 2
/// 2
/// 3
/// 3
/// 4
/// 4
/// ```
#[non_exhaustive]
pub struct Rendezvous {
/// Atomic counter for this thread
my_counter: Arc<AtomicUsize>,
/// Atomic counter for other thread
their_counter: Arc<AtomicUsize>,
/// Thread local generation
generation: usize,
}
impl Rendezvous {
/// Synchronize execution with other thread.
///
/// As a side-effect, memory is also synchronized.
#[inline]
pub fn wait(&mut self) {
self.wait_inline();
}
/// Always inlined version of [`Rendezvous::wait`]
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn wait_inline(&mut self) {
let next_generation = self.generation.wrapping_add(1);
self.my_counter.store(next_generation, Release);
while {
// Signal to processor (not OS) that we are in a spinloop.
// Performance seems to improve by a tiny bit with this.
spin_loop();
self.their_counter.load(Acquire) == self.generation
} {}
self.generation = next_generation;
}
/// Create a linked pair of [`Rendezvous`]
#[must_use]
#[inline]
pub fn new() -> (Self, Self) {
let first = Arc::new(AtomicUsize::new(0));
let second = Arc::new(AtomicUsize::new(0));
(
Self {
my_counter: Arc::clone(&first),
their_counter: Arc::clone(&second),
generation: 0,
},
Self {
my_counter: second,
their_counter: first,
generation: 0,
},
)
}
}