1use std::{
2 fmt::{Debug, Display, Formatter},
3 future::Future,
4 mem::replace,
5 pin::Pin,
6};
7#[cfg(feature = "hanging_detection")]
8use std::{
9 sync::Arc,
10 task::{Poll, ready},
11 time::Duration,
12};
13
14#[cfg(feature = "hanging_detection")]
15use tokio::time::{Timeout, timeout};
16
17fn block_on_listener(listener: event_listener::EventListener) {
24 #[cfg(not(target_family = "wasm"))]
25 {
26 use event_listener::Listener as _;
27
28 listener.wait();
29 }
30 #[cfg(target_family = "wasm")]
31 futures::executor::block_on(listener);
32}
33
34pub trait EventDescriptor {
35 #[cfg(feature = "hanging_detection")]
36 fn get_description(self) -> Arc<dyn Fn() -> String + Sync + Send>;
37}
38
39impl<T, InnerFn> EventDescriptor for T
40where
41 T: FnOnce() -> InnerFn,
42 InnerFn: Fn() -> String + Sync + Send + 'static,
43{
44 #[cfg(feature = "hanging_detection")]
45 fn get_description(self) -> Arc<dyn Fn() -> String + Sync + Send> {
46 Arc::new((self)())
47 }
48}
49
50#[derive(Clone)]
51pub struct EventDescription {
52 #[cfg(feature = "hanging_detection")]
53 description: Arc<dyn Fn() -> String + Sync + Send>,
54}
55
56impl EventDescription {
57 #[inline(always)]
58 pub fn new<InnerFn>(#[allow(unused_variables)] description: impl FnOnce() -> InnerFn) -> Self
59 where
60 InnerFn: Fn() -> String + Sync + Send + 'static,
61 {
62 Self {
63 #[cfg(feature = "hanging_detection")]
64 description: Arc::new((description)()),
65 }
66 }
67}
68
69impl Display for EventDescription {
70 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
71 #[cfg(not(feature = "hanging_detection"))]
72 return write!(f, "");
73
74 #[cfg(feature = "hanging_detection")]
75 return write!(f, "{}", (self.description)());
76 }
77}
78
79impl EventDescriptor for EventDescription {
80 #[cfg(feature = "hanging_detection")]
81 fn get_description(self) -> Arc<dyn Fn() -> String + Sync + Send> {
82 self.description
83 }
84}
85
86pub struct Event {
87 #[cfg(feature = "hanging_detection")]
88 description: Arc<dyn Fn() -> String + Sync + Send>,
89 event: event_listener::Event,
90}
91
92impl Event {
93 #[inline(always)]
107 pub fn new(#[allow(unused_variables)] description: impl EventDescriptor) -> Self {
108 #[cfg(not(feature = "hanging_detection"))]
109 return Self {
110 event: event_listener::Event::new(),
111 };
112 #[cfg(feature = "hanging_detection")]
113 return Self {
114 description: description.get_description(),
115 event: event_listener::Event::new(),
116 };
117 }
118
119 pub fn listen(&self) -> EventListener {
121 #[cfg(not(feature = "hanging_detection"))]
122 return EventListener {
123 listener: self.event.listen(),
124 };
125 #[cfg(feature = "hanging_detection")]
126 return EventListener {
127 description: self.description.clone(),
128 note: Arc::new(String::new),
129 future: Some(Box::pin(timeout(
130 Duration::from_secs(30),
131 self.event.listen(),
132 ))),
133 duration: Duration::from_secs(30),
134 };
135 }
136
137 pub fn listen_with_note(&self, _note: impl EventDescriptor) -> EventListener {
150 #[cfg(not(feature = "hanging_detection"))]
151 return EventListener {
152 listener: self.event.listen(),
153 };
154 #[cfg(feature = "hanging_detection")]
155 return EventListener {
156 description: self.description.clone(),
157 note: _note.get_description(),
158 future: Some(Box::pin(timeout(
159 Duration::from_secs(30),
160 self.event.listen(),
161 ))),
162 duration: Duration::from_secs(30),
163 };
164 }
165
166 pub fn take(&mut self) -> Event {
168 #[cfg(not(feature = "hanging_detection"))]
169 return Self {
170 event: replace(&mut self.event, event_listener::Event::new()),
171 };
172 #[cfg(feature = "hanging_detection")]
173 return Self {
174 description: self.description.clone(),
175 event: replace(&mut self.event, event_listener::Event::new()),
176 };
177 }
178}
179
180impl Event {
181 pub fn notify(&self, n: usize) {
183 self.event.notify(n);
184 }
185}
186
187impl Debug for Event {
188 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
189 let mut t = f.debug_tuple("Event");
190 #[cfg(feature = "hanging_detection")]
191 t.field(&(self.description)());
192 t.finish()
193 }
194}
195
196#[cfg(not(feature = "hanging_detection"))]
197pub struct EventListener {
198 listener: event_listener::EventListener,
199}
200
201#[cfg(not(feature = "hanging_detection"))]
202impl Debug for EventListener {
203 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
204 f.debug_tuple("EventListener").finish()
205 }
206}
207
208#[cfg(not(feature = "hanging_detection"))]
209impl Future for EventListener {
210 type Output = ();
211
212 fn poll(
213 self: Pin<&mut Self>,
214 cx: &mut std::task::Context<'_>,
215 ) -> std::task::Poll<Self::Output> {
216 let listener = unsafe { self.map_unchecked_mut(|s| &mut s.listener) };
217 listener.poll(cx)
218 }
219}
220
221#[cfg(not(feature = "hanging_detection"))]
222impl EventListener {
223 pub fn wait(self) {
228 block_on_listener(self.listener);
229 }
230}
231
232#[cfg(feature = "hanging_detection")]
233pub struct EventListener {
234 description: Arc<dyn Fn() -> String + Sync + Send>,
235 note: Arc<dyn Fn() -> String + Sync + Send>,
236 future: Option<std::pin::Pin<Box<Timeout<event_listener::EventListener>>>>,
239 duration: Duration,
240}
241
242#[cfg(feature = "hanging_detection")]
243impl Debug for EventListener {
244 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
245 let mut t = f.debug_tuple("EventListener");
246 t.field(&(self.description)());
247 let note = (self.note)();
248 if !note.is_empty() {
249 t.field(¬e);
250 }
251 t.finish()
252 }
253}
254
255#[cfg(feature = "hanging_detection")]
256impl Future for EventListener {
257 type Output = ();
258
259 fn poll(
260 mut self: Pin<&mut Self>,
261 cx: &mut std::task::Context<'_>,
262 ) -> std::task::Poll<Self::Output> {
263 while let Some(future) = self.future.as_mut() {
264 match ready!(future.as_mut().poll(cx)) {
265 Ok(_) => {
266 self.future = None;
267 return Poll::Ready(());
268 }
269 Err(_) => {
270 let note = (self.note)();
271 let description = (self.description)();
272 if note.is_empty() {
273 eprintln!(
274 "EventListener({}) is potentially hanging, waiting for {}s",
275 description,
276 self.duration.as_secs(),
277 );
278 } else {
279 eprintln!(
280 "EventListener({}) is potentially hanging, waiting for {}s from {}",
281 description,
282 self.duration.as_secs(),
283 note
284 );
285 }
286 self.duration *= 2;
287 let future = self.future.take().unwrap();
290 self.future = Some(Box::pin(timeout(
291 self.duration,
292 unsafe { std::pin::Pin::into_inner_unchecked(future) }.into_inner(),
295 )));
296 }
297 }
298 }
299 Poll::Ready(())
301 }
302}
303
304#[cfg(feature = "hanging_detection")]
305impl EventListener {
306 pub fn wait(mut self) {
311 if let Some(future) = self.future.take() {
312 block_on_listener(unsafe { std::pin::Pin::into_inner_unchecked(future) }.into_inner());
314 }
315 }
316}
317
318#[cfg(all(test, not(feature = "hanging_detection")))]
319mod tests {
320 use std::{
321 hint::black_box,
322 sync::{
323 Arc,
324 atomic::{AtomicBool, Ordering},
325 },
326 time::Instant,
327 };
328
329 use tokio::time::{Duration, timeout};
330
331 use super::*;
332
333 #[tokio::test]
337 async fn ensure_dead_code_elimination() {
338 fn dead_fn() {
339 unsafe {
341 unsafe extern "C" {
342 fn trigger_link_error() -> !;
343 }
344 trigger_link_error();
345 }
346 }
347
348 let event = black_box(Event::new(|| {
349 dead_fn();
350 || {
351 dead_fn();
352 String::new()
353 }
354 }));
355 let listener = black_box(event.listen_with_note(|| {
356 dead_fn();
357 || {
358 dead_fn();
359 String::new()
360 }
361 }));
362
363 let _ = black_box(timeout(Duration::from_millis(10), listener)).await;
364 }
365
366 #[test]
373 fn wait_blocks_until_notified_by_another_thread() {
374 const DELAY: Duration = Duration::from_millis(300);
375 const MIN_BLOCKED: Duration = Duration::from_millis(200);
377
378 let event = Arc::new(Event::new(|| || "test event".to_string()));
379 let listener = event.listen();
380
381 let notified = Arc::new(AtomicBool::new(false));
382 let notifier = {
383 let event = event.clone();
384 let notified = notified.clone();
385 std::thread::spawn(move || {
386 std::thread::sleep(DELAY);
387 notified.store(true, Ordering::SeqCst);
388 event.notify(1);
389 })
390 };
391
392 let start = Instant::now();
393 listener.wait();
394 let blocked_for = start.elapsed();
395
396 assert!(
397 notified.load(Ordering::SeqCst),
398 "wait() returned before the notifying thread ran"
399 );
400 assert!(
401 blocked_for >= MIN_BLOCKED,
402 "wait() did not block; it returned after {blocked_for:?}"
403 );
404
405 notifier.join().unwrap();
406 }
407}