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}