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