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