Skip to content

fix(zero-swift): send WebSocket frames off the main actor - #107

Merged
tmm merged 3 commits into
mainfrom
swift-transport-stall-report
Nov 6, 2026
Merged

tmm merged 3 commits into
mainfrom
swift-transport-stall-report

Conversation

@tmm

@tmm tmm commented Nov 5, 2026 •

Copy link
Copy Markdown
Member

keepsTheOrderOfFramesSentBeforeAndAfterTheOpen timed out on main (b7c61fb, Swift (macOS)). This PR first added timing to the test; its CI run then showed the cause:

The server got 107 of 200 frames, the last at 30858 ms. The client started 107 sends and finished 107, the last at 30858 ms and the slowest in 7 ms; failed: []. The main actor's longest wait was 6648 ms, at 20392 ms.

Every send finished quickly and the server got each frame at once, but the sends started slowly. TransportConnection's writer ran on the main actor and went back to it after each frame, so while the main actor was busy (here, other tests in the suite), every frame waited its turn. In an app, a busy main thread would hold up mutations the same way.

The writer now runs in a detached task, still one at a time, so frames and the close after them keep their order.

  • New test sendsWhileTheMainActorIsBusy: opens a socket, queues 200 frames, blocks the main actor for 1 s, and expects the server to have all 200. Without the fix it gets 0.
  • keepsTheOrderOfFramesSentBeforeAndAfterTheOpen keeps the timing report (sends timed in a wrapping socket, server frames counted off the main actor, the main actor's longest wait) for anything else that stalls.

Checked on a Mac (Xcode 26.6): both tests 30/30, three full suites pass; Linux ZeroTests|ZeroVirtualTests|E2ETests pass. Patch changeset.

tmm and others added 3 commits November 5, 2026 23:25
After 30 s the test records how many frames the server got and what the
client's writer logged, so a stall shows whether a client send or the
server's receive stopped.

Amp-Thread-ID: https://ampcode.com/threads/T-c513a037-a257-45e0-b9a9-15c15edfe859
Co-authored-by: tmm <tmm@tmm.dev>
The first report showed the frames arriving late, not lost, but the
writer's log runs on the main actor and couldn't tell a slow send from a
busy main actor. The test now times each send in a wrapping socket,
counts the server's frames off the main actor, and measures the main
actor's longest wait.

Amp-Thread-ID: https://ampcode.com/threads/T-c513a037-a257-45e0-b9a9-15c15edfe859
Co-authored-by: tmm <tmm@tmm.dev>
The frame order test's report showed each send finishing in at most 7 ms
but only 107 of 200 starting in 30 s, with the main actor waiting up to
6.6 s: the writer ran on the main actor and waited for it after every
frame. It now runs detached. A new test blocks the main actor for a
second after queuing 200 frames; without the fix the server gets none.

Amp-Thread-ID: https://ampcode.com/threads/T-c513a037-a257-45e0-b9a9-15c15edfe859
Co-authored-by: tmm <tmm@tmm.dev>
@tmm tmm changed the title test(zero-swift): report where the URLSession frame order test stalls fix(zero-swift): send WebSocket frames off the main actor Nov 5, 2026
@tmm
tmm merged commit a0146e4 into main Nov 6, 2026
19 of 20 checks passed
@tmm
tmm deleted the swift-transport-stall-report branch November 6, 2026 00:29
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.

2 participants