Skip to content

samod: wasm runtime support#29

Open
jackddouglas wants to merge 17 commits intoalexjg:mainfrom
tonk-labs:wasm-runtime
Open

samod: wasm runtime support#29
jackddouglas wants to merge 17 commits intoalexjg:mainfrom
tonk-labs:wasm-runtime

Conversation

@jackddouglas
Copy link

• adds comprehensive WASM runtime support
• adds WASM-specific infrastructure including IndexedDB storage adapter and WebSocket transport implementation

Runtime abstraction (samod/src/runtime/)

• adds wasm backend to runtime system
• conditional compilation based on target platform (wasm32-unknown-unknown vs native)
• modified task spawning to handle non-Send futures required for WASM single-threaded executor

WASM-specific implementation

samod/src/storage/indexeddb.rs: persistent storage for browser
samod/src/websocket.rs: WS client implementation for browser environments using web-sys
samod/src/wasm.rs: WASM runtime initialization and executor setup

Testing

Playwright-based end-to-end tests for WASM builds in wasm-tests/

alexjg and others added 17 commits August 30, 2025 23:03
…ync_channel

Currently document actors run on a `rayon` thread pool and use a
blocking `std::sync::mpsc::channel` to communicate with the main thread.
I want to make the use of a thread pool optional, spawning the actors
onto the runtime if there is no thread pool configured. This means that
the communication channel between the main thread and the actors must be
capable of async operations. `async_channel` is a stable, and widely
used async channel implementation that works in both sync and async
contexts. For consistency we use it everywhere, replacing also the uses
of `futures::channel::mpsc`.
The RuntimeHandle trait has a whole bunch of requirements that we don't
actually need because we never look at the results of tasks which we
spawn, we just fire and forget.
Problem: it is not always desirable or possible to run each document
actor on its own thread. For example, in node (via WebAssembly) it's
not possible to spawn new threads.

Solution: allow document actors to be run on the async runtime provided
to `SamodBuilder`
- add `wasm` feature flag with dependencies (`wasm-bindgen`, `web-sys`, `js-sys`)                                                                                                                                     │ │
- implement `WasmRuntime` using `wasm_bindgen_futures::spawn_local`                                                                                                                                                   │ │
- add time provider abstraction for external time sources in WASM                                                                                                                                                     │ │
- update `UnixTimestamp` to use `js_sys::Date::now()` on WASM targets
- add `set_time_provider` WASM binding for WASI environments
- configure build for WASM target in `.cargo/config.toml`                                                                                                                                                             │ │
- add `WasmWebSocket` struct wrapping browser's native WebSocket API                                                                                                                                                  │ │
- implement `Stream` and `Sink` traits for async message handling                                                                                                                                                     │ │
- add `connect_wasm_websocket` method to `Repo` for browser connections                                                                                                                                               │ │
- support binary (ArrayBuffer) message format for protocol compatibility                                                                                                                                              │ │
- handle connection lifecycle with proper error propagation                                                                                                                                                           │ │
- add comprehensive documentation for WASM WebSocket usage                                                                                                                                                            │ │
Problem: in some cases it's not possible to produce a `Storage` or
`AnnouncePolicy` implementation that produces `Send` futures, which
means those futures can't be spawned onto runtimes like `tokio` which
require `Send` futures to transfer tasks between threads. This
constraint is reflected in the `RuntimeHandle::spawn` method, which
requires the spawned future to be `Send`.

Solution: add "local" variants of `RuntimeHandle`, `Storage`, and
`AnnouncePolicy`. These don't require `Send` futures and so can be used
with runtimes (such as single threaded ones like
`futures::executor::LocalPool`) that don't require `Send` futures. These
new variants can be used via the `RepoBuilder::build_local` method.
chore: update for non-send tasks
Problem: the use of the rayon threadpool option to run document actors
in paralell requires the use of the
`async_channel::Sender::blocking_send` and
`async_channel::Receiver::recv_blocking` methods, which are behind a
conditional compilation flag which is not enabled for `wasm` targets.
This means that `samod` cannot be used on `wasm` targets even if the
threadpool feature is not being used.

Solution: make the threadpool feature compile time optional behind the
`threadpool` feature flag. We also put the `blocking_send` and
`recv_blocking` methods behind the same flag so that if the `threadpool`
feature is not available `samod` can compile on `wasm` just fine. While
we're here add a newtype wrapper for unbounded channels to make it
clearer that we expect these channels to be unbounded.
@matta
Copy link

matta commented Jan 14, 2026

I take it from this PR that the current samod releases are not expected to work when running browser side (wasm)? I was experimenting with linking samod into a Dioxus app and ran into issues that this PR seems to solve.

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