alpha2_gpu/
acquisition.rs1#[must_use = "acquisitions are serialized only while the guard lives"]
17pub struct AcquisitionGuard {
18 #[cfg(all(feature = "serial-acquisition", not(target_arch = "wasm32")))]
19 _held: held::Held,
20}
21
22impl std::fmt::Debug for AcquisitionGuard {
23 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 formatter.write_str("AcquisitionGuard")
25 }
26}
27
28pub fn serialize_acquisition() -> AcquisitionGuard {
34 AcquisitionGuard {
35 #[cfg(all(feature = "serial-acquisition", not(target_arch = "wasm32")))]
36 _held: held::Held::take(),
37 }
38}
39
40#[cfg(all(feature = "serial-acquisition", not(target_arch = "wasm32")))]
41mod held {
42 use std::sync::{Condvar, Mutex, PoisonError};
43 use std::thread::ThreadId;
44
45 static TURN: Mutex<Option<(ThreadId, usize)>> = Mutex::new(None);
46 static FREED: Condvar = Condvar::new();
47
48 pub(super) struct Held;
49
50 impl Held {
51 pub(super) fn take() -> Self {
52 let me = std::thread::current().id();
53 let mut turn = TURN.lock().unwrap_or_else(PoisonError::into_inner);
54 loop {
55 match &mut *turn {
56 None => {
57 *turn = Some((me, 1));
58 break;
59 }
60 Some((owner, count)) if *owner == me => {
61 *count += 1;
62 return Self;
63 }
64 Some(_) => turn = FREED.wait(turn).unwrap_or_else(PoisonError::into_inner),
65 }
66 }
67 drop(turn);
68 crate::instance::keep(crate::product_backends()[0]);
70 Self
71 }
72 }
73
74 impl Drop for Held {
75 fn drop(&mut self) {
76 let mut turn = TURN.lock().unwrap_or_else(PoisonError::into_inner);
77 if let Some((_, count)) = &mut *turn {
78 *count -= 1;
79 if *count == 0 {
80 *turn = None;
81 FREED.notify_all();
82 }
83 }
84 }
85 }
86}
87
88#[cfg(all(test, feature = "serial-acquisition", not(target_arch = "wasm32")))]
89mod tests {
90 use std::sync::Arc;
91 use std::sync::atomic::{AtomicUsize, Ordering};
92
93 use super::serialize_acquisition;
94
95 #[test]
96 fn one_thread_holds_the_turn_at_a_time_and_may_take_it_again() {
97 let inside = Arc::new(AtomicUsize::new(0));
98 let threads: Vec<_> = (0..8)
99 .map(|_| {
100 let inside = Arc::clone(&inside);
101 std::thread::spawn(move || {
102 for _ in 0..50 {
103 let _outer = serialize_acquisition();
104 let _inner = serialize_acquisition();
105 assert_eq!(inside.fetch_add(1, Ordering::SeqCst), 0);
106 std::thread::yield_now();
107 inside.fetch_sub(1, Ordering::SeqCst);
108 }
109 })
110 })
111 .collect();
112 for thread in threads {
113 thread.join().expect("no two threads held the turn");
114 }
115 }
116}