Commit 7b376d620a3d
tarball6 files changed+46-6
@@ -112,8 +112,11 @@ rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""]
112112
113113
what's different in a browser114114
115
-- a panic aborts the whole instance. natively a panicking command costs its116
- caller an `Interrupted`, here it takes the engine and everything else with it115
+- a panic doesn't abort anything. it traps the task mid poll and the rest of116
+ the engine carries on in an undefined state and goes quiet, no `Failed` and no117
+ `Stopped`. register `macltok::on_panic` and have it tear the worker down and118
+ start another. natively a panicking command just costs its caller an119
+ `Interrupted`117120
- `MediaUploadProgress` only ever reports completion, the sdk's wasm http path118121
has no upload progress119122
- the sdk doesn't retry requests or time them out individually. sync still@@ -30,8 +30,11 @@ doing the work died before it could answer. `challenge` turns up with
3030
the run loop routes and does nothing slow. every task it spawns is registered3131
with a context. a command task that panics costs its caller an `Interrupted`3232
and gets logged. a watcher or a room actor that panics takes the engine down,33
-emitting `Failed` then `Stopped`. natively, anyway. wasm aborts the whole34
-instance on any panic and nothing is left to log it33
+emitting `Failed` then `Stopped`. natively, anyway. on wasm a panic traps the34
+task mid poll and nothing is caught. the rest keeps running in an undefined35
+state and goes silent, no `Failed` and no `Stopped`, so the host hears of it36
+through `on_panic`, a free function since a panic hook belongs to the whole37
+instance rather than to one engine3538
3639
sync restarts itself with backoff after an error or an unexpected stop, and3740
`ConnectionChanged` shows `Offline` meanwhile. a run that drops again inside@@ -14,7 +14,7 @@ pub(super) fn is_panic(error: &JoinError) -> bool {
1414
error.is_panic()1515
}1616
17
-/// the sdk's wasm join error has no payload, though a panic there aborts the instance anyway17
+/// the sdk's wasm join error has no payload, and a real panic traps before it gets here1818
#[cfg(target_family = "wasm")]1919
pub(super) fn is_panic(error: &JoinError) -> bool {2020
!error.is_cancelled()@@ -145,7 +145,8 @@ impl TaskContext {
145145
}146146
}147147
148
-/// tasks catch their own panics, since the sdk's wasm join error drops the payload148
+/// tasks catch their own panics so each comes back with its context, since joining149
+/// through the sdk's executor gives no task id to look one up by. wasm has nothing to catch149150
fn panic_message(payload: Box<dyn std::any::Any + Send>) -> String {150151
payload151152
.downcast_ref::<&str>()@@ -10,6 +10,8 @@ mod normalise;
1010
mod transport;1111
mod uiaa;1212
mod upload;13
+#[cfg(target_family = "wasm")]14
+mod wasm;1315
1416
#[cfg(all(feature = "sqlite", target_family = "wasm"))]1517
compile_error!("sqlite has no filesystem to live on in wasm, use indexeddb");@@ -58,3 +60,5 @@ pub use uiaa::{
5860
InteractiveAuth, InteractiveAuthChallenge, InteractiveAuthFailure, InteractiveAuthFlow,5961
InteractiveAuthOutcome,6062
};63
+#[cfg(target_family = "wasm")]64
+pub use wasm::on_panic;@@ -0,0 +1,29 @@
1
+use std::cell::RefCell;2
+3
+type Handler = Box<dyn Fn(&str)>;4
+5
+thread_local! {6
+ static HANDLER: RefCell<Option<Handler>> = const { RefCell::new(None) };7
+}8
+9
+/// a panic on wasm32 traps the task that raised it and nothing unwinds, so the engine10
+/// catches nothing. the rest of it keeps running in an undefined state and goes silent,11
+/// no `Failed` and no `Stopped`. `handler` runs from the panic hook with the message,12
+/// after whatever hook was there before, and is the host's one chance to react, say by13
+/// terminating the worker and starting a fresh one. a second call replaces the handler14
+pub fn on_panic(handler: impl Fn(&str) + 'static) {15
+ let installed = HANDLER.with(|slot| slot.replace(Some(Box::new(handler))).is_some());16
+ if installed {17
+ return;18
+ }19
+ let previous = std::panic::take_hook();20
+ std::panic::set_hook(Box::new(move |info| {21
+ previous(info);22
+ let message = info.to_string();23
+ HANDLER.with(|slot| {24
+ if let Some(handler) = &*slot.borrow() {25
+ handler(&message);26
+ }27
+ });28
+ }));29
+}