Getting error because an asynchronous function returned by tokio is Send and JsFuture from wasm_bindgen_futures has a !Send bound:
error: future cannot be sent between threads safely
--> src\util\src\timing\mod.rs:564:17
|
564 | exec_future({
| _________________^
565 | | let stopped = Arc::clone(&mut stopped);
566 | | async move {
567 | | wait(duration).await;
... |
571 | | }
572 | | });
| |_____^ future created by async block is not `Send`
|
= help: within `[async block@src\util\src\timing\mod.rs:566:9: 571:10]`, the trait `std::marker::Send` is not implemented for `Rc<RefCell<wasm_bindgen_futures::Inner>>`
note: future is not `Send` as it awaits another future which is not `Send`
--> src\util\src\timing\platform\browser_runtime.rs:51:5
|
51 | wasm_bindgen_futures::JsFuture::from(wait_in_js_promise(ms.into())).await.unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here on type `JsFuture`, which is not `Send`
note: required by a bound in `exec_future`
--> src\util\src\futures\mod.rs:45:30
|
43 | pub fn exec_future<F>(future: F)
| ----------- required by a bound in this function
44 | where
45 | F: Future<Output = ()> + Send + 'static,
| ^^^^ required by this bound in `exec_future`
How to unsafely cast that JsFuture to a Send one?
I'm wondering if I could wrap JsFuture in some way:
The Send bound is for transferring a value to another thread and a core principle of JavaScript is that it is single-threaded. That means no objects from JavaScript will implement Send.
Of course, you can probably work under the assumption that objects from your WebAssembly code will only ever stay on the main JavaScript thread, so it's fine to create a wrapper with an unsafe impl Send. However that unsafe assumption is now your responsibility to maintain.
struct Sendable<T>(pub T);
// Safety: WebAssembly will only ever run in a single-threaded context.
unsafe impl<T> Send for Sendable<T> {}
For example, if you decided you'd like to implement multi-threaded WebAssembly by spinning up web workers and using a common SharedArrayBuffer for their backing memory, then you'd need to go back and rethink whether your wrapper is still valid.
Didn't know about unsafe impl, useful! For workers, I'll never expose that "private" sendable future (I'm also planning to have a rialight::javascript API for communicating with the host browser, which will just alias wasm_bindgen, js_sys and other crates). (Rialight won't take care of workers, so if the developer wants... they'd need rialight::javascript and use them carefully)
Now I've this issue:
pub async fn wait(duration: Duration) {
let ms: u32 = duration.as_millis().try_into().expect("Developer has given too large period for wait duration");
crate::futures::browser::SendableJsFuture::from(wait_in_js_promise(ms.into())).await.unwrap();
}
At my background_timeout function, my async block is still not Send due to the created Promise in the browser implementation. Full diagnostic:
error: future cannot be sent between threads safely
--> src\util\src\timing\mod.rs:564:17
|
564 | exec_future({
| _________________^
565 | | let stopped = Arc::clone(&mut stopped);
566 | | async move {
567 | | wait(duration).await;
... |
571 | | }
572 | | });
| |_____^ future created by async block is not `Send`
|
= help: within `[async block@src\util\src\timing\mod.rs:566:9: 571:10]`, the trait `std::marker::Send` is not implemented for `*mut u8`
note: future is not `Send` as this value is used across an await
--> src\util\src\timing\platform\browser_runtime.rs:51:84
|
51 | crate::futures::browser::SendableJsFuture::from(wait_in_js_promise(ms.into())).await.unwrap();
| ----------------------------- ^^^^^ - `wait_in_js_promise(ms.into())` is later dropped here
| | |
| | await occurs here, with `wait_in_js_promise(ms.into())` maybe used later
| has type `Promise` which is not `Send`
note: required by a bound in `exec_future`
--> src\util\src\futures\mod.rs:45:30
|
43 | pub fn exec_future<F>(future: F)
| ----------- required by a bound in this function
44 | where
45 | F: Future<Output = ()> + Send + 'static,
| ^^^^ required by this bound in `exec_future`
Here's background_timeout (which is equivalent to setTimeout and can be cancelled):
This can be trouble, because for one target export, some functions return a Send future, and for the browser export, these same functions would return a !Send future.
Hmm...
Anyway, I was forced to define exec_future as this:
I replaced tokio::task::spawn by tokio::task::spawn_local inside exec_future and that solved the issue (I mean, it compiles, but it doesn't work in the playground). Can anyone say whether this is good or bad?
Also, is it worriable that something like rialight::util::timing::wait returns a future with Send on some platforms and, in the browser, a future with !Send?
Is there still a way to not having to .await into such for extra performance gain? For a wait, the .await isn't critical, but it may be critical for intervals.
I don't get what you mean by that. Async code is not inherently slower than sync code. You can't magically gain performance by converting async code to sync (or vice versa) alone.
Probably not, since there's no reason to return from the current future if the awaited one immediately returns Poll::Ready. And functions with constant output are generally optimized away - e.g. this function -