Modules

Wasi_Runtime

moduleextensionexperimentalSource

Available on Wasm

#import "Extensions/Wasi_Runtime";

What a wasm64 program needs to run as a WASI command (node, wasmtime and the like). jaic adds it to jaic -os wasm programs and to workspaces whose LLVM triple names WASI; a program that brings its own host bindings leaves it out.

It supplies, in Jai, the C library entry points the stdlib reaches for under OS == .WASM (the same ones the interpreter's sandbox host implements): a heap, memmove/memcmp, read/write, clock_gettime, exit, files and directories (files.jai), process calls that fail the way a sandbox without processes would, green threads for the pthread calls (threads.jai), a libm (math.jai, elementary.jai), the compiler-rt binary128 helpers Long_Double needs (quad.jai), plus Runtime_Support's wasm_write_string and wasm_debug_break, and _start, which collects the WASI arguments and calls main. Every definition is a #program_export under its C name; jaic's LLVM backend binds the program's #foreign references to them without exporting them from the module, so the linker drops the ones a program never calls and the module imports nothing but the wasi_snapshot_preview1 calls it uses.

Under the interpreter (compile-time code, jaic -interp -os wasm) none of this runs: the sandbox host answers those #foreign calls itself.

Procedures#

next_to_runexperimentalSource

The next thread to run, after the one that just ran. With none ready, it sleeps until the first timed wait runs out. When every thread waits for something only another waiting thread could do, they are deadlocked: the one that blocked last gets EDEADLK.

(waiting_for: *void, deadline: u64) -> s32

Blocks the running thread until another wakes it or deadline passes, and says why it woke: 0, ETIMEDOUT or EDEADLK. Only for programs jaic made able to unwind (green.enabled).

wake_oneexperimentalSource
(object: *void)

Wakes the thread that has waited longest for object.

(total: u64) -> bool

Sleeps the whole interval: a host may wake a poll slightly early (Node's can).

(nanoseconds: u64) -> bool

A sleep is a timed wait for nothing, so other threads run meanwhile.

(condition: *void, mutex: *Green_Mutex, deadline: u64) -> s32

Releases the mutex for the length of the wait, however deeply the thread holds it.

Types#

Green_ControlstructexperimentalSource
:: struct

The compiler reads these by offset: state at 0, top at 8, limit at 16, enabled at 24.

state: s64
top: *u8
Where the running thread's next frame goes.
limit: *u8
The end of its frame stack.
enabled: s64
Green_ThreadstructexperimentalSource
:: struct
argument: *void
result: *void
frames: *u8
frames_end: *u8
started: bool
waiting_for: *void
While BLOCKED: what it waits for (a mutex, a condition, a thread; null for a sleep), until when (monotonic nanoseconds, 0 for no limit), and when it started waiting.
deadline: u64
ticket: u64
wake_result: s32
Green_MutexstructexperimentalSource
:: struct

The pthread calls. Handles are Green_Thread pointers; a mutex keeps its owner and lock count in the first bytes of a pthread_mutex_t. Every mutex is recursive.

owner: *Green_Thread
depth: s64

Constants#

:: 1024 * 1024 - 64

Just under 1 MiB, so the heap's power-of-two classes fit it with its tag.