scope_guard/async_scope.rs
1extern crate std;
2
3use std::boxed::Box;
4use std::panic;
5
6use core::future::Future;
7use core::any::Any;
8use core::pin::Pin;
9use core::task;
10
11#[must_use]
12///Wraps to propagate panic as error.
13///
14///## Example
15///
16///```rust
17///use scope_guard::CatchUnwindFut;
18///
19///async fn my_fut() -> Result<(), bool> {
20/// Err(true)
21///}
22///
23///async fn example() {
24/// match CatchUnwindFut(my_fut()).await {
25/// Ok(Ok(())) => panic!("Success!?"),
26/// Ok(Err(res)) => assert!(res),
27/// Err(panic) => std::panic::resume_unwind(panic),
28/// }
29///}
30///```
31pub struct CatchUnwindFut<F: panic::UnwindSafe>(pub F);
32
33impl<F: Future + panic::UnwindSafe> Future for CatchUnwindFut<F> {
34 type Output = Result<F::Output, Box<dyn Any + Send + 'static>>;
35
36 #[inline(always)]
37 fn poll(self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
38 let fut = unsafe {
39 self.map_unchecked_mut(|this| &mut this.0)
40 };
41
42 match panic::catch_unwind(panic::AssertUnwindSafe(|| fut.poll(ctx))) {
43 Ok(task::Poll::Pending) => task::Poll::Pending,
44 Ok(task::Poll::Ready(res)) => task::Poll::Ready(Ok(res)),
45 Err(error) => task::Poll::Ready(Err(error)),
46 }
47 }
48}
49
50///Executes future, making sure to perform cleanup regardless of whether `fut` is successful or
51///panics.
52///
53///## Arguments:
54///- `dtor` - Generic callback that accepts `args` as its only incoming parameter;
55///- `args` - Generic arguments that are passed as it is to the `dtor`;
56///- `fut` - Future to execute before calling `dtor`. Regardless of success, `dtor` is always
57///executed.
58///
59///Returns `Output` of `fut` or panics on error in executing `fut`.
60///Regardless of `fut` execution status, `dtor` is always called.
61///
62///## Example
63///
64///```rust
65///use scope_guard::async_scope;
66///
67///async fn dtor(_args: ()) {
68/// println!("dtor!");
69///}
70///
71///async fn example() {
72/// let fut = async {
73/// panic!("FAIL")
74/// };
75///
76/// async_scope(dtor, (), fut).await;
77///}
78///```
79pub async fn async_scope<
80 R,
81 F: Future<Output = R> + panic::UnwindSafe,
82 DTORARGS,
83 DTOR: Future<Output = ()>,
84 DTORFN: FnOnce(DTORARGS) -> DTOR,
85>(
86 dtor: DTORFN,
87 args: DTORARGS,
88 fut: F,
89) -> R {
90 let result = CatchUnwindFut(fut).await;
91 let dtor = (dtor)(args);
92 dtor.await;
93 match result {
94 Ok(result) => result,
95 Err(error) => std::panic::resume_unwind(error),
96 }
97}