Slice A: shared-code seams for a cooperative single-threaded event backend - #4
Closed
scottmarchant wants to merge 1 commit into
Closed
Slice A: shared-code seams for a cooperative single-threaded event backend#4scottmarchant wants to merge 1 commit into
scottmarchant wants to merge 1 commit into
Conversation
Collaborator
Author
…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
force-pushed
the
feat/scottm/wasi-sliceA-shared-seams
branch
from
August 17, 2026 23:25
12e6e73 to
d85fd0d
Compare
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.
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:
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: thedispatch_sync/dispatch_barrier_syncinline funnels, bothdispatch_async_and_waitfunnels (including thedispatch_block_createprivdata entry that Swift'sasyncAndWait(execute:)produces),_dispatch_barrier_trysync_or_async_f,dispatch_onceinitializers, object dispose, and the specifics-hash mutation indispatch_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.Main-queue drain hoist.
_dispatch_main_queue_drainmoves out ofDISPATCH_COCOA_COMPATinto#if DISPATCH_COCOA_COMPAT || defined(__wasi__), with the two runloop/Darwin-only steps (runloop-handle once, thread-QoS override propagation) staying gated underDISPATCH_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
DISPATCH_COCOA_COMPATrestructure 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.Verification
mainand this branch build identically and pass the same 23/23 upstream tests.sync-nested-async,async-and-wait,specific-destructor, dispose-path destructors) and for the shared drain.🤖 Generated with Claude Code