turbo_tasks/
capture_future.rs

1use std::{
2    borrow::Cow,
3    fmt::Display,
4    future::Future,
5    panic,
6    pin::Pin,
7    task::{Context, Poll},
8};
9
10use anyhow::Result;
11use pin_project_lite::pin_project;
12use serde::{Deserialize, Serialize};
13
14use crate::{backend::TurboTasksExecutionErrorMessage, panic_hooks::LAST_ERROR_LOCATION};
15
16pin_project! {
17    pub struct CaptureFuture<T, F: Future<Output = T>> {
18        #[pin]
19        future: F,
20    }
21}
22
23impl<T, F: Future<Output = T>> CaptureFuture<T, F> {
24    pub fn new(future: F) -> Self {
25        Self { future }
26    }
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
30pub struct TurboTasksPanic {
31    pub message: TurboTasksExecutionErrorMessage,
32    pub location: Option<String>,
33}
34
35impl TurboTasksPanic {
36    pub fn into_panic(self) -> Box<dyn std::any::Any + Send> {
37        Box::new(format!(
38            "{} at {}",
39            self.message,
40            self.location
41                .unwrap_or_else(|| "unknown location".to_string())
42        ))
43    }
44}
45
46impl Display for TurboTasksPanic {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        write!(f, "{}", self.message)
49    }
50}
51
52impl<T, F: Future<Output = T>> Future for CaptureFuture<T, F> {
53    type Output = Result<T, TurboTasksPanic>;
54
55    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
56        let this = self.project();
57
58        let result =
59            panic::catch_unwind(panic::AssertUnwindSafe(|| this.future.poll(cx))).map_err(|err| {
60                let message = match err.downcast_ref::<&'static str>() {
61                    Some(s) => TurboTasksExecutionErrorMessage::PIISafe(Cow::Borrowed(s)),
62                    None => match err.downcast_ref::<String>() {
63                        Some(s) => TurboTasksExecutionErrorMessage::NonPIISafe(s.clone()),
64                        None => {
65                            let error_message = err
66                                .downcast_ref::<Box<dyn Display>>()
67                                .map(|e| e.to_string())
68                                .unwrap_or_else(|| String::from("<unknown panic>"));
69
70                            TurboTasksExecutionErrorMessage::NonPIISafe(error_message)
71                        }
72                    },
73                };
74
75                LAST_ERROR_LOCATION.with_borrow(|loc| TurboTasksPanic {
76                    message,
77                    location: loc.clone(),
78                })
79            });
80
81        match result {
82            Err(err) => Poll::Ready(Err(err)),
83            Ok(Poll::Ready(r)) => Poll::Ready(Ok(r)),
84            Ok(Poll::Pending) => Poll::Pending,
85        }
86    }
87}