async_drop_guard/
sync_drop.rs

1use std::fmt::Debug;
2use std::ops::{Deref, DerefMut};
3use std::panic::AssertUnwindSafe;
4
5use super::{AsyncDrop, AsyncDropGuard};
6
7/// SyncDrop wraps an [AsyncDropGuard] and calls `AsyncDropGuard::async_drop` on it in its
8/// synchronous [Drop] destructor.
9///
10/// WARNING: This can cause deadlocks, see https://stackoverflow.com/questions/71541765/rust-async-drop
11pub struct SyncDrop<T: Debug + AsyncDrop>(Option<AsyncDropGuard<T>>);
12
13impl<T: Debug + AsyncDrop> SyncDrop<T> {
14    pub fn new(v: AsyncDropGuard<T>) -> Self {
15        Self(Some(v))
16    }
17
18    pub fn into_inner_dont_drop(mut self) -> AsyncDropGuard<T> {
19        self.0.take().expect("Already dropped")
20    }
21
22    pub fn inner(&self) -> &AsyncDropGuard<T> {
23        self.0.as_ref().expect("Already dropped")
24    }
25}
26
27impl<T: Debug + AsyncDrop> Drop for SyncDrop<T> {
28    fn drop(&mut self) {
29        if let Some(mut v) = self.0.take() {
30            if std::thread::panicking() {
31                // v is unwind safe because we're destroying it and not using it anymore after the async_drop call
32                // TODO Is this actually true? What if its Drop implementation relies on state?
33                let mut v = AssertUnwindSafe(v);
34                // If we're dropping this because of a panic, we want to avoid causing a double
35                // panic because they don't show any error message or backtrace and are super hard
36                // to debug. Instead, log the inner panic and let the outer panic continue.
37                if let Err(panic) = std::panic::catch_unwind(move || {
38                    futures::executor::block_on(v.async_drop()).unwrap()
39                }) {
40                    log::error!("Double panic.\nInner panic: {:?}", panic);
41                    eprintln!("Double panic.\nInner panic: {:?}", panic);
42                }
43            } else {
44                futures::executor::block_on(v.async_drop()).unwrap()
45            }
46        }
47    }
48}
49
50impl<T: Debug + AsyncDrop> Deref for SyncDrop<T> {
51    type Target = T;
52    fn deref(&self) -> &T {
53        self.0.as_ref().expect("Already dropped")
54    }
55}
56
57impl<T: Debug + AsyncDrop> DerefMut for SyncDrop<T> {
58    fn deref_mut(&mut self) -> &mut T {
59        self.0.as_mut().expect("Already dropped")
60    }
61}