Skip to content

5.6.1

Choose a tag to compare

@superuser404notfound superuser404notfound released this 17 Jul 18:18

Fixed

A diagnostics tick can no longer hang or kill the host app (#134)

Production tvOS hosts reported fatal app hangs (6+ s fully blocked main thread, watchdog terminations in the worst cases) with the main thread parked in mach_msg under LiveTelemetrySampler.tick. On the native path the 1 Hz tick made up to six synchronous AVFoundation reads per second on the main actor: accessLog() twice, currentTime() twice, loadedTimeRanges, timeControlStatus, plus the LagDiag property cluster. Each of those getters is a synchronous XPC round-trip to mediaserverd, so any moment the media server was slow to answer (a display-mode change on a 4K HDR start is one observed trigger) blocked the main thread for the duration of the stall. The sampler was the victim of the stall, not the cause, but it converted an invisible, recoverable backend delay into a fully blocked main thread.

The fix, in three parts:

  1. All AVFoundation reads of a tick now run as one coalesced batch on a dedicated background queue. accessLog() and currentTime() are read once per tick instead of twice, the main actor only suspends across the batch, and a stalled mediaserverd reply parks a GCD worker thread instead of the main thread. A tick that resumes after stop() or after a reload seam swapped the player mid-read drops its stale snapshot instead of publishing it into the new session, and the read queue is per sampler instance so a wedged read from a stopped session can never delay the next session's telemetry.
  2. The 30 s memory probe's buffer inspection (currentTime() + loadedTimeRanges) was the same class of main-actor sync XPC read and now hops through the same shared helper.
  3. seekableEnd is now a KVO mirror of seekableTimeRanges instead of a per-call synchronous read. Live clock-tick sinks and the 1 Hz paused-live window timer were reading it at a cadence with the same watchdog exposure; every consumer, including the live seek paths, now reads a cached value that playlist refreshes keep current.

As a side effect, the [LagDiag] diagnostic line no longer pays any AVFoundation cost when verbose logging is disabled: it consumes the same pre-read batch as the telemetry snapshot.

No API changes; engine.diagnostics.liveTelemetry behaves exactly as before. 670 tests green, including new coverage that pins the off-main behavior with deliberately stalled reads and verifies stale in-flight snapshots are dropped on session teardown and player swaps.

Thanks to @l984-451 for the exceptional report: the Sentry-backed stack, the exact inventory of blocking reads, and the off-main fix proposal this release implements.