Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions Sources/JavaScriptEventLoop/JobQueue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ extension JavaScriptEventLoop {

func runAllJobs() {
assert(queueState.isSpinning)
// Release the latch BEFORE running any job, so `isSpinning` means "a
// drain microtask is pending" rather than "a drain is in progress".
//
// `runSynchronously` can unwind without running Swift cleanup — a
// runtime trap lowers to `unreachable`, and a JS exception crossing
// back into wasm unwinds the frames outright. Neither runs a `defer`.
// Clearing the latch only after the loop therefore leaves it stuck at
// `true` on those paths, and `insertJobQueue` never schedules another
// drain: the executor is dead for the lifetime of the process,
// silently. Clearing it up front costs one extra drain microtask per
// batch that enqueues, and an unwinding job leaves the queue merely
// undrained rather than unreachable — the next enqueue picks it up.
queueState.isSpinning = false

while let job = self.claimNextFromQueue() {
#if compiler(>=5.9)
Expand All @@ -50,8 +63,6 @@ extension JavaScriptEventLoop {
job._runSynchronously(on: self.asUnownedSerialExecutor())
#endif
}

queueState.isSpinning = false
}

func claimNextFromQueue() -> UnownedJob? {
Expand Down