mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-10 00:08:03 +00:00
Sandbox node graph execution on native targets and attempt recovery from panics on Wasm (#1846)
* Test out wasm unwinding * Implement panic catching for native targets * Hack in support for recovering panics in wasm * Keep debug info in release builds * Check for DynAnyNode in Backtrace because that can't be inlined as well * Improve error dialog * Use a mutex for storing the frontend state instead of a RefCell * Code review * Update crash text --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
06177597ae
commit
5b1d3a0ae4
8 changed files with 110 additions and 46 deletions
|
@ -9,17 +9,19 @@ pub mod helpers;
|
|||
|
||||
use editor::messages::prelude::*;
|
||||
|
||||
use std::cell::{OnceCell, RefCell};
|
||||
use std::panic;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Mutex;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
// Set up the persistent editor backend state
|
||||
pub static EDITOR_HAS_CRASHED: AtomicBool = AtomicBool::new(false);
|
||||
pub static NODE_GRAPH_ERROR_DISPLAYED: AtomicBool = AtomicBool::new(false);
|
||||
pub static LOGGER: WasmLog = WasmLog;
|
||||
|
||||
thread_local! {
|
||||
pub static EDITOR: OnceCell<RefCell<editor::application::Editor>> = const { OnceCell::new() };
|
||||
pub static EDITOR_HANDLE: OnceCell<RefCell<editor_api::EditorHandle>> = const { OnceCell::new() };
|
||||
pub static EDITOR: Mutex<Option<editor::application::Editor>> = const { Mutex::new(None) };
|
||||
pub static EDITOR_HANDLE: Mutex<Option<editor_api::EditorHandle>> = const { Mutex::new(None) };
|
||||
}
|
||||
|
||||
/// Initialize the backend
|
||||
|
@ -35,16 +37,44 @@ pub fn init_graphite() {
|
|||
|
||||
/// When a panic occurs, notify the user and log the error to the JS console before the backend dies
|
||||
pub fn panic_hook(info: &panic::PanicInfo) {
|
||||
EDITOR_HAS_CRASHED.store(true, Ordering::SeqCst);
|
||||
let info = info.to_string();
|
||||
let backtrace = Error::new("stack").stack().to_string();
|
||||
if backtrace.contains("DynAnyNode") {
|
||||
log::error!("Node graph evaluation panicked {info}");
|
||||
|
||||
error!("{info}");
|
||||
// When the graph panics, the node runtime lock may not be released properly
|
||||
if editor::node_graph_executor::NODE_RUNTIME.try_lock().is_none() {
|
||||
unsafe { editor::node_graph_executor::NODE_RUNTIME.force_unlock() };
|
||||
}
|
||||
|
||||
if !NODE_GRAPH_ERROR_DISPLAYED.load(Ordering::SeqCst) {
|
||||
NODE_GRAPH_ERROR_DISPLAYED.store(true, Ordering::SeqCst);
|
||||
editor_api::editor_and_handle(|_, handle| {
|
||||
let error = r#"
|
||||
<rect x="50%" y="50%" width="600" height="100" transform="translate(-300 -50)" rx="4" fill="var(--color-error-red)" />
|
||||
<text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" font-size="18" fill="var(--color-2-mildblack)">
|
||||
<tspan x="50%" dy="-24" font-weight="bold">The document crashed while being rendered in its current state.</tspan>
|
||||
<tspan x="50%" dy="24">The editor is now unstable! Undo your last action to restore the artwork,</tspan>
|
||||
<tspan x="50%" dy="24">then save your document and restart the editor before continuing work.</tspan>
|
||||
/text>"#
|
||||
// It's a mystery why the `/text>` tag above needs to be missing its `<`, but when it exists it prints the `<` character in the text. However this works with it removed.
|
||||
.to_string();
|
||||
handle.send_frontend_message_to_js_rust_proxy(FrontendMessage::UpdateDocumentArtwork { svg: error });
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
} else {
|
||||
EDITOR_HAS_CRASHED.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
log::error!("{info}");
|
||||
|
||||
EDITOR_HANDLE.with(|editor_handle| {
|
||||
editor_handle.get().map(|handle| {
|
||||
handle
|
||||
.borrow_mut()
|
||||
.send_frontend_message_to_js_rust_proxy(FrontendMessage::DisplayDialogPanic { panic_info: info.to_string() })
|
||||
})
|
||||
let mut guard = editor_handle.lock();
|
||||
if let Ok(Some(ref mut handle)) = guard.as_deref_mut() {
|
||||
handle.send_frontend_message_to_js_rust_proxy(FrontendMessage::DisplayDialogPanic { panic_info: info.to_string() });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -56,6 +86,9 @@ extern "C" {
|
|||
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(msg: &str) -> Error;
|
||||
|
||||
#[wasm_bindgen(structural, method, getter)]
|
||||
fn stack(error: &Error) -> String;
|
||||
}
|
||||
|
||||
/// Logging to the JS console
|
||||
|
@ -69,6 +102,8 @@ extern "C" {
|
|||
fn warn(msg: &str, format: &str);
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn error(msg: &str, format: &str);
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn trace(msg: &str, format: &str);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue