std/sync/
barrier.rs

1use crate::fmt;
2use crate::sync::nonpoison::{Condvar, Mutex};
3
4/// A barrier enables multiple threads to synchronize the beginning
5/// of some computation.
6///
7/// # Examples
8///
9/// ```
10/// use std::sync::Barrier;
11/// use std::thread;
12///
13/// let n = 10;
14/// let barrier = Barrier::new(n);
15/// thread::scope(|s| {
16///     for _ in 0..n {
17///         // The same messages will be printed together.
18///         // You will NOT see any interleaving.
19///         s.spawn(|| {
20///             println!("before wait");
21///             barrier.wait();
22///             println!("after wait");
23///         });
24///     }
25/// });
26/// ```
27#[stable(feature = "rust1", since = "1.0.0")]
28pub struct Barrier {
29    lock: Mutex<BarrierState>,
30    cvar: Condvar,
31    num_threads: usize,
32}
33
34// The inner state of a double barrier
35struct BarrierState {
36    count: usize,
37    generation_id: usize,
38}
39
40/// A `BarrierWaitResult` is returned by [`Barrier::wait()`] when all threads
41/// in the [`Barrier`] have rendezvoused.
42///
43/// # Examples
44///
45/// ```
46/// use std::sync::Barrier;
47///
48/// let barrier = Barrier::new(1);
49/// let barrier_wait_result = barrier.wait();
50/// ```
51#[stable(feature = "rust1", since = "1.0.0")]
52pub struct BarrierWaitResult(bool);
53
54#[stable(feature = "std_debug", since = "1.16.0")]
55impl fmt::Debug for Barrier {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        f.debug_struct("Barrier").finish_non_exhaustive()
58    }
59}
60
61impl Barrier {
62    /// Creates a new barrier that can block a given number of threads.
63    ///
64    /// A barrier will block `n`-1 threads which call [`wait()`] and then wake
65    /// up all threads at once when the `n`th thread calls [`wait()`].
66    ///
67    /// [`wait()`]: Barrier::wait
68    ///
69    /// # Examples
70    ///
71    /// ```
72    /// use std::sync::Barrier;
73    ///
74    /// let barrier = Barrier::new(10);
75    /// ```
76    #[stable(feature = "rust1", since = "1.0.0")]
77    #[rustc_const_stable(feature = "const_barrier", since = "1.78.0")]
78    #[must_use]
79    #[inline]
80    pub const fn new(n: usize) -> Barrier {
81        Barrier {
82            lock: Mutex::new(BarrierState { count: 0, generation_id: 0 }),
83            cvar: Condvar::new(),
84            num_threads: n,
85        }
86    }
87
88    /// Blocks the current thread until all threads have rendezvoused here.
89    ///
90    /// Barriers are re-usable after all threads have rendezvoused once, and can
91    /// be used continuously.
92    ///
93    /// A single (arbitrary) thread will receive a [`BarrierWaitResult`] that
94    /// returns `true` from [`BarrierWaitResult::is_leader()`] when returning
95    /// from this function, and all other threads will receive a result that
96    /// will return `false` from [`BarrierWaitResult::is_leader()`].
97    ///
98    /// # Examples
99    ///
100    /// ```
101    /// use std::sync::Barrier;
102    /// use std::thread;
103    ///
104    /// let n = 10;
105    /// let barrier = Barrier::new(n);
106    /// thread::scope(|s| {
107    ///     for _ in 0..n {
108    ///         // The same messages will be printed together.
109    ///         // You will NOT see any interleaving.
110    ///         s.spawn(|| {
111    ///             println!("before wait");
112    ///             barrier.wait();
113    ///             println!("after wait");
114    ///         });
115    ///     }
116    /// });
117    /// ```
118    #[stable(feature = "rust1", since = "1.0.0")]
119    pub fn wait(&self) -> BarrierWaitResult {
120        let mut lock = self.lock.lock();
121        let local_gen = lock.generation_id;
122        lock.count += 1;
123        if lock.count < self.num_threads {
124            let _guard = self.cvar.wait_while(lock, |state| local_gen == state.generation_id);
125            BarrierWaitResult(false)
126        } else {
127            lock.count = 0;
128            lock.generation_id = lock.generation_id.wrapping_add(1);
129            self.cvar.notify_all();
130            BarrierWaitResult(true)
131        }
132    }
133}
134
135#[stable(feature = "std_debug", since = "1.16.0")]
136impl fmt::Debug for BarrierWaitResult {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        f.debug_struct("BarrierWaitResult").field("is_leader", &self.is_leader()).finish()
139    }
140}
141
142impl BarrierWaitResult {
143    /// Returns `true` if this thread is the "leader thread" for the call to
144    /// [`Barrier::wait()`].
145    ///
146    /// Only one thread will have `true` returned from their result, all other
147    /// threads will have `false` returned.
148    ///
149    /// # Examples
150    ///
151    /// ```
152    /// use std::sync::Barrier;
153    ///
154    /// let barrier = Barrier::new(1);
155    /// let barrier_wait_result = barrier.wait();
156    /// println!("{:?}", barrier_wait_result.is_leader());
157    /// ```
158    #[stable(feature = "rust1", since = "1.0.0")]
159    #[must_use]
160    pub fn is_leader(&self) -> bool {
161        self.0
162    }
163}