Skip to content

Slice A: shared-code seams for a cooperative single-threaded event backend - #4

Closed
scottmarchant wants to merge 1 commit into
mainfrom
feat/scottm/wasi-sliceA-shared-seams
Closed

Slice A: shared-code seams for a cooperative single-threaded event backend#4
scottmarchant wants to merge 1 commit into
mainfrom
feat/scottm/wasi-sliceA-shared-seams

Conversation

@scottmarchant

Copy link
Copy Markdown
Collaborator

Slice A of the WASI port series (see #3 for the combined change set and the slicing plan). Fork-internal draft: this is the upstream-facing shape for review here first; nothing points at upstream yet.

What this is

The two shared-code seams a cooperative single-threaded event backend needs, with no functional change on any current platform:

  1. Poke-defer brackets. _dispatch_cooperative_pokes_defer()/_undefer() compile to ((void)0) unless a cooperative event backend defines the real versions. They bracket every critical section that can submit work while holding an internal lock: the dispatch_sync/dispatch_barrier_sync inline funnels, both dispatch_async_and_wait funnels (including the dispatch_block_create privdata entry that Swift's asyncAndWait(execute:) produces), _dispatch_barrier_trysync_or_async_f, dispatch_once initializers, object dispose, and the specifics-hash mutation in dispatch_queue_set_specific. On threaded platforms a poke wakes another worker, so the brackets change nothing. On a cooperative backend (arriving in slice B1) a poke may run the submitted work immediately on the submitting stack; without the brackets, that work runs beneath the caller's held lock, turning programs that are correct on every threaded platform into spurious deadlock crashes.

  2. Main-queue drain hoist. _dispatch_main_queue_drain moves out of DISPATCH_COCOA_COMPAT into #if DISPATCH_COCOA_COMPAT || defined(__wasi__), with the two runloop/Darwin-only steps (runloop-handle once, thread-QoS override propagation) staying gated under DISPATCH_COCOA_COMPAT. This lets the cooperative backend drain the thread-bound main queue through the same code the CFRunLoop callback uses, instead of maintaining a 63-line copy kept "in sync" by a comment (which is what the combined branch had before this refactor). The compiled result on Darwin is unchanged; platforms with neither macro compile neither version, as before.

Why land this first

  • It is the only slice touching code other platforms compile, and it is small enough to review in one sitting.
  • We cannot fully validate the DISPATCH_COCOA_COMPAT restructure ourselves: the public macOS CMake build of this repository is broken upstream, so only Apple CI can compile that path. Landing this slice first forces that conversation while the stake is ~90 lines.
  • Every later slice (B1: build + core runtime; B2: test suite; C: Swift overlay; D: event sources) depends on these seams or is independent of them; none of them touch shared code again.

Verification

  • Linux container (clang/Ninja, aarch64): main and this branch build identically and pass the same 23/23 upstream tests.
  • The full cooperative backend built on these exact seams passes 52/52 WASI tests on the combined branch (feat: WASI port V2 — combined branch: #2's runtime + #1's test scenarios + API-contract tests #3), including regression tests for each bracket site (sync-nested-async, async-and-wait, specific-destructor, dispose-path destructors) and for the shared drain.
  • Darwin: the restructure is mechanically identity-preserving (the two gated steps sit under a macro that is always true there), but compiling it needs Apple CI - the ask that comes with this slice.

🤖 Generated with Claude Code

@scottmarchant
scottmarchant deleted the feat/scottm/wasi-sliceA-shared-seams branch August 17, 2026 22:10
@scottmarchant

Copy link
Copy Markdown
Collaborator Author

Superseded by #5: the branch rename to prepare-cooperative-event-backend closed this PR (GitHub could not retarget the head ref). #5 carries the same change with the upstream-ready commit message and description.

…ends

Some platforms run libdispatch on one thread, with no worker threads.
An event backend for such a platform cannot wake a worker when work is
enqueued. Instead, a poke may run the enqueued work immediately, on the
thread that enqueued it. This commit prepares the shared code for that
execution model. It makes no functional change on any current platform.

Part 1: poke-defer hooks. The macros
_dispatch_cooperative_pokes_defer() and
_dispatch_cooperative_pokes_undefer() compile to ((void)0) unless a
cooperative event backend defines the real versions. The hooks bracket
each critical section that can enqueue work while it holds an internal
lock:

- the dispatch_sync and dispatch_barrier_sync inline funnels
- both dispatch_async_and_wait funnels, including the private-data
  block entry that Swift's asyncAndWait(execute:) produces
- _dispatch_barrier_trysync_or_async_f
- dispatch_once initializers (the once gate is held)
- object dispose (destructor batches are enqueued during teardown)
- the specifics-hash mutation in dispatch_queue_set_specific

On a threaded platform, a poke only wakes another worker, so the hooks
change nothing. On a cooperative backend, the hooks defer the inline
execution until the outermost section exits. Without them, enqueued
work would run under the caller's lock and deadlock.

Part 2: a shared main-queue drain. _dispatch_main_queue_drain moves out
of DISPATCH_COCOA_COMPAT into its own guard. The two runloop-only steps
(the runloop-handle initialization and the thread-QoS override
propagation) stay gated under DISPATCH_COCOA_COMPAT. A cooperative
backend can then drain the thread-bound main queue through the same
code the CFRunLoop callback uses, instead of a divergent copy. The
compiled result on Darwin is unchanged. Platforms that define neither
macro compile neither version, as before.

Co-authored-by: Krzysztof Rodak <krodak.konta@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@scottmarchant
scottmarchant restored the feat/scottm/wasi-sliceA-shared-seams branch August 17, 2026 23:25
@scottmarchant scottmarchant reopened this Aug 17, 2026
@scottmarchant
scottmarchant force-pushed the feat/scottm/wasi-sliceA-shared-seams branch from 12e6e73 to d85fd0d Compare August 17, 2026 23:25
@scottmarchant
scottmarchant deleted the feat/scottm/wasi-sliceA-shared-seams branch August 17, 2026 23:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant