tokio/loom/std/
barrier.rs

1//! A `Barrier` that provides `wait_timeout`.
2//!
3//! This implementation mirrors that of the Rust standard library.
4
5use crate::loom::sync::{Condvar, Mutex};
6use std::fmt;
7use std::time::{Duration, Instant};
8
9/// A barrier enables multiple threads to synchronize the beginning
10/// of some computation.
11///
12/// # Examples
13///
14/// ```
15/// # #[cfg(not(target_family = "wasm"))]
16/// # {
17/// use std::sync::{Arc, Barrier};
18/// use std::thread;
19///
20/// let mut handles = Vec::with_capacity(10);
21/// let barrier = Arc::new(Barrier::new(10));
22/// for _ in 0..10 {
23///     let c = Arc::clone(&barrier);
24///     // The same messages will be printed together.
25///     // You will NOT see any interleaving.
26///     handles.push(thread::spawn(move|| {
27///         println!("before wait");
28///         c.wait();
29///         println!("after wait");
30///     }));
31/// }
32/// // Wait for other threads to finish.
33/// for handle in handles {
34///     handle.join().unwrap();
35/// }
36/// # }
37/// ```
38pub(crate) struct Barrier {
39    lock: Mutex<BarrierState>,
40    cvar: Condvar,
41    num_threads: usize,
42}
43
44// The inner state of a double barrier
45struct BarrierState {
46    count: usize,
47    generation_id: usize,
48}
49
50/// A `BarrierWaitResult` is returned by [`Barrier::wait()`] when all threads
51/// in the [`Barrier`] have rendezvoused.
52///
53/// # Examples
54///
55/// ```
56/// use std::sync::Barrier;
57///
58/// let barrier = Barrier::new(1);
59/// let barrier_wait_result = barrier.wait();
60/// ```
61pub(crate) struct BarrierWaitResult(bool);
62
63impl fmt::Debug for Barrier {
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        f.debug_struct("Barrier").finish_non_exhaustive()
66    }
67}
68
69impl Barrier {
70    /// Creates a new barrier that can block a given number of threads.
71    ///
72    /// A barrier will block `n`-1 threads which call [`wait()`] and then wake
73    /// up all threads at once when the `n`th thread calls [`wait()`].
74    ///
75    /// [`wait()`]: Barrier::wait
76    ///
77    /// # Examples
78    ///
79    /// ```
80    /// use std::sync::Barrier;
81    ///
82    /// let barrier = Barrier::new(10);
83    /// ```
84    #[must_use]
85    pub(crate) fn new(n: usize) -> Barrier {
86        Barrier {
87            lock: Mutex::new(BarrierState {
88                count: 0,
89                generation_id: 0,
90            }),
91            cvar: Condvar::new(),
92            num_threads: n,
93        }
94    }
95
96    /// Blocks the current thread until all threads have rendezvoused here.
97    ///
98    /// Barriers are re-usable after all threads have rendezvoused once, and can
99    /// be used continuously.
100    ///
101    /// A single (arbitrary) thread will receive a [`BarrierWaitResult`] that
102    /// returns `true` from [`BarrierWaitResult::is_leader()`] when returning
103    /// from this function, and all other threads will receive a result that
104    /// will return `false` from [`BarrierWaitResult::is_leader()`].
105    ///
106    /// # Examples
107    ///
108    /// ```
109    /// # #[cfg(not(target_family = "wasm"))]
110    /// # {
111    /// use std::sync::{Arc, Barrier};
112    /// use std::thread;
113    ///
114    /// let mut handles = Vec::with_capacity(10);
115    /// let barrier = Arc::new(Barrier::new(10));
116    /// for _ in 0..10 {
117    ///     let c = Arc::clone(&barrier);
118    ///     // The same messages will be printed together.
119    ///     // You will NOT see any interleaving.
120    ///     handles.push(thread::spawn(move|| {
121    ///         println!("before wait");
122    ///         c.wait();
123    ///         println!("after wait");
124    ///     }));
125    /// }
126    /// // Wait for other threads to finish.
127    /// for handle in handles {
128    ///     handle.join().unwrap();
129    /// }
130    /// # }
131    /// ```
132    pub(crate) fn wait(&self) -> BarrierWaitResult {
133        let mut lock = self.lock.lock();
134        let local_gen = lock.generation_id;
135        lock.count += 1;
136        if lock.count < self.num_threads {
137            // We need a while loop to guard against spurious wakeups.
138            // https://en.wikipedia.org/wiki/Spurious_wakeup
139            while local_gen == lock.generation_id {
140                lock = self.cvar.wait(lock).unwrap();
141            }
142            BarrierWaitResult(false)
143        } else {
144            lock.count = 0;
145            lock.generation_id = lock.generation_id.wrapping_add(1);
146            self.cvar.notify_all();
147            BarrierWaitResult(true)
148        }
149    }
150
151    /// Blocks the current thread until all threads have rendezvoused here for
152    /// at most `timeout` duration.
153    pub(crate) fn wait_timeout(&self, timeout: Duration) -> Option<BarrierWaitResult> {
154        // This implementation mirrors `wait`, but with each blocking operation
155        // replaced by a timeout-amenable alternative.
156
157        let deadline = Instant::now() + timeout;
158
159        // Acquire `self.lock` with at most `timeout` duration.
160        let mut lock = loop {
161            if let Some(guard) = self.lock.try_lock() {
162                break guard;
163            } else if Instant::now() > deadline {
164                return None;
165            } else {
166                std::thread::yield_now();
167            }
168        };
169
170        // Shrink the `timeout` to account for the time taken to acquire `lock`.
171        let timeout = deadline.saturating_duration_since(Instant::now());
172
173        let local_gen = lock.generation_id;
174        lock.count += 1;
175        if lock.count < self.num_threads {
176            // We need a while loop to guard against spurious wakeups.
177            // https://en.wikipedia.org/wiki/Spurious_wakeup
178            while local_gen == lock.generation_id {
179                let (guard, timeout_result) = self.cvar.wait_timeout(lock, timeout).unwrap();
180                lock = guard;
181                if timeout_result.timed_out() {
182                    return None;
183                }
184            }
185            Some(BarrierWaitResult(false))
186        } else {
187            lock.count = 0;
188            lock.generation_id = lock.generation_id.wrapping_add(1);
189            self.cvar.notify_all();
190            Some(BarrierWaitResult(true))
191        }
192    }
193}
194
195impl fmt::Debug for BarrierWaitResult {
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        f.debug_struct("BarrierWaitResult")
198            .field("is_leader", &self.is_leader())
199            .finish()
200    }
201}
202
203impl BarrierWaitResult {
204    /// Returns `true` if this thread is the "leader thread" for the call to
205    /// [`Barrier::wait()`].
206    ///
207    /// Only one thread will have `true` returned from their result, all other
208    /// threads will have `false` returned.
209    ///
210    /// # Examples
211    ///
212    /// ```
213    /// use std::sync::Barrier;
214    ///
215    /// let barrier = Barrier::new(1);
216    /// let barrier_wait_result = barrier.wait();
217    /// println!("{:?}", barrier_wait_result.is_leader());
218    /// ```
219    #[must_use]
220    pub(crate) fn is_leader(&self) -> bool {
221        self.0
222    }
223}