1use std::ffi::{c_char, c_void, CStr};
22use std::future::Future;
23use std::pin::Pin;
24use std::sync::atomic::{AtomicBool, Ordering};
25use std::sync::{Arc, Condvar, Mutex, PoisonError};
26use std::task::{Context, Poll, Waker};
27use std::time::{Duration, Instant};
28
29use crate::panic_safe::catch_user_panic;
30
31struct SyncCompletionState<T> {
42 result: Option<Result<T, String>>,
43}
44
45struct SyncCompletionInner<T> {
47 consumed: AtomicBool,
51 state: Mutex<SyncCompletionState<T>>,
52 cvar: Condvar,
53}
54
55pub struct SyncCompletion<T> {
64 inner: Arc<SyncCompletionInner<T>>,
65}
66
67pub type SyncCompletionPtr = *mut c_void;
69
70impl<T> SyncCompletion<T> {
71 #[must_use]
77 pub fn new() -> (Self, SyncCompletionPtr) {
78 let inner = Arc::new(SyncCompletionInner {
79 consumed: AtomicBool::new(false),
80 state: Mutex::new(SyncCompletionState { result: None }),
81 cvar: Condvar::new(),
82 });
83 let raw = Arc::into_raw(Arc::clone(&inner));
84 (Self { inner }, raw as SyncCompletionPtr)
85 }
86
87 pub fn wait(self) -> Result<T, String> {
95 let mut state = self
96 .inner
97 .state
98 .lock()
99 .unwrap_or_else(PoisonError::into_inner);
100 loop {
101 if let Some(result) = state.result.take() {
102 return result;
103 }
104 state = self
105 .inner
106 .cvar
107 .wait(state)
108 .unwrap_or_else(PoisonError::into_inner);
109 }
110 }
111
112 #[must_use]
113 pub fn wait_timeout(self, timeout: Duration) -> Option<Result<T, String>> {
114 let start = Instant::now();
115 let mut state = self
116 .inner
117 .state
118 .lock()
119 .unwrap_or_else(PoisonError::into_inner);
120 loop {
121 if let Some(result) = state.result.take() {
122 return Some(result);
123 }
124 let remaining = timeout.checked_sub(start.elapsed())?;
125 state = self
126 .inner
127 .cvar
128 .wait_timeout(state, remaining)
129 .unwrap_or_else(PoisonError::into_inner)
130 .0;
131 }
132 }
133
134 #[must_use]
135 pub fn context_ptr(&self) -> SyncCompletionPtr {
136 Arc::as_ptr(&self.inner).cast_mut().cast()
137 }
138
139 pub unsafe fn complete_ok(context: SyncCompletionPtr, value: T) {
149 Self::complete_with_result(context, Ok(value));
150 }
151
152 pub unsafe fn complete_err(context: SyncCompletionPtr, error: String) {
162 Self::complete_with_result(context, Err(error));
163 }
164
165 pub unsafe fn complete_with_result(context: SyncCompletionPtr, result: Result<T, String>) {
184 if context.is_null() {
185 return;
186 }
187
188 let inner_ref = unsafe { &*context.cast::<SyncCompletionInner<T>>() };
192 if inner_ref.consumed.swap(true, Ordering::AcqRel) {
193 eprintln!(
194 "doom-fish-utils: SyncCompletion callback fired more than once; \
195 ignoring duplicate to avoid double-free"
196 );
197 return;
198 }
199
200 let inner = unsafe { Arc::from_raw(context.cast::<SyncCompletionInner<T>>()) };
201 {
202 let mut state = inner
205 .state
206 .lock()
207 .unwrap_or_else(std::sync::PoisonError::into_inner);
208 state.result = Some(result);
209 }
210 inner.cvar.notify_one();
211 }
212}
213
214impl<T> Default for SyncCompletion<T> {
215 fn default() -> Self {
216 Self::new().0
217 }
218}
219
220struct AsyncCompletionState<T> {
226 result: Option<Result<T, String>>,
227 waker: Option<Waker>,
228}
229
230struct AsyncCompletionInner<T> {
234 consumed: AtomicBool,
235 state: Mutex<AsyncCompletionState<T>>,
236}
237
238pub struct AsyncCompletion<T> {
244 _marker: std::marker::PhantomData<T>,
245}
246
247pub struct AsyncCompletionFuture<T> {
249 inner: Arc<AsyncCompletionInner<T>>,
250}
251
252impl<T> AsyncCompletion<T> {
253 #[must_use]
259 pub fn create() -> (AsyncCompletionFuture<T>, SyncCompletionPtr) {
260 let inner = Arc::new(AsyncCompletionInner {
261 consumed: AtomicBool::new(false),
262 state: Mutex::new(AsyncCompletionState {
263 result: None,
264 waker: None,
265 }),
266 });
267 let raw = Arc::into_raw(Arc::clone(&inner));
268 (AsyncCompletionFuture { inner }, raw as SyncCompletionPtr)
269 }
270
271 pub unsafe fn complete_ok(context: SyncCompletionPtr, value: T) {
281 Self::complete_with_result(context, Ok(value));
282 }
283
284 pub unsafe fn complete_err(context: SyncCompletionPtr, error: String) {
294 Self::complete_with_result(context, Err(error));
295 }
296
297 pub unsafe fn complete_with_result(context: SyncCompletionPtr, result: Result<T, String>) {
314 if context.is_null() {
315 return;
316 }
317
318 let inner_ref = unsafe { &*context.cast::<AsyncCompletionInner<T>>() };
319 if inner_ref.consumed.swap(true, Ordering::AcqRel) {
320 eprintln!(
321 "doom-fish-utils: AsyncCompletion callback fired more than once; \
322 ignoring duplicate to avoid double-free"
323 );
324 return;
325 }
326
327 let inner = unsafe { Arc::from_raw(context.cast::<AsyncCompletionInner<T>>()) };
328
329 let waker = {
330 let mut state = inner
333 .state
334 .lock()
335 .unwrap_or_else(std::sync::PoisonError::into_inner);
336 state.result = Some(result);
337 state.waker.take()
338 };
339
340 if let Some(w) = waker {
341 w.wake();
342 }
343
344 }
348}
349
350impl<T> Future for AsyncCompletionFuture<T> {
351 type Output = Result<T, String>;
352
353 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
354 let mut state = self
355 .inner
356 .state
357 .lock()
358 .unwrap_or_else(std::sync::PoisonError::into_inner);
359
360 state.result.take().map_or_else(
361 || {
362 let waker = cx.waker();
369 match state.waker {
370 Some(ref existing) if existing.will_wake(waker) => {}
371 _ => state.waker = Some(waker.clone()),
372 }
373 Poll::Pending
374 },
375 Poll::Ready,
376 )
377 }
378}
379
380#[must_use]
390pub unsafe fn error_from_cstr(msg: *const c_char) -> String {
391 if msg.is_null() {
392 "Unknown error".to_string()
393 } else {
394 CStr::from_ptr(msg)
395 .to_str()
396 .map_or_else(|_| "Unknown error".to_string(), String::from)
397 }
398}
399
400pub type UnitCompletion = SyncCompletion<()>;
402
403impl UnitCompletion {
404 pub unsafe extern "C" fn callback(context: *mut c_void, success: bool, msg: *const c_char) {
424 catch_user_panic("UnitCompletion::callback", || {
425 if success {
426 unsafe { Self::complete_ok(context, ()) };
427 } else {
428 let error = unsafe { error_from_cstr(msg) };
429 unsafe { Self::complete_err(context, error) };
430 }
431 });
432 }
433}
434
435#[cfg(test)]
436mod tests {
437 use std::future::Future;
438 use std::pin::Pin;
439 use std::ptr;
440 use std::sync::atomic::{AtomicUsize, Ordering};
441 use std::sync::Arc;
442 use std::task::{Context, Poll, Wake, Waker};
443 use std::thread;
444 use std::time::{Duration, Instant};
445
446 use super::{AsyncCompletion, SyncCompletion, UnitCompletion};
447
448 #[test]
449 fn unit_completion_callback_matches_shared_alias() {
450 let callback: crate::ffi_callbacks::UnitCompletionCallback = UnitCompletion::callback;
451 let _ = callback;
452 }
453
454 #[test]
455 fn unit_completion_callback_completes_successfully() {
456 let (completion, context) = UnitCompletion::new();
457
458 unsafe { UnitCompletion::callback(context, true, ptr::null()) };
459
460 assert_eq!(completion.wait(), Ok(()));
461 }
462
463 #[test]
464 fn unit_completion_callback_reports_errors() {
465 let (completion, context) = UnitCompletion::new();
466
467 unsafe { UnitCompletion::callback(context, false, c"denied".as_ptr()) };
468
469 assert_eq!(completion.wait(), Err("denied".to_string()));
470 }
471
472 struct DropCounter(Arc<AtomicUsize>);
473
474 impl Drop for DropCounter {
475 fn drop(&mut self) {
476 self.0.fetch_add(1, Ordering::SeqCst);
477 }
478 }
479
480 #[test]
481 fn sync_completion_ignores_a_duplicate_callback() {
482 let (completion, context) = SyncCompletion::<u32>::new();
483
484 unsafe { SyncCompletion::<u32>::complete_ok(context, 1) };
485 unsafe { SyncCompletion::<u32>::complete_ok(context, 2) };
486
487 assert_eq!(completion.wait(), Ok(1));
488 }
489
490 #[test]
491 fn sync_completion_waits_for_another_thread() {
492 let (completion, context) = SyncCompletion::<String>::new();
493 let context = context as usize;
494
495 let callback = thread::spawn(move || {
496 thread::sleep(Duration::from_millis(20));
497 unsafe { SyncCompletion::<String>::complete_ok(context as *mut _, "done".to_string()) };
498 });
499
500 assert_eq!(completion.wait(), Ok("done".to_string()));
501 callback.join().unwrap();
502 }
503
504 #[test]
505 fn wait_timeout_returns_none_when_no_callback_arrives() {
506 let (completion, _context) = SyncCompletion::<u32>::new();
507 let timeout = Duration::from_millis(30);
508 let start = Instant::now();
509
510 assert_eq!(completion.wait_timeout(timeout), None);
511 assert!(start.elapsed() >= timeout);
512 }
513
514 #[test]
515 fn wait_timeout_returns_the_result() {
516 let (completion, context) = SyncCompletion::<u32>::new();
517 unsafe { SyncCompletion::<u32>::complete_err(context, "failed".to_string()) };
518
519 assert_eq!(
520 completion.wait_timeout(Duration::from_secs(5)),
521 Some(Err("failed".to_string()))
522 );
523
524 let (completion, context) = SyncCompletion::<u32>::new();
525 let context = context as usize;
526 let callback = thread::spawn(move || {
527 thread::sleep(Duration::from_millis(20));
528 unsafe { SyncCompletion::<u32>::complete_ok(context as *mut _, 9) };
529 });
530
531 assert_eq!(completion.wait_timeout(Duration::MAX), Some(Ok(9)));
532 callback.join().unwrap();
533 }
534
535 #[test]
536 fn late_callback_after_timeout_releases_the_value() {
537 let drops = Arc::new(AtomicUsize::new(0));
538 let (completion, context) = SyncCompletion::<DropCounter>::new();
539
540 assert!(completion.wait_timeout(Duration::from_millis(1)).is_none());
541 unsafe {
542 SyncCompletion::<DropCounter>::complete_ok(context, DropCounter(Arc::clone(&drops)));
543 };
544
545 assert_eq!(drops.load(Ordering::SeqCst), 1);
546 }
547
548 #[test]
549 fn default_sync_completion_can_be_completed() {
550 let completion = SyncCompletion::<u32>::default();
551 let context = completion.context_ptr();
552
553 unsafe { SyncCompletion::<u32>::complete_ok(context, 5) };
554
555 assert_eq!(completion.wait(), Ok(5));
556
557 let (completion, context) = SyncCompletion::<u32>::new();
558 assert_eq!(completion.context_ptr(), context);
559 unsafe { SyncCompletion::<u32>::complete_ok(context, 6) };
560 assert_eq!(completion.wait(), Ok(6));
561 }
562
563 #[test]
564 fn wait_recovers_from_a_poisoned_lock() {
565 let (completion, context) = SyncCompletion::<u32>::new();
566 let inner = Arc::clone(&completion.inner);
567 assert!(thread::spawn(move || {
568 let _state = inner.state.lock().unwrap();
569 panic!("poison completion state");
570 })
571 .join()
572 .is_err());
573
574 unsafe { SyncCompletion::<u32>::complete_ok(context, 3) };
575
576 assert_eq!(completion.wait(), Ok(3));
577
578 let (completion, _context) = SyncCompletion::<u32>::new();
579 let inner = Arc::clone(&completion.inner);
580 assert!(thread::spawn(move || {
581 let _state = inner.state.lock().unwrap();
582 panic!("poison completion state");
583 })
584 .join()
585 .is_err());
586
587 assert_eq!(completion.wait_timeout(Duration::from_millis(1)), None);
588 }
589
590 #[derive(Default)]
591 struct CountingWake(AtomicUsize);
592
593 impl Wake for CountingWake {
594 fn wake(self: Arc<Self>) {
595 self.0.fetch_add(1, Ordering::SeqCst);
596 }
597 }
598
599 #[test]
600 fn async_completion_resolves_with_the_value() {
601 let (future, context) = AsyncCompletion::<u32>::create();
602
603 unsafe { AsyncCompletion::<u32>::complete_ok(context, 42) };
604
605 assert_eq!(pollster::block_on(future), Ok(42));
606 }
607
608 #[test]
609 fn async_completion_wakes_a_pending_future() {
610 let (mut future, context) = AsyncCompletion::<u32>::create();
611 let probe = Arc::new(CountingWake::default());
612 let waker = Waker::from(Arc::clone(&probe));
613 let mut cx = Context::from_waker(&waker);
614
615 assert_eq!(Pin::new(&mut future).poll(&mut cx), Poll::Pending);
616
617 let context = context as usize;
618 thread::spawn(move || unsafe { AsyncCompletion::<u32>::complete_ok(context as *mut _, 8) })
619 .join()
620 .unwrap();
621
622 assert_eq!(probe.0.load(Ordering::SeqCst), 1);
623 assert_eq!(Pin::new(&mut future).poll(&mut cx), Poll::Ready(Ok(8)));
624 }
625
626 #[test]
627 fn async_completion_resolves_with_the_error() {
628 let (future, context) = AsyncCompletion::<u32>::create();
629
630 unsafe { AsyncCompletion::<u32>::complete_err(context, "denied".to_string()) };
631
632 assert_eq!(pollster::block_on(future), Err("denied".to_string()));
633 }
634
635 #[test]
636 fn async_completion_ignores_a_duplicate_callback() {
637 let drops = Arc::new(AtomicUsize::new(0));
638 let (future, context) = AsyncCompletion::<DropCounter>::create();
639
640 unsafe {
641 AsyncCompletion::<DropCounter>::complete_ok(context, DropCounter(Arc::clone(&drops)));
642 };
643 unsafe { AsyncCompletion::<DropCounter>::complete_err(context, "duplicate".to_string()) };
644
645 let value = pollster::block_on(future).unwrap();
646 assert_eq!(drops.load(Ordering::SeqCst), 0);
647 drop(value);
648 assert_eq!(drops.load(Ordering::SeqCst), 1);
649 }
650
651 #[test]
652 fn dropped_async_future_releases_a_late_value() {
653 let drops = Arc::new(AtomicUsize::new(0));
654 let (future, context) = AsyncCompletion::<DropCounter>::create();
655 drop(future);
656
657 unsafe {
658 AsyncCompletion::<DropCounter>::complete_ok(context, DropCounter(Arc::clone(&drops)));
659 };
660
661 assert_eq!(drops.load(Ordering::SeqCst), 1);
662 }
663}