Skip to content

TorusStudios/webstd

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

29 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

webstd

webstd is a portable std replacement for programs that target both native and browser WASM.

The crate re-exports a portable subset of std via webstd_core, and adds browser implementations of APIs such as env, io, process, thread, and time. It also includes a simple async runtime for working with async tasks, and synchronization primitives for shared memory access.

A goal of the crate is to provide maximum symmetry between native and browser implementations.

webstd is not a perfect drop-in replacement for std, but the APIs it provides are designed to mirror std closely.

Blocking and Async

Rather than requiring async code in every location where a lock is acquired, webstd has the philosophy of blocking for short-lived contention, and using async for operations that are likely to wait for longer periods of time.

For example, webstd::sync provides a Mutex which spins on the main thread if contention occurs, while the JoinHandle returned when spawning a thread must be awaited as a future rather than using a blocking join.

This results in a significantly simpler multi-threaded programming model while still being able to provide a symmetric API for both native and browser.

Process Model

Native Rust programs inherit process state from the host operating system including arguments, environment variables, standard I/O streams, and program exit status.

These concepts do not normally exist in browser WASM, however webstd simulates via a JavaScript options object:

type Options = {
  env?: Record<string, string>;
  args?: string[];
  stdout?: OutputStream | ((text: string) => void);
  stderr?: OutputStream | ((text: string) => void);
};

type OutputStream = {
  write: (chunk: Uint8Array) => void;
  flush?: () => void;
};

This can then be passed to the program's entrypoint:

import init, { run } from "./pkg/main.js";

await init({ module_or_path: "./pkg/main_bg.wasm" });

const exitCode = await run({
  env: { EXAMPLE_ENV: "example value" },
  args: ["example", "argument"],
  stdout: console.log,
  stderr: console.error,
});

If environment variables or arguments are provided, they will be available to the program as normal via familiar APIs such as webstd::env::args(), webstd::env::var().

Additionally, webstd::println!, webstd::eprintln!, etc. will be routed to any provided stdout and stderr implementations, and any exit code returned to JavaScript.

Function targets receive UTF-8 text batched at the end of the current synchronous execution turn. Each main or worker context batches independently, and worker batches remain separate callback invocations when they reach the main context. Object targets receive byte chunks directly.

Entrypoints

Browser WASM programs typically require something like #[wasm_bindgen(start)] to mark the entrypoint to be used by JavaScript. The program is then built as a library, rather than through the main entrypoint.

For symmetry between native and browser builds, webstd provides two macros which are intended to be used together with main calling the lib entrypoint:

// src/main.rs
#[webstd::main]
async fn main() -> webstd::process::ExitCode {
  my_crate::run().await
}
// src/lib.rs
use webstd::{prelude::*, process::ExitCode};

#[webstd::lib]
pub async fn run() -> ExitCode {
  // Shared native and browser startup.
  ExitCode::SUCCESS
}

If the rt feature is enabled, #[webstd::main] will initialize the async runtime.

#[webstd::lib] generates the browser-facing WASM export and performs process initialization from the options object.

Modules

Many std modules are portable as-is. For a few modules that depend on host runtime behavior, webstd provides browser-compatible implementations:

webstd::backtrace

Captures browser backtraces from JavaScript Error().stack, while preserving the familiar environment-variable controls such as RUST_BACKTRACE.

webstd::env

Stores Wasm args and environment variables in process-global browser-side state initialized by the entrypoint.

webstd::io

Routes println! and eprintln! through configured browser output streams.

webstd::sync

Provides synchronization primitives such as Mutex, RwLock, Condvar which spin on the main thread in the browser if contention occurs.

Additionally, webstd::sync provides a oneshot channel, and an async mpsc channel.

webstd::process

Provides portable exit-code handling for entrypoint return values.

webstd::thread

Uses Web Workers on WASM to spawn threads. Returns a JoinHandle which is a Future, so joining a worker is awaited rather than blocking the current thread.

webstd::time

Uses browser time APIs for Instant and SystemTime.

Runtime

webstd_rt provides a small, thread-local async runtime.

Tasks

Async tasks can be spawned with webstd::rt::task::run:

use webstd::rt::task::run;

let handle = run(async {
  // Async task body.
});

// Do something else...

// Await the task.
let result = handle.await;

When the rt feature is enabled, both the entrypoint and new threads will automatically initialize a local runtime, allowing tasks to be spawned regardless of context.

Timers

Sleep and Interval timers are also provided from webstd::time:

use webstd::time::{Duration, sleep, interval};

// Wait for 1 second
sleep(Duration::from_secs(1)).await;

// Tick every second
let mut interval = interval(Duration::from_secs(1));
loop {
  let tick = interval.tick().await;
  // ...
}

Feature Flags

Available features:

  • macros: enables #[webstd::main] and #[webstd::lib].
  • rt: enables webstd::rt.

The webstd crate enables both the rt and macros features by default.

no_std and Portability

Portability can be enforced by using #![no_std] in the crate root. This prevents accidental use of non-portable native std APIs, but is not required.

Additionally, webstd provides a prelude, which includes webstd::println!, Vec, Result, and other items that are globally accessible when using std.

#![no_std]

use webstd::prelude::*;

About

A portable std replacement for programs that target both native and browser WASM.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Contributors