Skip to content

Comments

fix(wasm): avoid std::time::Instant::now() and expand testing#625

Open
pmarks wants to merge 4 commits intoapache:mainfrom
pmarks:pmarks/wasm-fixes-tests
Open

fix(wasm): avoid std::time::Instant::now() and expand testing#625
pmarks wants to merge 4 commits intoapache:mainfrom
pmarks:pmarks/wasm-fixes-tests

Conversation

@pmarks
Copy link

@pmarks pmarks commented Jan 26, 2026

Proposed fix to #624 - opening for discussion, I'm happy to rework this.

Currently retry and throttle can crash on WASM due to hitting std::time::Instant::now(), and MultiPart upload relies on tokio::task::JoinSet, which doesn't work on WASM.

TODO: figure out a unit test that actually exercises RetryableRequest in WASM. Existing tests can't run because they need MockServer, which needs TcpListener.

Which issue does this PR close?

Closes #624

Rationale for this change

Solidify WASM support by fixing a couple small issues and greatly expanding WASM test coverage.

What changes are included in this PR?

  • Use n0-futures, which re-exports tokio types on native, and supplies browser-compatible versions on wasm32. Used to get a replacement for tokio::task::JoinSet, used for MultiPart uploads
  • Use sleep() from gloo-timers on wasm instead of tokio::time::sleep which relies on std::Instant::now()
  • To test WASM, we use #[wasm_bindgen_test] instead of #[tokio::test] to mark many existing tests. We can conditionally import the appropriate macro, renaming the macro as async_test then decorate the tests with #[async_test]. These tests will now run under the existing WASM CI.

Are there any user-facing changes?

The main Error enum will change for the WASM build - it will now expose n0_future::task::JoinError instead of tokio::task::JoinError - this is a breaking change.

Currently retry and throttle can crash on WASM due to hitting std::time::Instant::now(), and MultiPart upload relies on tokio::task::JoinSet, which doesn't work on WASM.  So do the following:

- Use n0-futures, which re-exports tokio types on native, and supplies browser-compatible versions on wasm32.  Used to get a replacement for tokio::task::JoinSet, used for MultiPart uploads
- Use sleep() from gloo-timers on wasm instead of tokio::time::sleep which relies on std::Instant::now()
- To test WASM, we use #[wasm_bindgen_test] instead of #[tokio::test] to mark many existing tests.  We can conditionally import the appropriate macro, renaming the macro as `async_test` then decorate the tests with #[async_test]. These tests will now run under the existing WASM CI.
@pmarks pmarks marked this pull request as ready for review January 27, 2026 08:36
pub(crate) async fn sleep(duration: Duration) {
use send_wrapper::SendWrapper;
if !duration.is_zero() {
SendWrapper::new(gloo_timers::future::sleep(duration)).await
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The inner future is not actually Send because it contains JS objects. SendWrapper makes it Send, with dynamic checking that it is not used on another thread. This works for standard wasm32-unknown-unknown, and will crash 'safely' if used across threads somehow with Web Workers, so we can figure out how to fix that later.

Copy link
Member

@kylebarron kylebarron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable to me

@crepererum
Copy link
Contributor

This is a rather high-level question for wasm32-unknown-unknown: are we assuming that this is ALWAYS a web browser / JavaScript-driven target? Because technically wasm32-unknown-unknown is -- according to the Rust docs -- a "no host" target. So this code could in theory also run within a micro kernel or alongside another WASM module in a freestanding wasmtime execution.

@kylebarron
Copy link
Member

Perhaps we should have a web named feature flag, and not automatically assume that wasm32-unknown-unknown will be run in the browser? E.g. https://github.com/cloudflare/workers-rs uses wasm32-unknown-unknown as a compilation target while I'm guessing it doesn't provide web primitives (though I don't know for sure)

@crepererum
Copy link
Contributor

Yeah, I would feel more comfortable w/ an explicit web feature. I think that would make it clearer (esp. if we document that, maybe in our top-level crate docs).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Crash in http in WASM due to hitting std::time::Instant::now()

3 participants