So the question I have is rather simple, but it has lead to me to some rabbit holes and I'm getting to darker places slowly but surely so I'm trying to get a fresh point of view from other rust users.
I have a command line tool, performing some iterative optimization algorithm that can take a bit of time (from few seconds to a minute or so). In the terminal, it's fairly easy to stop it via [Ctrl+C] if needed. But I have ported the algorithm to WebAssembly and I'm struggling to find a solution to stop the function from user input in the web app.
What I've got so far
The wasm module is loaded in a web worker to prevent the main UI thread to freeze. I can communicate from wasm to the web app via imported JS functions, that's what enabled my implementation of Log
to pass the program logs to the main thread as they occur. Below is a sreenshot of the logs while the algorithm is running and a "stop!" button that I'd like to be able to click to stop the algorithm.
I thought I could also inject a function as a parameter in my algorithm, with something looking like:
fn algorithm(should_stop: &mut dyn FnMut() -> bool) {
loop {
if should_stop() { return; }
...
}
}
And that should_stop
function would be imported from JS and would check a global value stop_order
in the worker that could be changed anytime by a user interaction in the main thread. But that's where the rabbit hole starts.
Since JS is monothreaded, I can't change that value until the algorithm finishes running, which defies its purpose. UNLESS algorithm
and should_stop
are actually async
. In such case, every call to should_stop
would give control back to the JS loop, which will execute the received worker message telling it to change that stop_order
global value and the next call to should_stop
would actually terminate the algorithm.
But I think doing that (if possible, which seems it is) means I bring the whole async
ecosystem to my crate, just because I need it in the WebAssembly port. Currently I have a workspace with <mycrate>-lib
, <mycrate>-bin
(a CLI app) and <mycrate>-wasm
to use in the web app. The algorithm
function lives in <mycrate>-lib
and I'd like to avoid bringing the whole async ecosystem to it.
So I thought I could just gate this behind a feature, but then again I'm hitting an unknown. The algoritm
function is roughly 200 lines and can't really get much shorter. I would hate having to duplicate this function to have another one (with some if should_stop() { .. }
sprinkled) under a cfg_attr
because it means the two will eventually get out of sync (pun intended ^^). Is there a simple way to do that in Rust without duplicating the function? I'm afraid not because the other function also must have a different signature starting with async fn ...
right? (I've actually never used async in Rust yet)
Is there a simpler way?
I'm getting quite far from my original goal of just being able to stop that wasm function? Is there a simpler way?
PS: here is the app in question in case interested: https://lowrr.pizenberg.fr.