1#![allow(unsafe_code)]
15
16use core::fmt;
17use core::future::Future;
18use core::pin::Pin;
19use core::task::{Context, Poll};
20
21use super::internal::{AtomicU64, Ordering, UnsafeCell};
22use super::list::List;
23use super::queue::{Arrivals, Policy, QUEUED, Step, WaitQueue};
24use super::waiter::{Cancel, Wait, WakeList, release_queued};
25
26struct Waiting {
27 list: List,
28 generation: usize,
30}
31
32pub struct Barrier {
56 n: u64,
58 arrivals: AtomicU64,
60 queue: WaitQueue,
61 waiting: UnsafeCell<Waiting>,
62}
63
64unsafe impl Send for Barrier {}
66unsafe impl Sync for Barrier {}
68
69impl Barrier {
70 loom_const_fn! {
71 pub fn new(n: usize) -> Self {
74 Self {
75 n: if n == 0 { 1 } else { n as u64 },
76 arrivals: AtomicU64::new(0),
77 queue: WaitQueue::new(0),
78 waiting: UnsafeCell::new(Waiting { list: List::new(), generation: 0 }),
79 }
80 }
81 }
82
83 pub fn wait(&self) -> BarrierWait<'_> {
85 BarrierWait {
86 barrier: self,
87 generation: None,
88 wait: Wait::new(),
89 }
90 }
91
92 fn arrive(&self) -> Result<(), usize> {
95 let arrival = self.arrivals.fetch_add(1, Ordering::AcqRel);
96 if arrival % self.n == self.n - 1 {
97 Ok(())
98 } else {
99 Err(self.generation_of(arrival))
100 }
101 }
102
103 fn generation_of(&self, arrival: u64) -> usize {
106 (arrival / self.n) as usize
107 }
108}
109
110impl Policy for Barrier {
111 fn queue(&self) -> &WaitQueue {
112 &self.queue
113 }
114
115 fn pass(&self, arrivals: Arrivals, woken: &mut WakeList<'_>, _wake_front: bool) -> usize {
116 let pool = &self.queue;
117 let current = self.generation_of(self.arrivals.load(Ordering::Acquire));
118 self.waiting.with_mut(|waiting| unsafe {
120 let waiting = &mut *waiting;
121 if waiting.generation != current {
122 while let Some(node) = waiting.list.pop_front() {
123 woken.grant(node);
124 }
125 waiting.generation = current;
126 }
127 for node in arrivals {
128 if node.as_ref().is_cancelled() {
129 release_queued(node, pool);
130 } else if node.as_ref().payload() != current {
131 woken.grant(node);
132 } else {
133 waiting.list.push_back(node);
134 }
135 }
136 waiting.list.tidy(&self.queue);
137 if waiting.list.front(pool).is_none() {
138 QUEUED
139 } else {
140 0
141 }
142 })
143 }
144}
145
146impl Drop for Barrier {
147 fn drop(&mut self) {
148 let pool = &self.queue;
149 self.waiting
151 .with_mut(|waiting| unsafe { (*waiting).list.clear(pool) });
152 }
153}
154
155impl fmt::Debug for Barrier {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 f.debug_struct("Barrier")
158 .field("n", &self.n)
159 .finish_non_exhaustive()
160 }
161}
162
163#[derive(Clone, Copy, Debug, PartialEq, Eq)]
165pub struct BarrierWaitResult {
166 leader: bool,
167}
168
169impl BarrierWaitResult {
170 pub fn is_leader(&self) -> bool {
173 self.leader
174 }
175}
176
177#[must_use = "futures do nothing unless polled"]
179pub struct BarrierWait<'a> {
180 barrier: &'a Barrier,
181 generation: Option<usize>,
183 wait: Wait,
184}
185
186impl Future for BarrierWait<'_> {
187 type Output = BarrierWaitResult;
188
189 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<BarrierWaitResult> {
190 let this = self.get_mut();
191 let barrier = this.barrier;
192 if this.generation.is_none() {
193 match barrier.arrive() {
194 Ok(()) => {
195 barrier.transition(|state| {
196 if state & QUEUED != 0 {
197 Step::Drain(state)
198 } else {
199 Step::Set(state)
200 }
201 });
202 return Poll::Ready(BarrierWaitResult { leader: true });
203 }
204 Err(generation) => {
205 this.generation = Some(generation);
206 barrier.enqueue(&mut this.wait, generation, cx.waker());
207 return this
208 .wait
209 .poll(&barrier.queue, None)
210 .map(|_| BarrierWaitResult { leader: false });
211 }
212 }
213 }
214 this.wait
215 .poll(&barrier.queue, Some(cx.waker()))
216 .map(|_| BarrierWaitResult { leader: false })
217 }
218}
219
220impl Drop for BarrierWait<'_> {
221 fn drop(&mut self) {
222 if let Cancel::Withdrawn(_) = self.wait.cancel(&self.barrier.queue) {
224 self.barrier.withdrawn(false);
225 }
226 }
227}
228
229impl fmt::Debug for BarrierWait<'_> {
230 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231 f.debug_struct("BarrierWait")
232 .field("generation", &self.generation)
233 .finish_non_exhaustive()
234 }
235}
236
237#[cfg(all(test, not(loom)))]
238mod tests {
239 use super::*;
240 use crate::sync::test_support::{Polled, block_on};
241 use std::sync::Arc;
242 use std::sync::atomic::{AtomicUsize, Ordering as StdOrdering};
243
244 #[test]
245 fn each_generation_releases_together_with_one_leader() {
246 let barrier = Barrier::new(3);
247 for _ in 0..3 {
248 let mut a = Polled::new(barrier.wait());
249 a.pending();
250 let mut b = Polled::new(barrier.wait());
251 b.pending();
252 let leader = Polled::new(barrier.wait()).ready();
253 assert!(leader.is_leader());
254 assert_eq!((a.wakes(), b.wakes()), (1, 1));
255 assert!(!a.ready().is_leader());
256 assert!(!b.ready().is_leader());
257 }
258 }
259
260 #[test]
261 fn a_dropped_wait_stays_counted() {
262 let barrier = Barrier::new(2);
263 Polled::new(barrier.wait()).pending();
264 let leader = Polled::new(barrier.wait()).ready();
265 assert!(leader.is_leader(), "the dropped arrival completed the pair");
266 let mut next = Polled::new(barrier.wait());
267 next.pending();
268 assert!(Polled::new(barrier.wait()).ready().is_leader());
269 assert!(!next.ready().is_leader());
270 }
271
272 #[test]
273 fn a_barrier_of_nothing_acts_as_one() {
274 let barrier = Barrier::new(0);
275 for _ in 0..3 {
276 assert!(Polled::new(barrier.wait()).ready().is_leader());
277 }
278 }
279
280 #[test]
281 #[cfg(target_pointer_width = "64")]
282 fn a_group_larger_than_u32_max_is_a_group_all_the_same() {
283 let barrier = Barrier::new(u32::MAX as usize + 2);
286 let mut first = Polled::new(barrier.wait());
287 first.pending();
288 assert_eq!(barrier.n, u64::from(u32::MAX) + 2);
289 }
290
291 #[test]
292 fn threads_meet_at_every_generation() {
293 const ROUNDS: usize = if cfg!(miri) { 3 } else { 200 };
294 let barrier = Arc::new(Barrier::new(4));
295 let leaders = Arc::new(AtomicUsize::new(0));
296 let threads: Vec<_> = (0..4)
297 .map(|_| {
298 let (barrier, leaders) = (Arc::clone(&barrier), Arc::clone(&leaders));
299 std::thread::spawn(move || {
300 for _ in 0..ROUNDS {
301 if block_on(barrier.wait()).is_leader() {
302 leaders.fetch_add(1, StdOrdering::SeqCst);
303 }
304 }
305 })
306 })
307 .collect();
308 for thread in threads {
309 thread.join().expect("worker");
310 }
311 assert_eq!(leaders.load(StdOrdering::SeqCst), ROUNDS);
312 }
313}