chemicstry / wasm_thread

A rust `std::thread` replacement for wasm32 target
Apache License 2.0
123 stars 16 forks source link

Futures created with `wasm_bindgen_futures::spawn_local` don't get executed #6

Closed felipellrocha closed 2 years ago

felipellrocha commented 2 years ago

I'm spinning a thread using wasm_thread. Inside of it, I'm running some async functions inside of a wasm_bindgen_futures::spawn_local as such:

wasm_thread::spawn(move || {
  log::info!("1: spawn");
  while Ok(message) = channel_receiver.recv() {
    log::info!("2: message {:?}", &message);
    wasm_bindgen_futures::spawn_local(async move {
      log::info!("3: am i here?");
      download.asset().await;
    })
  }
});

Problem is that neither the 3rd log::info!() nor the download.asset().await get called.

Any idea why?

chemicstry commented 2 years ago

Could you be blocking browser's main thread event loop? In order for worker to spawn you either have to return from js->rust call or await on async function, so it allows browser event loop to continue.

To be more clear: busy looping in wasm main thread freezes the browser event loop. This means that no DOM events, network fetches or worker spawning can proceed. The already spawned workers should work fine though, unless they try to communicate with the main thread.

chemicstry commented 2 years ago

I think the problem here might be the channel_receiver.recv(). What kind of channel is that? Try using an async one

felipellrocha commented 2 years ago

It's a crossbeam channel. This is on a spawned thread, so it shouldn't impact the main thread no? Also, this code worked fine previously in the main thread. In addition, the documentation on spawn_local leads me to believe the async I spawned would run in the spawned thread, not the main thread: https://docs.rs/wasm-bindgen-futures/0.4.31/wasm_bindgen_futures/fn.spawn_local.html

chemicstry commented 2 years ago

I might not have explained it well, but what I said also applies to the worker event loop. At least I think so, I haven't used wasm for a while now, so don't remember all the quirks.

The docs for spawn_local say The future will always be run on the next microtask tick. However, the worker can never execute next microtask tick because it is blocked on crossbeam channel recv().

felipellrocha commented 2 years ago

I have modified it to this:

wasm_thread::spawn(move || {
  log::info!("1: spawn");
  wasm_bindgen_futures::spawn_local(async move {
    log::info!("2: message {:?}", &message);
    while Ok(message) = channel_receiver.recv().await {
      log::info!("3: am i here?");
      download.asset().await;
    }
  })
});

I still get nothing (now a tokio channel)

DouglasDwyer commented 2 years ago

@felipellrocha, I am unable to reproduce your issue with the following code:

wasm_thread::spawn(move || {
    log::info!("1: spawn");
    wasm_bindgen_futures::spawn_local(async move {
      log::info!("2: message {:?}", "hello");
    })
  });

The code produces both log messages. As such, this looks like it may be an issue with the channel implementation or event loop rather than an incompatibility in wasm_thread and wasm_bindgen_futures. I would try using a channel implementation that is independent of runtime (like async_channel), as Tokio channels might depend upon a Tokio runtime to work (which is impossible on WASM).

felipellrocha commented 2 years ago

This worked: https://github.com/rustwasm/wasm-bindgen/issues/2945 Thanks for the help :D

Liamolucko commented 2 years ago

For the record, that is quite a hacky solution; I think it'd be better if wasm_thread were to support async entrypoints, since there isn't currently any way to yield from the blocking entrypoint without shutting down the worker.

chemicstry commented 2 years ago

Closing this and tracking async entrypoints in #10