Skip to content

Instantly share code, notes, and snippets.

@Lucretiel
Last active May 5, 2021 06:27
Show Gist options
  • Save Lucretiel/c60cc8c43ec0f527d7b4b8d55e0bed81 to your computer and use it in GitHub Desktop.
Save Lucretiel/c60cc8c43ec0f527d7b4b8d55e0bed81 to your computer and use it in GitHub Desktop.
A future adapter that retries the future if it panics
use std::{
future::Future,
panic::{catch_unwind, UnwindSafe},
pin::Pin,
task::{Context, Poll},
};
use pin_project::pin_project;
#[pin_project]
#[derive(Debug, Clone)]
pub struct RetryOnPanic<F> {
original: F,
#[pin]
attempt: F,
}
pub fn retry_on_panic<F>(fut: F) -> RetryOnPanic<F>
where
F: Future + Clone,
for<'a> &'a mut F: UnwindSafe,
{
RetryOnPanic {
attempt: fut.clone(),
original: fut,
}
}
impl<F: Future + Clone> Future for RetryOnPanic<F>
where
for<'a> &'a mut F: UnwindSafe,
{
type Output = F::Output;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let waker = cx.waker();
let attempt = self.as_mut().project().attempt;
match catch_unwind(|| {
let mut cx = Context::from_waker(waker);
attempt.poll(&mut cx)
}) {
Ok(polled) => polled,
Err(..) => {
let retry = self.as_mut().project().original.clone();
self.as_mut().project().attempt.set(retry);
waker.wake_by_ref();
Poll::Pending
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment