Skip to content

fix(hls): preserve caller isolation in persistent HTTP reads - #837

Merged
superuser404notfound merged 2 commits into
superuser404notfound:mainfrom
brandomoore:fix/persistent-body-caller-isolation
Oct 21, 2026
Merged

superuser404notfound merged 2 commits into
superuser404notfound:mainfrom
brandomoore:fix/persistent-body-caller-isolation

Conversation

@brandomoore

Copy link
Copy Markdown
Contributor

Summary

Restore Swift 6.4 compilation of persistent HTTP body reads without making mutable chunked framing or progress closures Sendable.

The 7.40.0 source fails in a consuming tvOS application with sending 'current' risks causing data races in Body.next(). Adding isolation only to readChunk reveals the same error for progress when it reaches nextBytes.

What changed

  • Inherit caller isolation in both readChunk and nextBytes with an isolated (any Actor)? = #isolation parameter. This keeps framing and progress callbacks in the caller's isolation domain across both awaits.
  • Keep the existing locking, cancellation handlers, socket deadlines, and connection-reuse checks.
  • Add a chunked-response regression that carries framing across repeated cancellation-handler reads and uses a progress closure capturing the consumer's received-byte count.
  • Document the isolation contract and add an Unreleased changelog entry. There are no public API changes or unchecked Sendable conformances.

Test plan

  • Device / OS: Apple TV 4K (3rd generation) simulator, tvOS 26.5; Xcode 27.0, Swift 6.4.
  • Source media (container / video codec + profile / audio / HDR-DV): no media playback; the existing loopback HTTP fixture serves a deterministic 5,000-byte chunked response.
  • Result: all 20 Issue732PersistentHTTP1Tests passed with no skips, including testChunkedReadsKeepFramingAcrossCancellationHandlers. The new test verifies the complete payload and reusable final framing.
  • A consuming Plozz integration containing the same two-method fix also completed a signed, optimized Debug tvOS device build. Its engine integration bundle passed 173 tests, with five existing platform-dependent skips.
  • Real-media physical-device playback has not been evaluated by this transport regression.

Checklist

  • CHANGELOG.md updated
  • Commit messages follow Conventional Commits (feat(...), fix(...), chore(...))
  • The fix lives in the engine, not in a host-side workaround
  • Public API changes are intentional and documented (none)

Keep the non-Sendable chunked framing on the cancellation handler caller's isolation rather than sending it to the generic executor. Add chunked read coverage through cancellation handlers.

Copilot-Session: e3687c50-89d2-4a1a-86ea-dc2326878fc6
Keep the progress closure in the same isolation through nextBytes as well; exercise a progress callback that reads the consumer's accumulated bytes.

Copilot-Session: e3687c50-89d2-4a1a-86ea-dc2326878fc6
@superuser404notfound
superuser404notfound merged commit 3450f1c into superuser404notfound:main Oct 21, 2026
4 checks passed
@superuser404notfound

Copy link
Copy Markdown
Owner

Thanks for the fix and the regression test! Merged in 3450f1c on top of current main (changelog entry moved under Unreleased > Fixed); the full suite is green, and it ships with the next release.

One note for the record: on Xcode 27.0 (27A266a, Swift 6.4.0.34.1) I could not reproduce the error. Main builds clean for tvOS in Debug and Release, and the new test compiles even against the unchanged transport code. Since both callers are nonisolated, the change does not alter where the reads run, so it is safe either way. If the Plozz build uses a different Xcode build or extra concurrency settings (approachable concurrency, default actor isolation), knowing which would help catch the next case of this in CI.

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