1use std::sync::atomic::{AtomicUsize, Ordering};
15use std::sync::{Arc, Condvar, Mutex};
16use std::time::{Duration, Instant};
17
18use crate::error::{CoreError, CoreResult, ErrorContext, ErrorLocation};
19
20fn lock_err(context: &'static str, e: impl std::fmt::Display) -> CoreError {
23 CoreError::MutexError(
24 ErrorContext::new(format!("{context}: mutex poisoned: {e}"))
25 .with_location(ErrorLocation::new(file!(), line!())),
26 )
27}
28
29fn wait_err(context: &'static str, e: impl std::fmt::Display) -> CoreError {
30 CoreError::MutexError(
31 ErrorContext::new(format!("{context}: condvar wait poisoned: {e}"))
32 .with_location(ErrorLocation::new(file!(), line!())),
33 )
34}
35
36struct CyclicBarrierInner {
40 waiting: usize,
42 parties: usize,
44 generation: u64,
46 broken: bool,
48}
49
50pub struct CyclicBarrier {
72 inner: Mutex<CyclicBarrierInner>,
73 condvar: Condvar,
74}
75
76impl CyclicBarrier {
77 pub fn new(parties: usize) -> Self {
84 Self {
85 inner: Mutex::new(CyclicBarrierInner {
86 waiting: parties,
87 parties,
88 generation: 0,
89 broken: false,
90 }),
91 condvar: Condvar::new(),
92 }
93 }
94
95 pub fn wait(&self) -> CoreResult<bool> {
100 let mut g = self
101 .inner
102 .lock()
103 .map_err(|e| lock_err("CyclicBarrier::wait", e))?;
104
105 if g.broken {
106 return Err(CoreError::MutexError(ErrorContext::new(
107 "CyclicBarrier: barrier is broken",
108 )));
109 }
110
111 let gen = g.generation;
112 g.waiting -= 1;
113
114 if g.waiting == 0 {
115 g.waiting = g.parties;
117 g.generation = gen.wrapping_add(1);
118 self.condvar.notify_all();
119 return Ok(true);
120 }
121
122 loop {
124 g = self
125 .condvar
126 .wait(g)
127 .map_err(|e| wait_err("CyclicBarrier::wait", e))?;
128 if g.broken {
129 return Err(CoreError::MutexError(ErrorContext::new(
130 "CyclicBarrier: barrier broken while waiting",
131 )));
132 }
133 if g.generation != gen {
134 return Ok(false);
135 }
136 }
137 }
138
139 pub fn wait_timeout(&self, timeout: Duration) -> CoreResult<bool> {
141 let deadline = Instant::now() + timeout;
142 let mut g = self
143 .inner
144 .lock()
145 .map_err(|e| lock_err("CyclicBarrier::wait_timeout", e))?;
146
147 if g.broken {
148 return Err(CoreError::MutexError(ErrorContext::new(
149 "CyclicBarrier: barrier is broken",
150 )));
151 }
152
153 let gen = g.generation;
154 g.waiting -= 1;
155
156 if g.waiting == 0 {
157 g.waiting = g.parties;
158 g.generation = gen.wrapping_add(1);
159 self.condvar.notify_all();
160 return Ok(true);
161 }
162
163 loop {
164 let remaining = deadline.saturating_duration_since(Instant::now());
165 if remaining.is_zero() {
166 g.broken = true;
168 self.condvar.notify_all();
169 return Err(CoreError::TimeoutError(ErrorContext::new(
170 "CyclicBarrier: timed out waiting for all parties",
171 )));
172 }
173 let (next_g, _timeout_result) = self
174 .condvar
175 .wait_timeout(g, remaining)
176 .map_err(|e| wait_err("CyclicBarrier::wait_timeout", e))?;
177 g = next_g;
178 if g.broken {
179 return Err(CoreError::MutexError(ErrorContext::new(
180 "CyclicBarrier: barrier broken while waiting",
181 )));
182 }
183 if g.generation != gen {
184 return Ok(false);
185 }
186 }
187 }
188
189 pub fn break_barrier(&self) {
191 if let Ok(mut g) = self.inner.lock() {
192 g.broken = true;
193 self.condvar.notify_all();
194 }
195 }
196
197 pub fn reset(&self) {
199 if let Ok(mut g) = self.inner.lock() {
200 g.waiting = g.parties;
201 g.broken = false;
202 g.generation = g.generation.wrapping_add(1);
203 self.condvar.notify_all();
204 }
205 }
206
207 pub fn is_broken(&self) -> bool {
209 self.inner.lock().map(|g| g.broken).unwrap_or(true)
210 }
211
212 pub fn waiting(&self) -> usize {
214 self.inner.lock().map(|g| g.waiting).unwrap_or(0)
215 }
216
217 pub fn parties(&self) -> usize {
219 self.inner.lock().map(|g| g.parties).unwrap_or(0)
220 }
221}
222
223struct PhaseBarrierInner {
227 phase: u64,
228 waiting: usize,
229 parties: usize,
230}
231
232pub struct PhaseBarrier {
252 inner: Mutex<PhaseBarrierInner>,
253 condvar: Condvar,
254}
255
256impl PhaseBarrier {
257 pub fn new(parties: usize) -> Self {
259 Self {
260 inner: Mutex::new(PhaseBarrierInner {
261 phase: 0,
262 waiting: parties,
263 parties,
264 }),
265 condvar: Condvar::new(),
266 }
267 }
268
269 pub fn arrive_and_wait(&self) -> CoreResult<u64> {
273 let mut g = self
274 .inner
275 .lock()
276 .map_err(|e| lock_err("PhaseBarrier::arrive_and_wait", e))?;
277
278 let current_phase = g.phase;
279 g.waiting -= 1;
280
281 if g.waiting == 0 {
282 g.phase = current_phase.wrapping_add(1);
284 g.waiting = g.parties;
285 self.condvar.notify_all();
286 return Ok(current_phase);
287 }
288
289 loop {
290 g = self
291 .condvar
292 .wait(g)
293 .map_err(|e| wait_err("PhaseBarrier::arrive_and_wait", e))?;
294 if g.phase != current_phase {
295 return Ok(current_phase);
296 }
297 }
298 }
299
300 pub fn arrive(&self) -> CoreResult<u64> {
305 let mut g = self
306 .inner
307 .lock()
308 .map_err(|e| lock_err("PhaseBarrier::arrive", e))?;
309
310 g.waiting -= 1;
311 if g.waiting == 0 {
312 let completed = g.phase;
313 g.phase = completed.wrapping_add(1);
314 g.waiting = g.parties;
315 self.condvar.notify_all();
316 Ok(completed)
317 } else {
318 Ok(g.phase)
319 }
320 }
321
322 pub fn phase(&self) -> u64 {
324 self.inner.lock().map(|g| g.phase).unwrap_or(0)
325 }
326
327 pub fn waiting(&self) -> usize {
329 self.inner.lock().map(|g| g.waiting).unwrap_or(0)
330 }
331}
332
333pub struct CountDownLatch {
360 inner: Mutex<usize>,
361 condvar: Condvar,
362}
363
364impl CountDownLatch {
365 pub fn new(n: usize) -> Self {
367 Self {
368 inner: Mutex::new(n),
369 condvar: Condvar::new(),
370 }
371 }
372
373 pub fn count_down(&self) {
375 if let Ok(mut g) = self.inner.lock() {
376 if *g > 0 {
377 *g -= 1;
378 if *g == 0 {
379 self.condvar.notify_all();
380 }
381 }
382 }
383 }
384
385 pub fn wait(&self) -> CoreResult<()> {
387 let mut g = self
388 .inner
389 .lock()
390 .map_err(|e| lock_err("CountDownLatch::wait", e))?;
391 loop {
392 if *g == 0 {
393 return Ok(());
394 }
395 g = self
396 .condvar
397 .wait(g)
398 .map_err(|e| wait_err("CountDownLatch::wait", e))?;
399 }
400 }
401
402 pub fn wait_timeout(&self, timeout: Duration) -> CoreResult<bool> {
406 let deadline = Instant::now() + timeout;
407 let mut g = self
408 .inner
409 .lock()
410 .map_err(|e| lock_err("CountDownLatch::wait_timeout", e))?;
411 loop {
412 if *g == 0 {
413 return Ok(true);
414 }
415 let remaining = deadline.saturating_duration_since(Instant::now());
416 if remaining.is_zero() {
417 return Ok(false);
418 }
419 let (next_g, _) = self
420 .condvar
421 .wait_timeout(g, remaining)
422 .map_err(|e| wait_err("CountDownLatch::wait_timeout", e))?;
423 g = next_g;
424 }
425 }
426
427 pub fn count(&self) -> usize {
429 self.inner.lock().map(|g| *g).unwrap_or(0)
430 }
431
432 pub fn is_open(&self) -> bool {
434 self.count() == 0
435 }
436}
437
438pub struct SpinBarrier {
459 arrived: AtomicUsize,
461 epoch: AtomicUsize,
463 parties: usize,
464}
465
466impl SpinBarrier {
467 pub fn new(parties: usize) -> Self {
469 let parties = parties.max(1);
470 Self {
471 arrived: AtomicUsize::new(0),
472 epoch: AtomicUsize::new(0),
473 parties,
474 }
475 }
476
477 pub fn wait(&self) {
482 let current_epoch = self.epoch.load(Ordering::Acquire);
483 let prev = self.arrived.fetch_add(1, Ordering::AcqRel);
484 let new_count = prev + 1;
485
486 if new_count == self.parties {
487 self.arrived.store(0, Ordering::Release);
489 self.epoch.fetch_add(1, Ordering::Release);
490 } else {
491 let mut spins = 0usize;
493 loop {
494 let e = self.epoch.load(Ordering::Acquire);
495 if e != current_epoch {
496 break;
497 }
498 if spins < 32 {
499 std::hint::spin_loop();
500 } else {
501 std::thread::yield_now();
502 }
503 spins = spins.saturating_add(1);
504 }
505 }
506 }
507
508 pub fn parties(&self) -> usize {
510 self.parties
511 }
512
513 pub fn epoch(&self) -> usize {
515 self.epoch.load(Ordering::Relaxed)
516 }
517}
518
519#[cfg(test)]
522mod tests {
523 use super::*;
524 use std::sync::atomic::{AtomicU64, Ordering as AO};
525 use std::thread;
526
527 #[test]
530 fn cyclic_barrier_all_proceed() {
531 const N: usize = 5;
532 let barrier = Arc::new(CyclicBarrier::new(N));
533 let counter = Arc::new(AtomicU64::new(0));
534 let mut handles = Vec::new();
535
536 for _ in 0..N {
537 let b = Arc::clone(&barrier);
538 let c = Arc::clone(&counter);
539 handles.push(thread::spawn(move || {
540 c.fetch_add(1, AO::Relaxed);
541 b.wait().expect("barrier wait");
542 }));
543 }
544 for h in handles {
545 h.join().expect("thread");
546 }
547 assert_eq!(counter.load(AO::Relaxed), N as u64);
548 }
549
550 #[test]
551 fn cyclic_barrier_trip_thread_count() {
552 const N: usize = 4;
553 let barrier = Arc::new(CyclicBarrier::new(N));
554 let trips = Arc::new(AtomicU64::new(0));
555 let mut handles = Vec::new();
556
557 for _ in 0..N {
558 let b = Arc::clone(&barrier);
559 let t = Arc::clone(&trips);
560 handles.push(thread::spawn(move || {
561 let trip = b.wait().expect("wait");
562 if trip {
563 t.fetch_add(1, AO::Relaxed);
564 }
565 }));
566 }
567 for h in handles {
568 h.join().expect("thread");
569 }
570 assert_eq!(trips.load(AO::Relaxed), 1, "exactly one trip thread");
571 }
572
573 #[test]
574 fn cyclic_barrier_two_cycles() {
575 const N: usize = 3;
576 let barrier = Arc::new(CyclicBarrier::new(N));
577 let phase_counter = Arc::new(AtomicU64::new(0));
578 let mut handles = Vec::new();
579
580 for _ in 0..N {
581 let b = Arc::clone(&barrier);
582 let p = Arc::clone(&phase_counter);
583 handles.push(thread::spawn(move || {
584 b.wait().expect("phase 1 wait");
586 p.fetch_add(1, AO::Relaxed);
587 b.wait().expect("phase 2 wait");
589 p.fetch_add(1, AO::Relaxed);
590 }));
591 }
592 for h in handles {
593 h.join().expect("thread");
594 }
595 assert_eq!(phase_counter.load(AO::Relaxed), (N * 2) as u64);
596 }
597
598 #[test]
601 fn phase_barrier_advances_phase() {
602 const N: usize = 4;
603 let pb = Arc::new(PhaseBarrier::new(N));
604 let mut handles = Vec::new();
605 for _ in 0..N {
606 let p = Arc::clone(&pb);
607 handles.push(thread::spawn(move || {
608 p.arrive_and_wait().expect("arrive phase 1");
609 p.arrive_and_wait().expect("arrive phase 2");
610 }));
611 }
612 for h in handles {
613 h.join().expect("thread");
614 }
615 assert_eq!(pb.phase(), 2);
616 }
617
618 #[test]
621 fn countdown_latch_basic() {
622 const N: usize = 5;
623 let latch = Arc::new(CountDownLatch::new(N));
624 let counter = Arc::new(AtomicU64::new(0));
625 let mut handles = Vec::new();
626
627 for _ in 0..N {
628 let l = Arc::clone(&latch);
629 let c = Arc::clone(&counter);
630 handles.push(thread::spawn(move || {
631 c.fetch_add(1, AO::Relaxed);
632 l.count_down();
633 }));
634 }
635
636 latch.wait().expect("latch wait");
637 assert!(latch.is_open());
638 assert_eq!(counter.load(AO::Relaxed), N as u64);
639
640 for h in handles {
641 h.join().expect("thread");
642 }
643 }
644
645 #[test]
646 fn countdown_latch_already_open() {
647 let latch = CountDownLatch::new(0);
648 assert!(latch.is_open());
649 latch.wait().expect("already open wait");
650 }
651
652 #[test]
653 fn countdown_latch_timeout_opens() {
654 let latch = Arc::new(CountDownLatch::new(1));
655 let l2 = Arc::clone(&latch);
656 thread::spawn(move || {
657 thread::sleep(Duration::from_millis(20));
658 l2.count_down();
659 });
660 let opened = latch
661 .wait_timeout(Duration::from_secs(5))
662 .expect("wait_timeout");
663 assert!(opened);
664 }
665
666 #[test]
667 fn countdown_latch_timeout_expires() {
668 let latch = CountDownLatch::new(1); let opened = latch
670 .wait_timeout(Duration::from_millis(10))
671 .expect("wait_timeout");
672 assert!(!opened);
673 }
674
675 #[test]
678 fn spin_barrier_basic() {
679 const N: usize = 4;
680 let b = Arc::new(SpinBarrier::new(N));
681 let counter = Arc::new(AtomicU64::new(0));
682 let mut handles = Vec::new();
683
684 for _ in 0..N {
685 let bar = Arc::clone(&b);
686 let c = Arc::clone(&counter);
687 handles.push(thread::spawn(move || {
688 bar.wait();
689 c.fetch_add(1, AO::Relaxed);
690 }));
691 }
692 for h in handles {
693 h.join().expect("thread");
694 }
695 assert_eq!(counter.load(AO::Relaxed), N as u64);
696 assert_eq!(b.epoch(), 1);
697 }
698
699 #[test]
700 fn spin_barrier_multiple_epochs() {
701 const N: usize = 3;
702 let b = Arc::new(SpinBarrier::new(N));
703 let mut handles = Vec::new();
704
705 for _ in 0..N {
706 let bar = Arc::clone(&b);
707 handles.push(thread::spawn(move || {
708 bar.wait(); bar.wait(); bar.wait(); }));
712 }
713 for h in handles {
714 h.join().expect("thread");
715 }
716 assert_eq!(b.epoch(), 3);
717 }
718}