Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add WASI support by tokio_wasi #11

Merged
merged 7 commits into from
Dec 11, 2023
Merged

Conversation

langyo
Copy link
Contributor

@langyo langyo commented Nov 20, 2023

Description

I've been trying to use yew to render the page into the static HTML string on WASI. However, prokio cannot distinguish the browser WASM target (wasm32-unknown-unknown with wasm-bindgen) and WASI target (wasm32-wasi), and it would choose wrong modules for wasm32-*.

This pull request consists of the following changes:

  • Add the new runtime module which supported by tokio_wasi for wasm32-wasi target.

Checklist

  • I have self-reviewed and tested this pull request.
  • I have added tests for my changes.
  • I have updated docs to reflect any new features / changes in this pull request.
  • I have added my changes to CHANGELOG.md.

@ranile
Copy link
Member

ranile commented Dec 7, 2023

@futursolo Can you give this a review?

Cargo.toml Outdated Show resolved Hide resolved
Copy link
Member

@futursolo futursolo left a comment

Choose a reason for hiding this comment

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

Thank you for the pull request.

However, I feel we cannot support WASI as a spawnable runtime (Runtime::new_...) until multi-threading is properly supported as prokio requires the runtime to continuing resolve tasks spawned with spawn_local(). This requires either dedicated worker thread or a runtime that already runs for the lifetime of the program (#[tokio::main(flavor = “current_thread")] + LocalSet).

However, you can run tokio::task::LocalSet::run_until() in the main() function (See tokio documentation about run_until).
After that, if we add feature flags to redirect wasm32-wasi to use tokio instead of wasm-bindgen, prokio should work under WASI.

We can make Runtime a shim that redirects to tokio::task::spawn_local.

where
F: Future<Output = ()> + 'static,
{
RUNTIME.block_on(async { f.await })
Copy link
Member

Choose a reason for hiding this comment

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

To spawn a !Send future, you need to combine a LocalSet and a Runtime.
You can see how this is being done in the current tokio runtime.

block_on cannot be called in asynchronous execution context, which effectively prevents tasks from spawning.

Comment on lines 7 to 12
static RUNTIME: Lazy<tokio::runtime::Runtime> = Lazy::new(|| {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
});
Copy link
Member

@futursolo futursolo Dec 8, 2023

Choose a reason for hiding this comment

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

If a platform has no multi-threading support, i.e.: a thread runs on the main thread, the runtime should already be running via #[tokio::main]. Creating a different runtime assumed to be run in the same thread is not going to work as calling block_on effectively blocks the outer runtime.

The correct way to run a runtime that can execute a !Send future in the main thread is to call tokio::task::LocalSet::run_until() in main().

See: https://docs.rs/tokio/latest/tokio/task/struct.LocalSet.html#method.run_until

@langyo
Copy link
Contributor Author

langyo commented Dec 8, 2023

I did some research...Although I don’t quite understand your intention, we can indeed use run_until directly in a WASI program that already uses #[tokio::main(flavor = “current_thread”)].

However, I created this PR to support yew being able to do SSR in a WASI environment. In order to do this, I modified many upstream libraries, including this repository.

When yew implements SSR rendering internally, it will create additional threads through prokio for node parsing. This seems to be a good way to speed up rendering, but doing so directly makes the original rendering logic unable to run in the WASI environment...After all, WASI has not yet incorporated multi-threading into the standard.

For implement the SSR rendering capability in WASI first, I currently added the function render_async() to another PR associated with this PR in yew. This function will render nodes serially without creating any additional asynchronous tasks.

The above function has achieved my goal and is already running in my current private business code. Maybe this PR needs to be modified to only ensure that wasm_bindgen is not introduced when compiling with WASI to avoid global symbol export pollution. I may only be able to avoid single-threaded rendering of asynchronous tasks through my new render_async() method before WASI officially supports multi-threading.

I've delete the incomplete WASI runtime implementation I implemented and continue to use the current tokio module. The only change currently by this PR is the correct handling of WASI compilation flags.

Umm...And don't forget my yew's PR please...

cc @futursolo

@futursolo
Copy link
Member

futursolo commented Dec 8, 2023

When yew implements SSR rendering internally, it will create additional threads through prokio for node parsing. This seems to be a good way to speed up rendering, but doing so directly makes the original rendering logic unable to run in the WASI environment...After all, WASI has not yet incorporated multi-threading into the standard.

The reason why it is unable to do so is not because of lack of multi-threading but it assumes wasm-bindgen-futures instead of tokio for wasm32 targets.

This seems to be a good way to speed up rendering, but doing so directly makes the original rendering logic unable to run in the WASI environment...After all, WASI has not yet incorporated multi-threading into the standard.
For implement the SSR rendering capability in WASI first, I currently added the function render_async() to yewstack/yew#3534 associated with this PR in yew. This function will render nodes serially without creating any additional asynchronous tasks.

I intended to address render_async in the Yew pull request.
The purpose of the task spawning in ServerRenderer is not to speed up rendering but to convert a !Send Future to a Send one. Most tokio web frameworks require their futures to be Send and wasm_bindgen generated types are !Send, which makes Yew !Send.

You can see below what happens if you try to use the _async functions.

image

Instead of adding _async flavour, you should use LocalServerRenderer if you do not need Send futures and ServerRender if you need Send futures.

I may only be able to avoid single-threaded rendering of asynchronous tasks through my new render_async() method before WASI officially supports multi-threading.

The ServerRenderer avoids single-threading by pinning the renderer to a worker thread that is least busy via spawn_pinned, which achieves multi threading when there is multiple worker threads in the prokio runtime.

langyo added a commit to langyo/yew that referenced this pull request Dec 9, 2023
@langyo
Copy link
Contributor Author

langyo commented Dec 9, 2023

When yew implements SSR rendering internally, it will create additional threads through prokio for node parsing. This seems to be a good way to speed up rendering, but doing so directly makes the original rendering logic unable to run in the WASI environment...After all, WASI has not yet incorporated multi-threading into the standard.

The reason why it is unable to do so is not because of lack of multi-threading but it assumes wasm-bindgen-futures instead of tokio for wasm32 targets.

This seems to be a good way to speed up rendering, but doing so directly makes the original rendering logic unable to run in the WASI environment...After all, WASI has not yet incorporated multi-threading into the standard.
For implement the SSR rendering capability in WASI first, I currently added the function render_async() to yewstack/yew#3534 associated with this PR in yew. This function will render nodes serially without creating any additional asynchronous tasks.

I intended to address render_async in the Yew pull request. The purpose of the task spawning in ServerRenderer is not to speed up rendering but to convert a !Send Future to a Send one. Most tokio web frameworks require their futures to be Send and wasm_bindgen generated types are !Send, which makes Yew !Send.

You can see below what happens if you try to use the _async functions.

image Instead of adding `_async` flavour, you should use `LocalServerRenderer` if you do not need `Send` futures and `ServerRender` if you need `Send` futures.

I may only be able to avoid single-threaded rendering of asynchronous tasks through my new render_async() method before WASI officially supports multi-threading.

The ServerRenderer avoids single-threading by pinning the renderer to a worker thread that is least busy via spawn_pinned, which achieves multi threading when there is multiple worker threads in the prokio runtime.

Thank you for your reminder. I didn't realize there was an interface called LocalServerRenderer until you gave me specific instructions.

I made some modifications based on your instructions. Now there are no new interfaces, and the original WASI rendering example has been changed to use LocalServerRenderer for rendering.

Perhaps this interface should be specifically pointed out in the offical guide to remind latecomers... I've add some description on the guide.

Cargo.toml Outdated Show resolved Hide resolved
Copy link
Member

@futursolo futursolo left a comment

Choose a reason for hiding this comment

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

Thank you! Looks good to me.

The Runtime struct for tokio backend still needs some adjustments before it can properly support WASI without threading support, I will do this in a separate pull request.

After that, we can release prokio’s wasi support.

@futursolo futursolo merged commit 83d97d4 into yewstack:master Dec 11, 2023
3 checks passed
@langyo
Copy link
Contributor Author

langyo commented Dec 21, 2023

As #12 cannot be implemented for a while, could you release a patch version now to support yew to run normally in the WASI environment?

@futursolo

ranile added a commit to yewstack/yew that referenced this pull request Oct 21, 2024
* Try to add wasi feature to avoid browser's ABI.

* Add async render for single-threaded env.

* Temporarily enable my own patch branch.
It would be modified later
after the corresponding library branches are merged.

* add example for WASI SSR.

* Ready to run WASI on wasmtime.

* complete the example

* fix fmt

* fix fmt

* I made a mistake..sry

* add yew-router suites for demo

* fix typo

* Make the async render stream function public

* Use target_os instead of feature.

* Renew gloo-history's patch.

* Exclude WASI example to avoid web-sys.

* Try to add CI for WASI example.

* Fix CI.

* Fix CI that requires compiler 1.67 or newer.

* Use CLI's flag instead of exclude example.
bytecodealliance/wasmtime#4312

* Remove patchs.

* Use LocalServerRenderer instead of ServerRenderer.
yewstack/prokio#11 (comment)

* Remove unused exports.

* Add description about `LocalServerRenderer`.

* fix fmt

* fix fmt

* Update Cargo.lock

* Bump rust compiler's version to 1.67...

* Exclude WASI on yew-router browser interfaces.

* fix fmt

* Wait for gloo's PR dealed.

* Rollback to rust compiler 1.64.
cc rustwasm/gloo#423 (comment)

* Fix lock file.

* Downgrade `toml_datetime` version.

* Fix enum for `gloo-history`.

* Well, it seems there is no way to avoid the MSRV upgrade....

* fix: Replace feature = "wasi" to target_os = "wasi".

* Remove tips for rust version.

* Bump `gloo` to 0.11.

* Try to test yew-macro on compiler 1.67.

* Try to use compiler 1.68 instead.

* Try to use compiler 1.69 instead......

* Revert MSRV back

* Pin the oldest Cargo.lock.

* Downgrade deps for MSRV.

* Bump benchmark tool's tokio to 1.35

* Try to write WASI CI.

* Rollback the quotes

* Combine CI files...

* Rollback the use that gloo-history has fixed it.

* fix

* Bump gloo-history version.

* Block raw html update tests on WASI.

* Rollback indexmap's version.

* fix CI

* fix CI

* Update some SSR test suites that replace ServerRender instead of LocalServerRender.

* Remove yew-router's cfg macro

* Fix fmt

* Try to fix CI

* Update examples/wasi_ssr_module/README.md

Co-authored-by: Elina <imelina@elina.website>

* Revert back some unnecessary changes.

* Clippy

* fmt

* Fix CI.

* Fix CI.

* Try to fix clippy.

* Fix `ToString` trait.

* Remove pin version of WASI CI test.

* Pin the newer version.

* Fix typo.

* Bump `wasm-bindgen`.

* Fix SSR example.

* Fix typo.

* Try to support non-browser environments.

* Update wasm-bindgen-test to 0.3.43

refer to rustwasm/wasm-bindgen#4083

* fix doc test running on nightly

* Update website/docs/advanced-topics/server-side-rendering.md

Co-authored-by: WorldSEnder <WorldSEnder@users.noreply.github.com>

* Update WASI CI.

* Remove WASI test for rustc 1.76.

* Try to let `wasmtime` CLI can be executed.

* Limit the function `decode_base64` that it shouldn't runnable in non-browser environment.

* Remove WASI example test for rustc 1.76.

* Revert changes.

* Fix CI

* Fix Cargo.lock

* Remove unused deps

* Undo the formatting changes.

* Undo the formatting changes.

---------

Co-authored-by: Elina <imelina@elina.website>
Co-authored-by: Martin Molzer <WorldSEnder@users.noreply.github.com>
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.

3 participants