Skip to content

Commit

Permalink
Handle wasm panics, display a message and allow a restart (#7507)
Browse files Browse the repository at this point in the history
  • Loading branch information
Frizi authored Aug 30, 2023
1 parent d9768ca commit c834847
Show file tree
Hide file tree
Showing 61 changed files with 1,049 additions and 1,637 deletions.
27 changes: 12 additions & 15 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ serde-wasm-bindgen = { version = "0.4.5" }
tokio = { version = "1.23.0", features = ["full", "tracing"] }
tokio-stream = { version = "0.1.12", features = ["fs"] }
tokio-util = { version = "0.7.4", features = ["full"] }
wasm-bindgen = { version = "0.2.84", features = [] }
wasm-bindgen = { version = "0.2.87", features = [] }
wasm-bindgen-test = { version = "0.3.34" }
anyhow = { version = "1.0.66" }
failure = { version = "0.1.8" }
Expand Down
1 change: 0 additions & 1 deletion app/gui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ engine-protocol = { path = "controller/engine-protocol" }
json-rpc = { path = "../../lib/rust/json-rpc" }
span-tree = { path = "language/span-tree" }
bimap = { version = "0.4.0" }
console_error_panic_hook = { workspace = true }
const_format = { workspace = true }
convert_case = { workspace = true }
failure = { workspace = true }
Expand Down
11 changes: 5 additions & 6 deletions app/gui/src/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::executor::setup_global_executor;
use crate::executor::web::EventLoopExecutor;
use crate::Ide;

use enso_web::Closure;
use enso_web::CleanupHandle;
use enso_web::HtmlDivElement;
use ensogl::application::test_utils::ApplicationExt;
use std::pin::Pin;
Expand Down Expand Up @@ -205,8 +205,8 @@ impl Fixture {
#[derive(Default, Debug)]
#[cfg_attr(not(target_arch = "wasm32"), allow(dead_code))]
pub struct WaitAFrame {
frame_passed: Rc<Cell<bool>>,
closure: Option<Closure<dyn FnMut(f64)>>,
frame_passed: Rc<Cell<bool>>,
frame_listener: Option<CleanupHandle>,
}

impl Future for WaitAFrame {
Expand All @@ -230,12 +230,11 @@ impl Future for WaitAFrame {
} else {
let waker = cx.waker().clone();
let frame_passed = self.frame_passed.clone_ref();
let closure = Closure::once(move |_| {
let listener = enso_web::request_animation_frame(move |_| {
frame_passed.set(true);
waker.wake()
});
enso_web::window.request_animation_frame_with_closure_or_panic(&closure);
self.closure = Some(closure);
self.frame_listener = Some(listener);
std::task::Poll::Pending
}
}
Expand Down
69 changes: 37 additions & 32 deletions app/gui/src/transport/web.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
//! web_sys::WebSocket-based `Transport` implementation.
//! web::WebSocket-based `Transport` implementation.
use crate::prelude::*;
use enso_web::traits::*;

use enso_web as web;
use enso_web::event::listener::Slot;
use failure::Error;
use futures::channel::mpsc;
use json_rpc::Transport;
use json_rpc::TransportEvent;
use wasm_bindgen::JsCast;
use web_sys::BinaryType;



Expand Down Expand Up @@ -54,7 +53,7 @@ pub enum SendingError {

impl SendingError {
/// Constructs from the error yielded by one of the JS's WebSocket sending functions.
pub fn from_send_error(error: wasm_bindgen::JsValue) -> SendingError {
pub fn from_send_error(error: web::JsValue) -> SendingError {
SendingError::FailedToSend(error.print_to_string())
}
}
Expand Down Expand Up @@ -82,18 +81,18 @@ pub enum State {

impl State {
/// Returns current state of the given WebSocket.
pub fn query_ws(ws: &web_sys::WebSocket) -> State {
pub fn query_ws(ws: &web::WebSocket) -> State {
State::from_code(ws.ready_state())
}

/// Translates code returned by `WebSocket.readyState` into our enum.
/// cf https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState
pub fn from_code(code: u16) -> State {
match code {
web_sys::WebSocket::CONNECTING => State::Connecting,
web_sys::WebSocket::OPEN => State::Open,
web_sys::WebSocket::CLOSING => State::Closing,
web_sys::WebSocket::CLOSED => State::Closed,
web::WebSocket::CONNECTING => State::Connecting,
web::WebSocket::OPEN => State::Open,
web::WebSocket::CLOSING => State::Closing,
web::WebSocket::CLOSED => State::Closed,
num => State::Unknown(num), // impossible
}
}
Expand All @@ -114,35 +113,35 @@ pub mod event {
#[derive(Clone, Copy, Debug)]
pub enum Open {}
impl Type for Open {
type Interface = web_sys::Event;
type Target = web_sys::WebSocket;
type Interface = web::Event;
type Target = web::WebSocket;
const NAME: &'static str = "open";
}

/// Represents WebSocket.close event.
#[derive(Clone, Copy, Debug)]
pub enum Close {}
impl Type for Close {
type Interface = web_sys::CloseEvent;
type Target = web_sys::WebSocket;
type Interface = web::CloseEvent;
type Target = web::WebSocket;
const NAME: &'static str = "close";
}

/// Represents WebSocket.message event.
#[derive(Clone, Copy, Debug)]
pub enum Message {}
impl Type for Message {
type Interface = web_sys::MessageEvent;
type Target = web_sys::WebSocket;
type Interface = web::MessageEvent;
type Target = web::WebSocket;
const NAME: &'static str = "message";
}

/// Represents WebSocket.error event.
#[derive(Clone, Copy, Debug)]
pub enum Error {}
impl Type for Error {
type Interface = web_sys::Event;
type Target = web_sys::WebSocket;
type Interface = web::Event;
type Target = web::WebSocket;
const NAME: &'static str = "error";
}
}
Expand All @@ -165,7 +164,7 @@ struct Model {
pub on_error: Slot<event::Error>,

// === Internal ===
pub socket: web_sys::WebSocket,
pub socket: web::WebSocket,
/// Special callback on "close" event. As it must be invoked after `on_close`, care should be
/// taken to keep it registered as an event listener *after* `on_close` registration.
/// By default `Model` takes care of it by itself.
Expand All @@ -176,8 +175,8 @@ struct Model {

impl Model {
/// Wraps given WebSocket object.
pub fn new(socket: web_sys::WebSocket) -> Model {
socket.set_binary_type(BinaryType::Arraybuffer);
pub fn new(socket: web::WebSocket) -> Model {
socket.set_binary_type(web::BinaryType::Arraybuffer);
Model {
on_close: Slot::new(&socket),
on_message: Slot::new(&socket),
Expand All @@ -190,7 +189,7 @@ impl Model {
}

/// Close the socket.
pub fn close(&mut self, reason: &str) -> Result<(), wasm_bindgen::JsValue> {
pub fn close(&mut self, reason: &str) -> Result<(), web::JsValue> {
// If socket was manually requested to close, it should not try to reconnect then.
self.auto_reconnect = false;
let normal_closure = 1000;
Expand Down Expand Up @@ -226,15 +225,15 @@ impl Model {

/// Establish a new WS connection, using the same URL as the previous one.
/// All callbacks will be transferred to the new connection.
pub fn reconnect(&mut self) -> Result<(), wasm_bindgen::JsValue> {
pub fn reconnect(&mut self) -> Result<(), web::JsValue> {
if !self.auto_reconnect {
return Err(js_sys::Error::new("Reconnecting has been disabled").into());
return Err(web::Error::new("Reconnecting has been disabled").into());
}

let url = self.socket.url();
info!("Reconnecting WS to {url}.");

let new_ws = web_sys::WebSocket::new(&url)?;
let new_ws = web::WebSocket::new(&url)?;

self.on_close.set_target(&new_ws);
self.on_error.set_target(&new_ws);
Expand Down Expand Up @@ -271,22 +270,22 @@ pub struct WebSocket {

impl WebSocket {
/// Wrap given raw JS WebSocket object.
pub fn new(ws: web_sys::WebSocket) -> WebSocket {
pub fn new(ws: web::WebSocket) -> WebSocket {
let model = Rc::new(RefCell::new(Model::new(ws)));
WebSocket { model }
}

/// Establish connection with endpoint defined by the given URL and wrap it.
/// Asynchronous, because it waits until connection is established.
pub async fn new_opened(url: &str) -> Result<WebSocket, ConnectingError> {
let ws = web_sys::WebSocket::new(url).map_err(ConnectingError::construction_error)?;
let ws = web::WebSocket::new(url).map_err(ConnectingError::construction_error)?;
let mut wst = WebSocket::new(ws);
wst.wait_until_open().await?;
Ok(wst)
}

/// Generate a callback to be invoked when socket needs reconnecting.
fn reconnect_trigger(&self) -> impl FnMut(web_sys::CloseEvent) {
fn reconnect_trigger(&self) -> impl FnMut(web::CloseEvent) {
let model = Rc::downgrade(&self.model);
move |_| {
if let Some(model) = model.upgrade() {
Expand Down Expand Up @@ -336,7 +335,7 @@ impl WebSocket {
}

/// Sets callback for the `close` event.
pub fn set_on_close(&mut self, f: impl FnMut(web_sys::CloseEvent) + 'static) {
pub fn set_on_close(&mut self, f: impl FnMut(web::CloseEvent) + 'static) {
self.with_borrow_mut_model(move |model| {
model.on_close.set_callback(f);
// Force internal callback to be after the user-defined one.
Expand All @@ -345,17 +344,17 @@ impl WebSocket {
}

/// Sets callback for the `error` event.
pub fn set_on_error(&mut self, f: impl FnMut(web_sys::Event) + 'static) {
pub fn set_on_error(&mut self, f: impl FnMut(web::Event) + 'static) {
self.with_borrow_mut_model(move |model| model.on_error.set_callback(f))
}

/// Sets callback for the `message` event.
pub fn set_on_message(&mut self, f: impl FnMut(web_sys::MessageEvent) + 'static) {
pub fn set_on_message(&mut self, f: impl FnMut(web::MessageEvent) + 'static) {
self.with_borrow_mut_model(move |model| model.on_message.set_callback(f))
}

/// Sets callback for the `open` event.
pub fn set_on_open(&mut self, f: impl FnMut(web_sys::Event) + 'static) {
pub fn set_on_open(&mut self, f: impl FnMut(web::Event) + 'static) {
self.with_borrow_mut_model(move |model| model.on_open.set_callback(f))
}

Expand All @@ -367,7 +366,7 @@ impl WebSocket {
///
/// WARNING: `f` works under borrow_mut and must not give away control.
fn send_with_open_socket<F, R>(&mut self, f: F) -> Result<R, Error>
where F: FnOnce(&mut web_sys::WebSocket) -> Result<R, wasm_bindgen::JsValue> {
where F: FnOnce(&mut web::WebSocket) -> Result<R, web::JsValue> {
// Sending through the closed WebSocket can return Ok() with error only
// appearing in the log. We explicitly check for this to get failure as
// early as possible.
Expand Down Expand Up @@ -405,6 +404,7 @@ impl Transport for WebSocket {
self.send_with_open_socket(|ws| ws.send_with_u8_array(mut_slice))
}

#[cfg(target_arch = "wasm32")]
fn set_event_transmitter(&mut self, transmitter: mpsc::UnboundedSender<TransportEvent>) {
info!("Setting event transmitter.");
let transmitter_copy = transmitter.clone();
Expand Down Expand Up @@ -435,6 +435,11 @@ impl Transport for WebSocket {
channel::emit(&transmitter, TransportEvent::Opened);
});
}

#[cfg(not(target_arch = "wasm32"))]
fn set_event_transmitter(&mut self, transmitter: mpsc::UnboundedSender<TransportEvent>) {
let _ = transmitter;
}
}

#[cfg(test)]
Expand Down
Loading

0 comments on commit c834847

Please sign in to comment.