POC: multi-threaded dispatch on wasm32-unknown-wasip1-threads - #6
Draft
scottmarchant wants to merge 4 commits into
Draft
POC: multi-threaded dispatch on wasm32-unknown-wasip1-threads#6scottmarchant wants to merge 4 commits into
scottmarchant wants to merge 4 commits into
Conversation
Proven end to end on this machine: a 4-thread pthread program with atomics, semaphores, and accurate timed waits compiles with both the wasi-sdk 33 clang and the Swift 6.3.3 host clang against the swift-wasm-6.3-RELEASE threads artifactbundle, and passes under wasmtime v24 LTS with -S threads. Key findings: - _REENTRANT discriminates the threads triple at compile time. - Memory must be imported and shared via explicit link flags, or pthread_create fails with EAGAIN at runtime. - Current wasmtime (v47) has removed wasi-threads; v24 LTS is the one solid runtime today. The proposal is deprecated upstream, so threads mode should be an experimental knob, not the default. - The slice A seams need no change for threads mode: the poke-defer hooks compile to no-ops there, and no API signature moves. This answers the #5 pre-upstream blocker. - Full gate audit of every __wasi__ conditional with a KEEP / SPLIT / THREADS classification, and a threads-mode design sketch. Co-authored-by: Krzysztof Rodak <krodak.konta@gmail.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DISPATCH_WASI_THREADS=ON retargets the WASI toolchain file to wasm32-unknown-wasip1-threads: it switches the C, C++, and Swift target triples, adds -pthread, and links with --import-memory,--export-memory,--max-memory=DISPATCH_WASI_MAX_MEMORY (default 256 MiB, cache-overridable). wasi-threads hosts spawn every thread against one imported shared memory; a module that owns a private memory fails at the first pthread_create with EAGAIN, so the import flags are not optional. The knob defaults to OFF and the default configuration is unchanged: the existing single-threaded build reconfigures and builds identically. The threads bundle ships its builtins archive at the same relative path, so the DISPATCH_WASI_BUILTINS default holds for both SDK layouts (verified by linking and running a pthread smoke test with the bundle builtins under wasmtime v24). Co-authored-by: Krzysztof Rodak <krodak.konta@gmail.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eads Split every single-thread assumption on a new DISPATCH_WASI_COOPERATIVE discriminator (defined(__wasi__) && !defined(_REENTRANT)). The threads triple now behaves like a generic POSIX platform: - queue.c: the cooperative poke/drain arms compile only in cooperative mode; the pthread worker pool, root-queue drain, and worker threads compile in threads mode, the Linux shape. _gettid() derives a unique nonzero per-thread id from pthread_self() (4-aligned, so it survives the tid << 2 lock-owner encoding). - shims/lock: threads mode gets a real futex via __builtin_wasm_memory_atomic_wait32/notify for _dispatch_wait_on_address(), sched_yield for _dispatch_thread_switch, and POSIX semaphores for sema4 (USE_POSIX_SEM=1 in the WASI CMake arm when DISPATCH_WASI_THREADS is on). - event: a new DISPATCH_EVENT_BACKEND_WASI_THREADS backend (event_wasi_threads.c). The manager thread parks on a CLOCK_MONOTONIC condition variable and merges due timers; pokes signal the condvar. wasip1 has no poll wakeup object (no eventfd, no pipe creation), so fd and signal sources crash with a named message until a bounded-slice poller thread lands. - dispatch_main() crashes with a named message in threads mode: wasi-libc has no pthread_exit (a wasi thread ends only by returning from its start function), so the generic park does not exist. - DISPATCH_HW_CONFIG_UP stays cooperative-only. The threads target is not a uniprocessor, and UP also shrinks continuations below sizeof(struct dispatch_apply_s) on wasm32. The library builds clean (-Werror) in both modes. The cooperative build is unchanged: 52 of 52 ctest cases still pass. Co-authored-by: Krzysztof Rodak <krodak.konta@gmail.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add tests/wasm/threads-dispatch-smoke.c: 8 dispatch_async blocks must
run on worker pthreads (checked against pthread_self of main),
dispatch_semaphore must block and wake across threads, dispatch_sync
must funnel, and dispatch_after must fire through the manager thread.
Under wasmtime v24 (-S threads) the test prints:
PASS: async=8/8 on-worker-thread=8/8 sync=1 timer=1
Multi-threaded dispatch on wasm32-unknown-wasip1-threads is proven.
The research notes record the build and run commands, the
implementation results, and what stays unimplemented (fd/signal
sources, dispatch_main, the Swift overlay).
Co-authored-by: Krzysztof Rodak <krodak.konta@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fork-only proof of concept. This PR is a draft on purpose and is not meant to merge. It shows the multi-threaded research on top of #3 (the combined WASI port). The full findings live in
tests/wasm/THREADS-RESEARCH.mdon this branch. The series tracker is #3.Summary
libdispatch becomes multi-threaded when compiled for
wasm32-unknown-wasip1-threads, behind an experimentalDISPATCH_WASI_THREADS=ONCMake knob. In that mode the library takes the generic POSIX shape: a pthread worker pool, real blocking waits on a wasm futex, POSIX semaphores, and a manager thread on a condition variable. The smoke test under wasmtime v24 (-S threads) prints:Every
dispatch_asyncblock ran on a worker pthread, semaphores blocked and woke across threads,dispatch_syncfunneled, anddispatch_afterfired through the manager thread. The cooperative build is unchanged: 52 of 52 ctest cases still pass.What this dictates for #3 and #5
_REENTRANT(defined by-pthreadon the threads triple) separates the modes. The branch re-gates the cooperative sites onDISPATCH_WASI_COOPERATIVE(__wasi__ && !_REENTRANT); a future threads slice would carry that split into B1's gates.Discoveries specific to threads
-Wl,--import-memory,--export-memory,--max-memory=N) or everypthread_createfails with EAGAIN.pthread_exit: a thread ends only by returning from its start function.dispatch_main()therefore crashes with a named message until a dedicated park design lands.__builtin_wasm_memory_atomic_wait32/notifyis a true futex;_dispatch_wait_on_addressuses it directly.DISPATCH_HW_CONFIG_UPmust stay cooperative-only: UP-sized continuations (32 bytes on wasm32) are smaller thanstruct dispatch_apply_s.poll_oneoffhas no cross-thread wakeup object),dispatch_main(), and the Swift overlay.Commits
Four conventional commits: research notes, the CMake knob, the gate split plus the condvar manager backend (
event_wasi_threads.c), and the smoke test.