mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-11-23 07:17:55 +01:00
120 lines
3.6 KiB
Rust
120 lines
3.6 KiB
Rust
|
//!
|
||
|
//! Handle for recoverable process
|
||
|
use std::any::Any;
|
||
|
use crate::proc_data::ProcData;
|
||
|
use crate::proc_handle::ProcHandle;
|
||
|
use crate::state::State;
|
||
|
use std::fmt::{self, Debug, Formatter};
|
||
|
use std::future::Future;
|
||
|
use std::pin::Pin;
|
||
|
use std::task::{Context, Poll};
|
||
|
use std::thread;
|
||
|
|
||
|
/// Recoverable handle which encapsulates a standard Proc Handle and contain all panics inside.
|
||
|
///
|
||
|
/// Execution of `after_panic` will be immediate on polling the [RecoverableHandle]'s future.
|
||
|
pub struct RecoverableHandle<R> {
|
||
|
inner: ProcHandle<thread::Result<R>>,
|
||
|
|
||
|
/// Panic callback
|
||
|
///
|
||
|
/// This callback will be called if the interior future panics. It is passed the panic
|
||
|
// reason i.e. the `Err` of [`std::thread::Result`]
|
||
|
panicked: Option<Box<dyn FnOnce(Box<dyn Any + Send>) + Send + Sync>>,
|
||
|
}
|
||
|
|
||
|
impl<R> RecoverableHandle<R> {
|
||
|
pub(crate) fn new(inner: ProcHandle<thread::Result<R>>) -> Self {
|
||
|
RecoverableHandle {
|
||
|
inner,
|
||
|
panicked: None,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Cancels the proc.
|
||
|
///
|
||
|
/// If the proc has already completed, calling this method will have no effect.
|
||
|
///
|
||
|
/// When a proc is cancelled, its future cannot be polled again and will be dropped instead.
|
||
|
pub fn cancel(&self) {
|
||
|
self.inner.cancel()
|
||
|
}
|
||
|
|
||
|
/// Returns a state of the ProcHandle.
|
||
|
pub fn state(&self) -> State {
|
||
|
self.inner.state()
|
||
|
}
|
||
|
|
||
|
/// Adds a callback that will be executed should the inner future `panic!`s
|
||
|
///
|
||
|
/// ```rust
|
||
|
/// # use std::any::Any;
|
||
|
/// use lightproc::proc_stack::ProcStack;
|
||
|
/// use lightproc::proc_state::EmptyProcState;
|
||
|
/// # use lightproc::prelude::*;
|
||
|
/// #
|
||
|
/// # // ... future that does work
|
||
|
/// # let future = async {
|
||
|
/// # println!("Doing some work");
|
||
|
/// # };
|
||
|
/// #
|
||
|
/// # // ... basic schedule function with no waker logic
|
||
|
/// # fn schedule_function(proc: LightProc) {;}
|
||
|
/// #
|
||
|
/// # // ... process stack with a lifecycle callback
|
||
|
/// # let proc_stack =
|
||
|
/// # ProcStack::default()
|
||
|
/// # .with_after_panic(|s: &mut EmptyProcState| {
|
||
|
/// # println!("After panic started!");
|
||
|
/// # });
|
||
|
/// #
|
||
|
/// // ... creating a recoverable process
|
||
|
/// let (proc, recoverable) = LightProc::recoverable(
|
||
|
/// future,
|
||
|
/// schedule_function,
|
||
|
/// );
|
||
|
///
|
||
|
/// recoverable
|
||
|
/// .on_return(|_e: Box<dyn Any + Send>| {
|
||
|
/// println!("Inner future panicked");
|
||
|
/// });
|
||
|
/// ```
|
||
|
pub fn on_panic<F>(mut self, callback: F) -> Self
|
||
|
where F: FnOnce(Box<dyn Any + Send>) + Send + Sync + 'static,
|
||
|
{
|
||
|
self.panicked = Some(Box::new(callback));
|
||
|
self
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
impl<R> Future for RecoverableHandle<R> {
|
||
|
type Output = Option<R>;
|
||
|
|
||
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||
|
match Pin::new(&mut self.inner).poll(cx) {
|
||
|
Poll::Pending => Poll::Pending,
|
||
|
Poll::Ready(None) => Poll::Ready(None),
|
||
|
Poll::Ready(Some(Ok(val))) => Poll::Ready(Some(val)),
|
||
|
Poll::Ready(Some(Err(e))) => {
|
||
|
if let Some(callback) = self.panicked.take() {
|
||
|
callback(e);
|
||
|
}
|
||
|
|
||
|
Poll::Ready(None)
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<R> Debug for RecoverableHandle<R> {
|
||
|
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
||
|
let ptr = self.inner.raw_proc.as_ptr();
|
||
|
let pdata = ptr as *const ProcData;
|
||
|
|
||
|
fmt.debug_struct("ProcHandle")
|
||
|
.field("pdata", unsafe { &(*pdata) })
|
||
|
.finish_non_exhaustive()
|
||
|
}
|
||
|
}
|