From a80512063870626582aa6a25fcedae63234566d9 Mon Sep 17 00:00:00 2001 From: mfish33 <32677537+mfish33@users.noreply.github.com> Date: Mon, 3 Jan 2022 13:15:43 -0800 Subject: [PATCH] Fix to send panic message to all editor instances immediately upon crash (#460) --- frontend/wasm/src/api.rs | 35 +++++++++++++++++------------------ frontend/wasm/src/lib.rs | 16 +++++++++++++--- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/frontend/wasm/src/api.rs b/frontend/wasm/src/api.rs index fd4b0be7..e4ac0951 100644 --- a/frontend/wasm/src/api.rs +++ b/frontend/wasm/src/api.rs @@ -1,8 +1,7 @@ // This file is where functions are defined to be called directly from JS. // It serves as a thin wrapper over the editor backend API that relies // on the dispatcher messaging system and more complex Rust data types. - -use std::cell::Cell; +use std::sync::atomic::Ordering; use crate::helpers::Error; use crate::type_translators::{translate_blend_mode, translate_key, translate_tool_type, translate_view_mode}; @@ -25,9 +24,9 @@ use wasm_bindgen::prelude::*; // we must make all methods take a non mutable reference to self. Not doing this creates // an issue when rust calls into JS which calls back to rust in the same call stack. #[wasm_bindgen] +#[derive(Clone)] pub struct JsEditorHandle { editor_id: u64, - instance_received_crashed: Cell, handle_response: js_sys::Function, } @@ -38,23 +37,15 @@ impl JsEditorHandle { pub fn new(handle_response: js_sys::Function) -> Self { let editor_id = generate_uuid(); let editor = Editor::new(); - EDITOR_INSTANCES.with(|instances| instances.borrow_mut().insert(editor_id, editor)); - JsEditorHandle { - editor_id, - instance_received_crashed: Cell::new(false), - handle_response, - } + let editor_handle = JsEditorHandle { editor_id, handle_response }; + EDITOR_INSTANCES.with(|instances| instances.borrow_mut().insert(editor_id, (editor, editor_handle.clone()))); + editor_handle } // Sends a message to the dispatcher in the Editor Backend fn dispatch>(&self, message: T) { // Process no further messages after a crash to avoid spamming the console - let possible_crash_message = EDITOR_HAS_CRASHED.with(|crash_state| crash_state.borrow().clone()); - if let Some(message) = possible_crash_message { - if !self.instance_received_crashed.get() { - self.handle_response(message); - self.instance_received_crashed.set(true); - } + if EDITOR_HAS_CRASHED.load(Ordering::SeqCst) { return; } @@ -63,6 +54,7 @@ impl JsEditorHandle { .borrow_mut() .get_mut(&self.editor_id) .expect("EDITOR_INSTANCES does not contain the current editor_id") + .0 .handle_message(message.into()) }); for response in responses.into_iter() { @@ -94,9 +86,8 @@ impl JsEditorHandle { // backend from the web frontend. // ======================================================================== - pub fn has_crashed(&self) -> JsValue { - let has_crashed = EDITOR_HAS_CRASHED.with(|crash_state| crash_state.borrow().is_some()); - has_crashed.into() + pub fn has_crashed(&self) -> bool { + EDITOR_HAS_CRASHED.load(Ordering::SeqCst) } /// Modify the currently selected tool in the document state store @@ -514,6 +505,14 @@ impl JsEditorHandle { } } +// Needed to make JsEditorHandle functions pub to rust. Do not fully +// understand reason but has to do with #[wasm_bindgen] procedural macro. +impl JsEditorHandle { + pub fn handle_response_rust_proxy(&self, message: FrontendMessage) { + self.handle_response(message); + } +} + impl Drop for JsEditorHandle { fn drop(&mut self) { EDITOR_INSTANCES.with(|instances| instances.borrow_mut().remove(&self.editor_id)); diff --git a/frontend/wasm/src/lib.rs b/frontend/wasm/src/lib.rs index e9293552..a4e5245c 100644 --- a/frontend/wasm/src/lib.rs +++ b/frontend/wasm/src/lib.rs @@ -8,15 +8,17 @@ use logging::WasmLog; use std::cell::RefCell; use std::collections::HashMap; use std::panic; +use std::sync::atomic::AtomicBool; use wasm_bindgen::prelude::*; // Set up the persistent editor backend state static LOGGER: WasmLog = WasmLog; thread_local! { - pub static EDITOR_HAS_CRASHED: RefCell> = RefCell::new(None); - pub static EDITOR_INSTANCES: RefCell> = RefCell::new(HashMap::new()); + pub static EDITOR_INSTANCES: RefCell> = RefCell::new(HashMap::new()); } +pub static EDITOR_HAS_CRASHED: AtomicBool = AtomicBool::new(false); + // Initialize the backend #[wasm_bindgen(start)] pub fn init() { @@ -31,5 +33,13 @@ fn panic_hook(info: &panic::PanicInfo) { let panic_info = info.to_string(); let title = "The editor crashed — sorry about that".to_string(); let description = "An internal error occurred. Reload the editor to continue. Please report this by filing an issue on GitHub.".to_string(); - EDITOR_HAS_CRASHED.with(|crash_status| crash_status.borrow_mut().replace(FrontendMessage::DisplayPanic { panic_info, title, description })); + EDITOR_INSTANCES.with(|instances| { + instances.borrow_mut().values_mut().for_each(|instance| { + instance.1.handle_response_rust_proxy(FrontendMessage::DisplayPanic { + panic_info: panic_info.clone(), + title: title.clone(), + description: description.clone(), + }) + }) + }); }