rendezvous_swap/
rendezvous.rs

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