Skip to content

Flush queued writes without retrying when the scheduler runs tasks in… - #350

Open
rnro wants to merge 2 commits into
apple:mainfrom
rnro:flush-writes-on-inline-scheduler
Open

rnro wants to merge 2 commits into
apple:mainfrom
rnro:flush-writes-on-inline-scheduler

Conversation

@rnro

@rnro rnro commented Nov 14, 2026

Copy link
Copy Markdown
Contributor

…line

EndpointFlow.scheduleWriteFlush retried requestFlush() whenever withFlow returned nil, assuming context.async always defers the task until the current flow call has returned. A scheduler that runs runImmediate tasks on the caller's stack runs the flush task inside the send that still holds the flow, so withFlow returned nil and the retry recursed until the stack overflowed.

Changed:

  • Handed the flush to the call holding the flow through EndpointFlowParent.deferWriteFlush() instead of retrying from the flush task.
  • Tracked the pending flush in NetworkChannelBase with FlowBorrow.borrowed(flushPending:), and flushed in releaseFlow() before ending the borrow.
  • Made EndpointFlow.flushWrites() internal so the owner can call it.
  • Added SwiftNetworkEndpointFlowInlineSchedulerTests, which runs the flush task inline during a send.

Result:

  • Sending on a ready flow no longer recurses when the scheduler runs tasks inline.
  • The new test fails without the change and passes with it.

…line

`EndpointFlow.scheduleWriteFlush` retried `requestFlush()` whenever `withFlow` returned `nil`, assuming `context.async` always defers the task until the current flow call has returned. A scheduler that runs `runImmediate` tasks on the caller's stack runs the flush task inside the `send` that still holds the flow, so `withFlow` returned `nil` and the retry recursed until the stack overflowed.

Changed:
* Handed the flush to the call holding the flow through `EndpointFlowParent.deferWriteFlush()` instead of retrying from the flush task.
* Tracked the pending flush in `NetworkChannelBase` with `FlowBorrow.borrowed(flushPending:)`, and flushed in `releaseFlow()` before ending the borrow.
* Made `EndpointFlow.flushWrites()` internal so the owner can call it.
* Added `SwiftNetworkEndpointFlowInlineSchedulerTests`, which runs the flush task inline during a `send`.

Result:
* Sending on a ready flow no longer recurses when the scheduler runs tasks inline.
* The new test fails without the change and passes with it.
@rnro

rnro commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

/benchmark

@rnro rnro added the 🔨 semver/patch No public API change. label Nov 14, 2026
@github-actions

Copy link
Copy Markdown

Benchmark results

2523f41e9473 (this change) against fe21d729ad85 (merge base), Swift 6.4.1, workflow run. Counts are the heap allocations and the retain and release calls made by a short fixed workload under valgrind.

Tool Allocations ARC traffic
QUICTransfer −0.03% ➖ −0.01% ➖
QUICStreamLoad +0.00% ➖ +0.06% ➖
IPUDPTransfer −0.00% ➖ −0.00% ➖

🟢 lower, 🔴 higher, ➖ no change: a change must exceed 0.25% and 3 times the variation between repeats of either build.

QUICTransfer
base head Δ repeat spread
Allocations 424 k 424 k −0.03% 0.05% ➖
ARC traffic 2.15 M 2.15 M −0.01% 0.03% ➖

Allocations: base 424451, 424344, 424435; head 424312, 424474, 424277
ARC traffic: base 2149223, 2148744, 2149074; head 2148949, 2149368, 2148770

QUICStreamLoad
base head Δ repeat spread
Allocations 365 k 365 k +0.00% 0.16% ➖
ARC traffic 2.23 M 2.23 M +0.06% 0.05% ➖

Allocations: base 364771, 364205, 364783; head 364595, 364775, 364951
ARC traffic: base 2227315, 2226932, 2228123; head 2227949, 2228562, 2228707

IPUDPTransfer
base head Δ repeat spread
Allocations 904 k 904 k −0.00% 0.00% ➖
ARC traffic 4.57 M 4.57 M −0.00% 0.00% ➖

Allocations: base 904251, 904256, 904256; head 904246, 904246, 904251
ARC traffic: base 4570761, 4570792, 4570791; head 4570724, 4570725, 4570756

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔨 semver/patch No public API change.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant