Expand description
Cooperative threads for games with their own loop (design/threads.md), the Rust
side of the C SDK’s gasm_thread.h: the same scheduler, the same rules.
Every thread runs on the guest’s one wasm thread, until it blocks, yields or waits for the next frame; then the next ready thread runs, in creation order. A frame ends when no thread is ready. So there are no data races, and the schedule depends only on the input: runs stay reproducible on every runner.
Threads need threaded_main_loop! (and Binaryen’s
wasm-opt --asyncify, as main_loop!); spawn panics elsewhere. The locks
in crate::sync work everywhere.
fn run() -> i32 {
let loader = gasm::thread::spawn(|| load_level("e1m1")); // may wait for frames
while !loader.is_finished() { draw_loading_screen(); gasm::main_loop::wait_frame(); }
let level = loader.join();
...
}
gasm::threaded_main_loop!(run);How it works: each thread is suspended with Asyncify into its own buffer and has
its own shadow stack. Asyncify leaves the wasm stack pointer alone both ways (an
unwind skips the functions’ epilogues, a rewind their prologues), so a suspended
thread’s frames are everything above the stack pointer it had when it unwound:
the scheduler records it then, sets it back before rewinding the thread, and runs
its own calls on a stack of its own (gasm__get_sp/gasm__set_sp: sp/gasm_sp.s).
Differences from std::thread: closures needn’t be Send (it is one wasm
thread), thread_local! values are shared by all threads, time is the frame’s
(sleep ends at the first frame at or after its deadline), and a thread that
spins without blocking or yielding keeps the frame from ending.
Structs§
- Builder
- Thread options (the stack size).
- Join
Handle - The handle of a spawned thread:
joinwaits for its result. Dropping it detaches the thread (it runs to the end on its own). - Thread
Id - A thread’s number: main is 1, then in creation order.
Constants§
- DEFAULT_
STACK - A thread’s default stack (its locals, the shadow stack) and Asyncify buffer.
- FOREVER
- “No deadline” for
block.
Functions§
- available
- Whether cooperative threads run (inside
threaded_main_loop!). - block
- Wait until
wakeis called ononordeadline_ms(crate::time_mstime,FOREVER: none) passes; true if it timed out. For building locks:onis any address that identifies what is waited for. Without the scheduler, a wait with a deadline times out at once and one without is a deadlock (trap). - current
- The running thread’s id.
- frames
- Frames the threaded loop has run.
- sleep
- Wait at least
dof frame time (other threads run meanwhile). - spawn
- Start a thread running
f(seeBuilder::spawn). - wait_
frame - Wait for the next frame (the same as
main_loop::wait_framein a threaded loop). - wake
- Make threads blocked on
onready (the first one in creation order, or all). - yield_
now - Let the other ready threads run first.