@oligami/browser_wasi_shim-threads
v0.5.0
Published
A pure javascript shim for WASI with support for threads
Maintainers
Readme
A pure javascript shim for WASI Preview 1 threads
[!WARNING] The code in this directory is less production ready than the main browser_wasi_shim code. This code requires
SharedArrayBuffer,waitAsyncandAtomicsto be enabled in the browser, so it may not work in all browsers. For example, Firefox failed to run the demo in this directory. Chrome worked fine. This library requirecross-origin isolationto be enabled in the browser.
This project implement threads on browser_wasi_shim
Features
- [x] thread creation
- [x] Filesystem wrapper accessible by multiple workers
- [ ] thread pool
Building
$ npm install
$ npm run buildRunning the demo
$ git submodule update --init
$ cd examples && npm install && npm run devAnd visit http://localhost
Farm base calls
WASIFarm accepts an unknown_fn callback that Animals invoke with
call_unknown_fn. Calls from different Animals can run concurrently, and each
caller receives its own result or thrown error even when callbacks complete out
of order.
max_base_calls_limit controls concurrent user calls. It defaults to 128 and
must be an integer from 1 through 1024; one additional slot is reserved for
system operations. Synchronous Animal calls fail immediately with
BaseCallCapacityError when every user slot is occupied.
Animal base calls use blocking Atomics.wait, so they must run in a Worker that
is separate from the agent running the Park listener. The Park agent must remain
able to process callbacks asynchronously; do not make a blocking Animal call on
that same agent or on a browser agent where Atomics.wait is forbidden.
Forcibly terminating a Worker while it owns a base-call slot cannot run the
caller's SharedArrayBuffer cleanup. The slot therefore remains occupied until
the Farm is destroyed. Terminate callers cooperatively when the Farm must remain
usable; otherwise call WASIFarm.destroy() to cancel the remaining slots and
discard late response payloads.
Worker runtime destruction
WASIFarm.destroy() remains Park-only. It synchronously and idempotently
destroys the Farm's owned Park, rejects new Park calls, wakes blocked callers,
discards late callback results, and makes later get_ref() calls fail. It does
not destroy Animals, the worker runtime, or external Workers.
WASIFarmAnimal.destroy() and DestroyerHandle.destroy() synchronously make
the shared worker runtime reject new work and initiate runtime-wide teardown.
Returning means logical teardown has started, not that every Worker has
physically exited. Neither method destroys a Park.
WASIFarmAnimal.async_destroy() and DestroyerHandle.async_destroy() observe
logical teardown completion, including released coordinator state and issued
termination operations. Browser platforms do not provide a physical thread
join, so resolution does not guarantee host thread reclamation. When an elected
managed requester initiates teardown, its Promise resolves after every other
managed Worker receives termination and its own close is scheduled; code after
the await must not start new runtime work.
A transferred DestroyerHandleObject inside a managed worker must be
reconstructed with WASIFarmAnimal.init_destroyer() so the receiving Animal is
bound as the local requester. DestroyerHandle.init_self() creates an unbound
handle for an external controller.
WASIFarmAnimal.kill_animal(animal_id) affects only the published, registered
Animal with that ID. Unknown, unpublished, completed, or already killed IDs are
idempotent no-ops and do not change the runtime lifecycle.
Returning acknowledges the request, not physical termination. The coordinator
reserves termination, acknowledges any in-flight command, and waits for short
shared-memory updates to release ownership before terminating the captured
Worker. This also permits a Worker to request its own termination without
abandoning the command lock. Sibling Workers remain usable after an individual
kill; runtime-wide completion and destruction still stop the applicable Workers.
The Park-specific methods remain separate: destroy_park(n) destroys one
referenced Park, destroy_park_all() destroys all referenced Parks, and
destroy_with_park() destroys the referenced Parks before initiating worker
runtime teardown.
Architecture
In the thread that maintains the file system for @bjorn3/browser_wasi_shim, file access is awaited asynchronously, while in the thread executing the WASM, file access is performed synchronously.
Since the functions called within the WASM are invoked synchronously, it is not possible to properly create web workers. Therefore, it is necessary to first create a dedicated thread for generating web workers.
Please refer to the diagram below for more details.
Additionally, since data of unknown size is being exchanged, an allocator is essential. A simple allocator is implemented on the assumption that the allocated memory will be released promptly.
