wasm-bindgen icon indicating copy to clipboard operation
wasm-bindgen copied to clipboard

Panic Recovery and Module Re-loadability

Open cwfitzgerald opened this issue 1 year ago • 3 comments

Motivation

We are building a web app in rust that is designed to be a productivity tool with a large chunk of the code implemented in rust on wasm. As part of of this, we want to be robust to rust panics. Our rust wasm module is designed such that we can completely re-initialize the data if something were to go wrong. In practice though, this almost always leads to some kind of spooky behavior/UB because of the module-level globals generated by wasm-bindgen.

Proposed Solution

Our ideal solution would be some way to "clear" the wasm memory and any wasm-bindgen datastructures to that we can re-start our module with a fresh slate within the same session. In a similar way, having all the state as an object we can manage, as opposed to module-level globals, would be great.

Alternatives

Currently we tell the user to re-load the page, which is less than ideal. If there was some way to re-load modules in javascript, we could use that, but as far as I'm aware, caching will mean that all subsequent loads of a module will be the same as the original load. If there are other alternatives that I'm not aware of, do let me know!

Additional Context

Thank you for all the amazing work on wasm-bindgen and friends, it honestly keeps surprising me how well it works!

I tried to look for duplicate issues, but I didn't find any direct matches.

cwfitzgerald avatar Aug 28 '24 21:08 cwfitzgerald

I am sympathetic to this problem and had to workaround it myself as well many times.

Generally speaking, I agree this should be possible and I would even be willing to implement it as its something I would need myself as well.

However the first thing that comes to mind is that e.g. event listeners would not be removed and they would start throwing when attempting to call non-existing Wasm resources. So this would complicate things unless we intend to just let them throw.

Secondly, my assumption here is that this need is mostly caused by the lack of unwinding, otherwise the state could be recovered directly in Rust, but let me know if this doesn't apply to your use case! If this is the case, we could address this issue by just implementing unwinding support in Walrus, as LLVM and Rust already support the proposal (its old form anyway, which is what browsers implement as well).

daxpedda avatar Sep 07 '24 16:09 daxpedda

I come from the future with a completely different (but very similar) use case!

I think in both cases, implementing proper unwinding in walrus would actually fully solve the problem, at least for me. If panics left the rust binary in a sane state, as long as we make our code panic safe, we can safely continue with operation, potentially dropping all our internal state.

The case I'm hitting now is that wgpu's webassembly tests like to panic when things go wrong internally, and as our tests were designed for the native panic-on-error moniker. As it stands, there is a large amount of bizzare behavior on subsequent tests if one fails, because the rust blob has undefined internal state, causing truly brain-melting error messages.

That all being said, I do think that module reloadability would be a fairly high-impact solution to a wide variety of problem, and for production apps, being able to entirely abort and start again when builtin recovery systems are failing is a godsend.

However the first thing that comes to mind is that e.g. event listeners would not be removed and they would start throwing when attempting to call non-existing Wasm resources. So this would complicate things unless we intend to just let them throw.

I think letting them throw is fine - this is a giant hammer with giant consequences, but the alternative state (your memory is corrupt) is far worse for recoverability. In the codebase that sparked this post, we need to tell people to entirely reload their browser, which isn't great UX.

cwfitzgerald avatar Feb 13 '25 00:02 cwfitzgerald

Event handlers could be addressed by having a “drop” hook in the wasm-bindgen JS which knows how to remove the event listeners (upon the restart operation) without involving Rust code. This could be a feature that’s part of Closure, which is what’d be doing the throwing — “call this JS function if this closure becomes dead”.

kpreid avatar Feb 13 '25 01:02 kpreid