agave_wake_channel/
lib.rs1#![cfg(feature = "agave-unstable-api")]
24
25#[cfg(feature = "shuttle-test")]
26use shuttle::sync::atomic::AtomicUsize;
27#[cfg(not(feature = "shuttle-test"))]
28use std::sync::atomic::AtomicUsize;
29use {
30 crossbeam_channel::{RecvError, SendError, TryRecvError},
31 crossbeam_utils::Backoff,
32 std::{
33 mem,
34 sync::{
35 Arc,
36 atomic::{AtomicU32, Ordering, fence},
37 },
38 },
39};
40
41#[derive(Default)]
45struct WakeEvent {
46 cookie: AtomicU32,
48 waiters: AtomicUsize,
50}
51
52impl WakeEvent {
53 fn register_waiter(&self) -> WakeWaiter<'_> {
55 let cookie = self.cookie.load(Ordering::Relaxed);
56 self.waiters.fetch_add(1, Ordering::Relaxed);
57 fence(Ordering::SeqCst);
59 WakeWaiter {
60 event: self,
61 cookie,
62 }
63 }
64
65 fn wake_one(&self) {
66 fence(Ordering::SeqCst);
68 if self.waiters.load(Ordering::Relaxed) != 0 {
69 self.cookie.fetch_add(1, Ordering::Relaxed);
70 atomic_wait::wake_one(&self.cookie);
71 }
72 }
73
74 fn wake_all(&self) {
75 fence(Ordering::SeqCst);
77 if self.waiters.load(Ordering::Relaxed) != 0 {
78 self.cookie.fetch_add(1, Ordering::Relaxed);
79 atomic_wait::wake_all(&self.cookie);
80 }
81 }
82}
83
84struct WakeWaiter<'a> {
88 event: &'a WakeEvent,
89 cookie: u32,
90}
91
92impl WakeWaiter<'_> {
93 fn wait(self) {
94 if self.event.cookie.load(Ordering::Relaxed) == self.cookie {
95 atomic_wait::wait(&self.event.cookie, self.cookie);
96 }
97 }
98}
99
100impl Drop for WakeWaiter<'_> {
101 fn drop(&mut self) {
102 self.event.waiters.fetch_sub(1, Ordering::Relaxed);
103 }
104}
105
106struct Shared {
107 wake_event: WakeEvent,
108 num_senders: AtomicUsize,
112}
113
114pub struct Sender<T> {
117 inner: crossbeam_channel::Sender<T>,
118 shared: Arc<Shared>,
119}
120
121impl<T> Sender<T> {
122 pub fn send(&self, value: T) -> Result<(), SendError<T>> {
123 self.inner.send(value)?;
124 self.shared.wake_event.wake_one();
125 Ok(())
126 }
127}
128
129impl<T> Clone for Sender<T> {
130 fn clone(&self) -> Self {
131 self.shared.num_senders.fetch_add(1, Ordering::Relaxed);
132 Self {
133 inner: self.inner.clone(),
134 shared: Arc::clone(&self.shared),
135 }
136 }
137}
138
139impl<T> Drop for Sender<T> {
140 fn drop(&mut self) {
141 let (replacement, _) = crossbeam_channel::bounded(0);
142 drop(mem::replace(&mut self.inner, replacement));
143 if self.shared.num_senders.fetch_sub(1, Ordering::AcqRel) == 1 {
144 self.shared.wake_event.wake_all();
145 }
146 }
147}
148
149pub struct Receiver<T> {
152 inner: crossbeam_channel::Receiver<T>,
153 shared: Arc<Shared>,
154}
155
156impl<T> Receiver<T> {
157 pub fn recv(&self) -> Result<T, RecvError> {
158 loop {
159 let backoff = Backoff::new();
160 loop {
161 match self.inner.try_recv() {
162 Ok(value) => return Ok(value),
163 Err(TryRecvError::Disconnected) => return Err(RecvError),
164 Err(TryRecvError::Empty) if backoff.is_completed() => break,
165 Err(TryRecvError::Empty) => backoff.snooze(),
166 }
167 }
168
169 let waiter = self.shared.wake_event.register_waiter();
180 match self.inner.try_recv() {
181 Ok(value) => return Ok(value),
182 Err(TryRecvError::Disconnected) => return Err(RecvError),
183 Err(TryRecvError::Empty) => waiter.wait(),
184 }
185 }
186 }
187}
188
189impl<T> Clone for Receiver<T> {
190 fn clone(&self) -> Self {
191 Self {
192 inner: self.inner.clone(),
193 shared: Arc::clone(&self.shared),
194 }
195 }
196}
197
198pub fn bounded<T>(capacity: usize) -> (Sender<T>, Receiver<T>) {
199 assert_ne!(capacity, 0, "channel capacity must be nonzero");
200 let (sender, receiver) = crossbeam_channel::bounded(capacity);
201 let shared = Arc::new(Shared {
202 wake_event: WakeEvent::default(),
203 num_senders: AtomicUsize::new(1),
204 });
205 (
206 Sender {
207 inner: sender,
208 shared: Arc::clone(&shared),
209 },
210 Receiver {
211 inner: receiver,
212 shared,
213 },
214 )
215}
216
217#[cfg(all(test, not(feature = "shuttle-test")))]
218mod tests {
219 use {
220 super::*,
221 std::{sync::Barrier, thread},
222 };
223
224 #[test]
225 fn test_wake_before_wait() {
226 let event = WakeEvent::default();
227 let waiter = event.register_waiter();
228 event.wake_one();
229 waiter.wait();
230 }
231
232 #[test]
233 fn test_wake_receivers_and_disconnect() {
234 const NUM_RECEIVERS: usize = 4;
235
236 let (sender, receiver) = bounded(NUM_RECEIVERS);
237 let sender1 = sender.clone();
238 drop(sender);
239 let barrier = Arc::new(Barrier::new(NUM_RECEIVERS + 1));
240 let handles = (0..NUM_RECEIVERS)
241 .map(|_| {
242 let receiver = receiver.clone();
243 let barrier = Arc::clone(&barrier);
244 thread::spawn(move || {
245 receiver.recv().unwrap();
246 barrier.wait();
247 assert!(receiver.recv().is_err());
248 })
249 })
250 .collect::<Vec<_>>();
251
252 while receiver.shared.wake_event.waiters.load(Ordering::Relaxed) != NUM_RECEIVERS {
253 thread::yield_now();
254 }
255 for _ in 0..NUM_RECEIVERS {
256 sender1.send(()).unwrap();
257 }
258 barrier.wait();
259 drop(sender1);
260
261 for handle in handles {
262 handle.join().unwrap();
263 }
264 }
265}
266
267#[cfg(all(test, feature = "shuttle-test"))]
268mod shuttle_tests {
269 use {super::*, shuttle::thread};
270
271 #[test]
272 fn test_disconnect_is_visible_before_wake() {
273 shuttle::check_dfs(
274 || {
275 let (sender, receiver) = bounded::<()>(1);
276 let sender1 = sender.clone();
277 let observer_receiver = receiver.clone();
278 let _waiter = receiver.shared.wake_event.register_waiter();
279
280 let sender_drop = thread::spawn(move || drop(sender));
281 let sender1_drop = thread::spawn(move || drop(sender1));
282 let observer = thread::spawn(move || {
283 if observer_receiver
286 .shared
287 .wake_event
288 .cookie
289 .load(Ordering::Relaxed)
290 != 0
291 {
292 assert_eq!(
293 observer_receiver.inner.try_recv(),
294 Err(TryRecvError::Disconnected),
295 );
296 }
297 });
298
299 sender_drop.join().unwrap();
300 sender1_drop.join().unwrap();
301 observer.join().unwrap();
302 assert_ne!(receiver.shared.wake_event.cookie.load(Ordering::Relaxed), 0);
303 assert_eq!(receiver.inner.try_recv(), Err(TryRecvError::Disconnected));
304 },
305 None,
306 );
307 }
308}