Async executor for WebAssembly

Overview

Async Executor for WebAssembly

Crate API Chat

There are a number of async task executors available in Rust's ecosystem. However, most (if not all?) of them rely on primitives that might not be available or optimal for WebAssembly deployment at the time.

Usage

Include this dependency in your Cargo.toml:

[dependencies]
wasm-rs-async-executor = "0.9.0"

wasm-rs-async-executor is expected to work on stable Rust, 1.49.0 and higher up. It may also work on earlier versions. This hasn't been tested yet.

Supported targets

Currently, it's been only tested on wasm32-unknown-unknown and, excluding cooperative functionality, it passes tests under wasmtime and wasmer with wasm32-wasi target. Further testing is to be completed.

Notes

Please note that this library hasn't received much analysis in terms of safety and soundness. Some of the caveats related to that might never be resolved completely. This is an ongoing development and the maintainer is aware of potential pitfalls. Any productive reports of unsafeties or unsoundness are welcomed (whether they can be resolved or simply walled with unsafe for end-user to note).

FAQ

Q: Why not just use wasm-bindgen-futures?

A: (short) In many cases, wasm-bindgen-futures is just fine

A: (longer) There are some subtle differences in the way wasm-rs-async-executor exposes its functionality. The core idea behind it is to expose a reasonable amount of explicit controls over the its operations. You need to run the executor explicitly, you can block on futures (again, explicitly -- which gives you a limited ability to do scoped futures). It does come with minor trade-offs. For example, if you want to use async APIs from your host environment, you can't simply await on then, as the executor won't yield to the browser until told to do so. However, this is typically solved by simply running a permanent task that loops yielding to the browser.

Ultimately, if this amount of control is beneficial for your case, then perhaps this executor is waranted. It is also important to note that currently wasm-rs-async-executor does not support WebAssembly multi-threading in any way. wasm-bindgen-futures does, if the standard library is built with support for it. It is planned to support this, but hasn't been high priority so far due to current state of things in Rust.

License

Licensed under either of

Comments
  • Problem: hard to test cooperative execution

    Problem: hard to test cooperative execution

    This is because it relies on a scheduler outside of our domain (JavaScript)

    Solution: allow to run a callback after the queue is exhausted

    This changes single_threaded::run_cooperatively type signature a bit, adding an extra parameter.

    The testing is still not perfect as we can't really ensure the callback was called, but if it was, then we can run the assertions.

    This can be alternatively done by something like join_all for all futures representing tasks scheduled and spawning another task to do something after they've been joined, but it seems at the time that adding this callback has lower impact / easier ergonomics (but, of course, it's less "pure" if you may)

    opened by yrashk 1
  • Problem: block_on API returns an Option

    Problem: block_on API returns an Option

    This is confusing (why would it be an Option?).

    Solution: make it return the value

    It looks like we can pretty much guarantee that the receiver stays non-dropped during the entire lifetime of block_on, hence sending a oneshot message from the spawned task will be successful and the receiver will be able to retrieve the data.

    opened by yrashk 0
  • Problem: WASM runtimes' dependencies

    Problem: WASM runtimes' dependencies

    Under certain circumstances, browser or JavaScript related dependencies might be pulled in for targets like wasm32-wasi. This is undesirable.

    Solution: separate wasm32-unknown-unknown dependencies

    opened by yrashk 0
  • Problem: unknown whether it works with WASI

    Problem: unknown whether it works with WASI

    Solution: include CI tests for wasi target using wasmtime and wasmer

    This change also makes sure cooperative won't be enabled for wasm32-wasi since it's currently specific JavaScript environment.

    opened by yrashk 0
  • Problem: returning results from tasks

    Problem: returning results from tasks

    It can be done but requires manual instrumentation of it with a oneshot channel or something like that.

    Solution: instrument all spawned tasks with this approach and allow joining to retrieve the result

    This follows the API pattern in Tokio.

    opened by yrashk 0
  • Problem: figuring out when task is complete

    Problem: figuring out when task is complete

    This requires either cooperation with the task itself or introspecting into task list, neither of which is succinct.

    Solution: make Task implement Future to allow for joining

    opened by yrashk 0
  • Problem:

    Problem: "token" term leaking in some APIs

    This token thing is really internal to the design of the executor yet it leaks into some semi-public APIs. It's confusing and unnecessary.

    Solution: unify everything under the term of "task" and carry the type information in Task (if debug is enabled)

    opened by yrashk 0
  • Problem: panics in some yielding futures

    Problem: panics in some yielding futures

    Particularly, yield_animation_frame and yield_until_idle might panic when checking for the ouput, trying to unwrap a value.

    Solution: de-reference output before checking it for is_some() instead of taking as_ref as that means is_some() is checked on the pointer, not the target value.

    opened by yrashk 0
  • Problem: `run_cooperatively` is rather limiting

    Problem: `run_cooperatively` is rather limiting

    It only allows to run tasks cooperatively under setTimeout and will always execute just one iteration before yielding back to the browser, therefore taking control of the behaviour. It would have been nice to have more control over yielding in the tasks themselves.

    Solution: introduce manual yielding primitives single_threaded::yield_timeout, single_threaded::yield_async and single_threaded::yield_animation_frame

    These functions return futures that first yield to the environment using setTimemout or requestAnimationFrame, respectively. These futures will be ready and will produce the output of the enclosed future (if there's one) once yielded and those future produced the output.

    This allows tasks to control the yielding to a (hopefully) sufficient degree.

    Also, this change saves until argument to run at the yield/resume threshold, so the executor will stop if until is specified to be a certain task when that task is done.


    This PR obsoletes #14

    opened by yrashk 0
  • Problem: run_cooperatively marked as `unsafe`

    Problem: run_cooperatively marked as `unsafe`

    Now, I am not 100% sure it is safe -- simply because I've only spent a few days on this library.

    I've published it as unsafe at first because I do remember transmuting some futures into a static lifetime somewhere higher up in the code :)

    But the reality is that spawn itself requires task's future to be of static lifetime. Which is how it is in wasm-bindgen-future. Which is how it should be.

    The only time we're actually extending lifetime is when we block on a future to run it to completion under the assumption that since it will run to completion at that point, it is safe to do so (whether I am actually correct is something to be seen)

    Solution: remove the unsafe marker

    It doesn't 100% guarantee I am right, but this is the best I can come up with right now.

    opened by yrashk 0
  • Problem: interacting with the JavaScript environment of the thread

    Problem: interacting with the JavaScript environment of the thread

    Running Rust thread in the browser using single_threaded::run locks the thread for good. One can call JavaScript functions from it but can't really expect asynchronous APIs to work.

    This is not always the most desirable trade-off.

    Solution: introduce run_cooperatively function

    It works the same way as run, however, after just one iteration of processing its task queue, it relinquishes control to JavaScript and schedules its own execution (provided it has not reached requested outcome).

    opened by yrashk 0
  • Example fails to compile

    Example fails to compile

    > wasm-pack build
    [INFO]: 🎯  Checking for the Wasm target...
    [INFO]: 🌀  Compiling to Wasm...
       Compiling async-executor-demo v0.1.0 (/Users/kyle/github/rust/async-executor/example)
    error[E0308]: mismatched types
      --> example/src/lib.rs:71:24
       |
    71 |     executor::run(Some(task1));
       |                        ^^^^^ expected struct `Task`, found struct `TaskHandle`
       |
       = note: expected struct `Task`
                  found struct `TaskHandle<()>`
    
    For more information about this error, try `rustc --explain E0308`.
    error: could not compile `async-executor-demo` due to previous error
    Error: Compiling your crate to WebAssembly failed
    Caused by: failed to execute `cargo build`: exited with exit status: 101
      full command: "cargo" "build" "--lib" "--release" "--target" "wasm32-unknown-unknown"
    
    > cargo --version
    cargo 1.60.0 (d1fd9fe2c 2022-03-01)
    > rustc --version
    rustc 1.60.0 (7737e0b5c 2022-04-04)
    
    opened by kylebarron 2
  • there is no reactor running, must be called from the context of a Tokio 1.x runtime

    there is no reactor running, must be called from the context of a Tokio 1.x runtime

    Sorry, but I am confused. I am using wasm_rs_async_executor::single_threaded::block_on, and I believe it is causing this runtime error: there is no reactor running, must be called from the context of a Tokio 1.x runtime.

    Is wasm_rs_async_executor designed to be used without tokio, or does it require tokio?

    opened by lastmjs 2
Owner
wasm.rs
A collection of crates, a community about WebAssembly + Rust
wasm.rs
The feature-rich, portable async channel library

The feature-rich, portable async channel library > crates.io > docs.rs Why use Postage? Includes a rich set of channels. | barrier | broadcast | dispa

Austin Jones 221 Dec 26, 2022
Mix async code with CPU-heavy thread pools using Tokio + Rayon

tokio-rayon Mix async code with CPU-heavy thread pools using Tokio + Rayon Resources Documentation crates.io TL;DR Sometimes, you're doing async stuff

Andy Barron 74 Jan 2, 2023
Another Async IO Framework based on io_uring

kbio, the Async IO Framework based on io_uring, is used in KuiBaDB to implement async io. Features Support multi-threading concurrent task submission.

KuiBaDB 59 Oct 31, 2022
async-alloc-counter measures max allocations in a future invocation

async-alloc-counter measures max allocations in a future invocation see examples/ for usage This allocator can be used as follows: use async_alloc_cou

Geoffroy Couprie 2 Dec 3, 2021
Golang like WaitGroup implementation for sync/async Rust.

wg Golang like WaitGroup implementation for sync/async Rust.

Al Liu 8 Dec 6, 2022
BPF library for Async Rust, complementary for libbpf-rs.

libbpf-async A library for writing BPF programs in Async Rust, complementary for libbpf-rs, Rust wrapper for libbpf. Currently, this provides Async-fr

FUJITA Tomonori 13 Nov 9, 2022
Async `TryFrom/TryInto` traits

async-convert Async TryFrom/TryInto traits API Docs | Releases | Contributing Installation $ cargo add async-convert Safety This crate uses #![deny(un

Yosh 4 Mar 4, 2022
Simple async codec for rkyv. Reuses streaming buffer for maximum speed

rkyv_codec Simple async codec for rkyv. Reuses streaming buffer for maximum speed! This crate provides a makeshift adaptor for streaming &Archived<Obj

Zyansheep 19 Jun 14, 2022
You can name anonymous Future from async fn without dyn or Box!

rename-future You can name anonymous Future from async fn without dyn or Box! PLEASE READ THIS THIS PROJECT NOT YET WELL TESTED! DON'T USE THIS IN PRO

Jun Ryung Ju 54 Sep 17, 2022
The most fundamental type for async synchronization: an intrusive linked list of futures

wait-list This crate provides WaitList, the most fundamental type for async synchronization. WaitList is implemented as an intrusive linked list of fu

Sabrina Jewson 7 Oct 26, 2022
Ector is an open source async, no-alloc actor framework for embedded devices

Ector is an open source async, no-alloc actor framework for embedded devices. Ector is an open source async, no-alloc actor framework for embedded dev

Drogue IoT 11 Dec 15, 2022
Async variant of Tonic's `interceptor` function

Tonic Async Interceptor This crate contains AsyncInterceptor, an async variant of Tonic's Interceptor. Other than accepting an async interceptor funct

Arcanyx Technical Wizardry LLC 4 Dec 20, 2022
Use winit like the async runtime you've always wanted

async-winit Use winit like the async runtime you've always wanted. winit is actually asynchronous, contrary to popular belief; it's just not async. It

John Nunley 17 Apr 23, 2023
Rust library for one-time async initialization

async-lazy An async version of once_cell::sync::Lazy, std::sync::OnceLock or lazy_static. Uses tokio's sychronization primitives. This crate offers an

Jules Bertholet 5 Jun 9, 2023
An efficient async condition variable for lock-free algorithms

async-event An efficient async condition variable for lock-free algorithms, a.k.a. "eventcount". Overview Eventcount-like primitives are useful to mak

Asynchronics 3 Jul 10, 2023
🗑Async-dropper is probably the least-worst ad-hoc AysncDrop implementation you've seen so far.

?? async-dropper async-dropper is probably the least-worst ad-hoc AsyncDrop implementation you've seen, and it works in two ways: async_dropper::simpl

Victor Adossi ( 18 Aug 6, 2023
A safe sync/async multi-producer, multi-consumer channel

Loole A safe async/sync multi-producer multi-consumer channel. Producers can send and consumers can receive messages asynchronously or synchronously:

Mahdi Shojaee 50 Oct 6, 2023
Graceful shutdown util for Rust projects using the Tokio Async runtime.

Shutdown management for graceful shutdown of tokio applications. Guard creating and usage is lock-free and the crate only locks when: the shutdown sig

Plabayo 54 Sep 29, 2023
Async Rust cron scheduler running on Tokio.

Grizzly Cron Scheduler A simple and easy to use scheduler, built on top of Tokio, that allows you to schedule async tasks using cron expressions (with

Ivan Brko 4 Feb 27, 2024