feat: Temporary PR to view accumulated changes to compile libDispatch to Wasm - #1
feat: Temporary PR to view accumulated changes to compile libDispatch to Wasm#1scottmarchant wants to merge 8 commits into
Conversation
First milestone of the WebAssembly/WASI port. Single-threaded libdispatch now compiles for wasm32-wasip1 and runs real GCD APIs (dispatch_async, dispatch_group + notify, dispatch_after timers, dispatch_main) on a cooperative run loop with no worker-thread pool and no JavaScript. Verified on wasmtime and browser_wasi_shim; module imports are wasi_snapshot_preview1 only. - event: new DISPATCH_EVENT_BACKEND_WASI + src/event/event_wasi.c (timer-only backend; _dispatch_event_loop_drain sleeps via poll_oneoff to next deadline) - queue.c: dispatch_main() -> _dispatch_wasi_runloop_main cooperative loop (drains mgr queue + anon timers + root queues, then blocks to next deadline); _gettid, sigsuspend/sig_thread, root_queues_init_once wasi branches - internal.h: force DISPATCH_USE_PTHREAD_POOL=0 (keep INTERNAL_WORKQUEUE=1); wasi system-include branch - shims: wasi lock encoding + trapping sema4 + wait_on_address (lock.h/.c), clock_gettime time (time.h), 1-cpu hw_config, getprogname, _dispatch_thread_switch - public headers: widen __unix__ gates to include __wasi__ (os/object.h, dispatch/dispatch.h, private/private.h) - init.c: stderr logging; avoid __builtin_return_address (unsupported on wasm) - io.c/transform.c: wasi major()/endian shims - CMake: detect CMAKE_SYSTEM_NAME==WASI, add _WASI_EMULATED_* defines, gate -Werror off during bring-up Blocking APIs (dispatch_sync, blocking semaphore/group waits) trap by design. Main-queue draining, the Swift overlay, and the threaded variant are follow-ups. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The thread-bound main queue already drains correctly under the cooperative dispatch_main() run loop (it flows through its target root queue, which the loop drains), so DispatchQueue.main async/after work with no further changes. Add tests/wasi: - dispatch_wasi_smoke.c async + group(+notify) + dispatch_after - dispatch_wasi_mainqueue.c main-queue serial FIFO + interleave + timer - run.sh / README.md build + run under wasmtime Verified on wasmtime and browser_wasi_shim; main-queue work runs in strict FIFO order. Blocking APIs still trap by design. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Swift overlay (src/swift/*.swift) now cross-compiles to a Dispatch.swiftmodule + libswiftDispatch.a for wasm32-wasip1, and a downstream `import Dispatch` program (DispatchQueue.async/asyncAfter, DispatchGroup.notify, dispatchMain()) compiles, links, and runs under wasmtime — imports limited to wasi_snapshot_preview1 (no JavaScript). - src/swift/Source.swift, Wrapper.swift: gate the Darwin/BSD-only source-type accessors (PROC/VNODE/FS-object/etc.) with `&& !os(WASI)` so WASI follows the Linux exclusion (those C types don't exist on wasi). - os/generic_unix_base.h: on wasi, don't pull <sys/param.h> (it includes <endian.h>, whose __bswap* inlines are redefined inside the Swift clang importer's CDispatch module); provide MIN/MAX/howmany directly instead. - tests/wasi: add dispatch_wasi_swift.swift (the import-Dispatch consumer test) and swift-overlay.md (build recipe + toolchain notes: use the swift.org toolchain matching the Wasm SDK, emulation defines, module-map, clang_rt). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add dispatch_wasi_api.swift covering custom serial queue FIFO ordering, DispatchWorkItem, DispatchSemaphore (signal/wait fast path), concurrentPerform, DispatchData, and a repeating DispatchSource timer (fires 3x, exercising the timer re-arm path). All pass under wasmtime via the single-threaded run loop. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…s trap Empirically refine what works vs traps on the single-threaded port: - dispatch_sync to a non-current queue runs INLINE and works (global + serial verified) — uncontended sync needs no blocking on one thread. (Better than the plan assumed; only re-entrant sync onto the current queue deadlocks/crashes.) - A dispatch_semaphore_wait(FOREVER) with nothing to signal it raises a clean DISPATCH_CLIENT_CRASH (wasm trap), not a hang. Add dispatch_wasi_sync.c and correct tests/wasi/README scope accordingly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The single-threaded wasi build compiles cleanly under the project's full -Werror -Wall -Wextra set once the unused pool/thread vars are marked DISPATCH_UNUSED and the pthread-pool context array is POOL-gated (done in the earlier commits). Remove the temporary -Wno-error bring-up band-aids: - DispatchCompilerWarnings.cmake: drop the `if(NOT DISPATCH_WASI)` guard - CMakeLists.txt / src/CMakeLists.txt: drop the -Wno-error overrides Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tests/wasi/swift-demo: a vanilla SwiftPM executable that `import Dispatch` and cross-compiles to wasm via `swift build --swift-sdk` — the real downstream flow. Verified: builds and runs under wasmtime (custom queue async, DispatchGroup notify on .main, DispatchQueue.main.asyncAfter), imports wasi_snapshot_preview1 only (no JavaScript). - build-prefix.sh: builds the overlay (-module-link-name swiftDispatch) and assembles a self-contained SDK-overlay prefix (headers + module maps + Dispatch.swiftmodule + libs). - Package.swift / main.swift: the consumer (flags only needed because the overlay is consumed out-of-tree; upstreamed, `import Dispatch` needs none). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When configured for WASI (toolchain sets CMAKE_SYSTEM_NAME=WASI), tests/CMakeLists.txt routes to tests/wasi/CMakeLists.txt and returns early (the legacy bsdtest harness uses fork/exec, absent on WASI). The wasi CMakeLists builds dispatch_wasi_smoke/mainqueue/sync to wasm32-wasip1 and registers them as ctest cases run under a WASI runtime (wasmtime by default; override with -DWASI_TEST_RUNNER). Verified: `ctest` -> 3/3 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comparison: PR #1 vs PR #2 vs PR #3 (libdispatch → Wasm/WASI)(Updated after two review rounds on #3: a full-branch review with fixes, then an adversarial probe round from the #2 side with a hardening series. Every row is verified empirically — built and executed. Cross-posted on #1, #2, and #3.) All three branches were built for Branch summary
Dispatch API supportLegend: ✅ works · Queues & submission
Synchronization
Sources
Data & I/O
Test coverage
Harness note (#3): Node is now optional — without it (or below 19.8) everything still builds and all tests register as visible-but-DISABLED, the same shape as the missing- Runtime support
Review round (what changed since the previous version of this comment)Two rounds. First, a high-effort review of #3's full diff vs Second, an adversarial probe round from the #2 side found one bypassed funnel in the poke-defer fix ( Bottom line#1 is the minimal seed but breaks contracts on APIs it nominally supports. #2 made the supported set behave per spec and the unsupported set fail loudly; the review round found its one systemic gap — eager drains running client code beneath caller-held locks — plus a silent-spin path and the signal/cap issues, all inherited by and now fixed in #3. #3 is #2's design carried to completion: fd and signal sources work, the re-entrancy divergence from threaded platforms is confined to documented, pinned-by-test semantics, and every failure mode is a named crash. The remaining unsupported surface (process/vnode/memory-pressure/Mach, cross-process signals, true parallelism) is bounded by WASI itself. 🤖 Generated with Claude Code |
Fold the best of the first WASI port candidate (PR #1) and the findings from the side-by-side comparison of both candidates into the test suite: - sync-inline.c, main-queue-order.c: inline dispatch_sync without a drain and thread-bound main-queue FIFO ordering, adapted from PR #1's dispatch_wasi_sync and dispatch_wasi_mainqueue tests. - blocking-waits.c: pins the blocking-wait contracts that PR #1's trap-on-block design violated: dispatch_block_wait runs the queued block, dispatch_group_wait(FOREVER) returns once the group empties, and a timed semaphore wait consumes its full timeout instead of returning early. - api-surface.c: one-binary sweep of the object/attr/block/data/group/ source families, derived from the probe program used to compare the two candidates. - assert-queue.c: dispatch_assert_queue must trap off-queue and pass on-queue. Guards the tid-vs-DLOCK_OWNER_MASK encoding in shims/lock.h; PR #1 shipped an unshifted constant tid that masked to DLOCK_OWNER_NULL, making assert_queue pass off-queue and assert_queue_not trap spuriously. All 35 ctest cases pass under wasmtime/node with the Swift overlay enabled (swift.org 6.3.3 toolchain + Swift 6.3.3 Wasm SDK). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Closing: the two-port evaluation concluded in favor of #2's cooperative eager-drain runtime, and the combined branch (#3) carries that runtime plus this PR's test scenarios and consumer documentation. #3 is the single source of truth going forward; upstream-facing slices will be cut from it one at a time. See the comparison comment above for the full evaluation record. |
Fold the best of the first WASI port candidate (PR #1) and the findings from the side-by-side comparison of both candidates into the test suite: - sync-inline.c, main-queue-order.c: inline dispatch_sync without a drain and thread-bound main-queue FIFO ordering, adapted from PR #1's dispatch_wasi_sync and dispatch_wasi_mainqueue tests. - blocking-waits.c: pins the blocking-wait contracts that PR #1's trap-on-block design violated: dispatch_block_wait runs the queued block, dispatch_group_wait(FOREVER) returns once the group empties, and a timed semaphore wait consumes its full timeout instead of returning early. - api-surface.c: one-binary sweep of the object/attr/block/data/group/ source families, derived from the probe program used to compare the two candidates. - assert-queue.c: dispatch_assert_queue must trap off-queue and pass on-queue. Guards the tid-vs-DLOCK_OWNER_MASK encoding in shims/lock.h; PR #1 shipped an unshifted constant tid that masked to DLOCK_OWNER_NULL, making assert_queue pass off-queue and assert_queue_not trap spuriously. All 35 ctest cases pass under wasmtime/node with the Swift overlay enabled (swift.org 6.3.3 toolchain + Swift 6.3.3 Wasm SDK). Co-authored-by: Krzysztof Rodak <krodak.konta@gmail.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fold the best of the first WASI port candidate (PR #1) and the findings from the side-by-side comparison of both candidates into the test suite: - sync-inline.c, main-queue-order.c: inline dispatch_sync without a drain and thread-bound main-queue FIFO ordering, adapted from PR #1's dispatch_wasi_sync and dispatch_wasi_mainqueue tests. - blocking-waits.c: pins the blocking-wait contracts that PR #1's trap-on-block design violated: dispatch_block_wait runs the queued block, dispatch_group_wait(FOREVER) returns once the group empties, and a timed semaphore wait consumes its full timeout instead of returning early. - api-surface.c: one-binary sweep of the object/attr/block/data/group/ source families, derived from the probe program used to compare the two candidates. - assert-queue.c: dispatch_assert_queue must trap off-queue and pass on-queue. Guards the tid-vs-DLOCK_OWNER_MASK encoding in shims/lock.h; PR #1 shipped an unshifted constant tid that masked to DLOCK_OWNER_NULL, making assert_queue pass off-queue and assert_queue_not trap spuriously. All 35 ctest cases pass under wasmtime/node with the Swift overlay enabled (swift.org 6.3.3 toolchain + Swift 6.3.3 Wasm SDK). Co-authored-by: Krzysztof Rodak <krodak.konta@gmail.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fold the best of the first WASI port candidate (PR #1) and the findings from the side-by-side comparison of both candidates into the test suite: - sync-inline.c, main-queue-order.c: inline dispatch_sync without a drain and thread-bound main-queue FIFO ordering, adapted from PR #1's dispatch_wasi_sync and dispatch_wasi_mainqueue tests. - blocking-waits.c: pins the blocking-wait contracts that PR #1's trap-on-block design violated: dispatch_block_wait runs the queued block, dispatch_group_wait(FOREVER) returns once the group empties, and a timed semaphore wait consumes its full timeout instead of returning early. - api-surface.c: one-binary sweep of the object/attr/block/data/group/ source families, derived from the probe program used to compare the two candidates. - assert-queue.c: dispatch_assert_queue must trap off-queue and pass on-queue. Guards the tid-vs-DLOCK_OWNER_MASK encoding in shims/lock.h; PR #1 shipped an unshifted constant tid that masked to DLOCK_OWNER_NULL, making assert_queue pass off-queue and assert_queue_not trap spuriously. All 35 ctest cases pass under wasmtime/node with the Swift overlay enabled (swift.org 6.3.3 toolchain + Swift 6.3.3 Wasm SDK). Co-authored-by: Krzysztof Rodak <krodak.konta@gmail.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
No description provided.