Context. tvOS 26 (AppleTV14,1 ×2), engine 6.7.0, persistent reader, direct-play VOD over LAN. Follow-on to #220's "suspend is advisory": this is what we now believe the long-suspended state does to the whole process.
Finding. During low-bitrate playback we see recurring 10–80 s episodes in which EVERY Network.framework-based flow in the app process goes deaf at once — established WebSockets stop receiving (dying of ETIMEDOUT ~30 s in, their sends unACKed), and no NEW nw flow completes a handshake (URLSession and bare NWConnection fail identically, -1001 at deadline, SYN-ACKs arriving on the wire unprocessed) — while in the same seconds raw BSD sockets from the same process complete TCP handshakes AND full plaintext HTTP exchanges 100% (229/229 in one evening), other processes on the box are unaffected, and the process is idle (4–18% CPU, ~16 threads parked, ~30 fds). On tvOS, TCP for nw flows runs in-process (user-space networking); everything observed is consistent with the process's nw inbound path stalling as a unit — beneath CFNetwork, above the kernel-socket path.
The dose-response tying it to reader dormancy. The reader suspends its data task at high water and can then sit dormant for long stretches holding a full unread receive window (a closed TCP window on an idle established flow). Dormant-stretch length is set by media bitrate (≈ window size / rate). Same fleet, same wire, three runs in 30 hours:
| media rate |
dormant stretch |
episodes |
| ~1.2 Mbps h264 1080p |
~100 s |
~11 in 80 min |
| ~14 Mbps HEVC 4K |
~8.5 s |
1 in 54 min |
| ~88 Mbps HEVC 4K |
~1.4 s |
0 in 118 min |
A paused player is the limiting case (indefinite dormancy) and is where we've recorded the worst episode (11 unbroken minutes). The effect switches back on at will by returning to low-bitrate content.
Why the fix belongs at the reader even though the fault looks like Apple's. Nothing a session owner does dodges the stall once it starts — it hits fresh sessions and bare NWConnection equally. The escape is not entering the dormant state: the engine already ENDS the connection at the hard cap and re-requests on drain (#220's machinery); at low bitrates, though, suspend() engages first and the flow then sits dormant-established ~100 s per cycle. If the reader closed-and-rerequested at high water instead of suspending (or bounded how long a task may stay suspended before the conn is ended), the pathological state never forms — one extra range request per drain cycle (every ~100 s at 1 Mbps; ~2 ms connects on LAN), and a paused player would hold no dormant flow at all.
Happy to run any instrumented build — we have per-second probe arms (URLSession / NWConnection / raw-BSD) that make episodes and their absence directly measurable.
Context. tvOS 26 (AppleTV14,1 ×2), engine 6.7.0, persistent reader, direct-play VOD over LAN. Follow-on to #220's "suspend is advisory": this is what we now believe the long-suspended state does to the whole process.
Finding. During low-bitrate playback we see recurring 10–80 s episodes in which EVERY Network.framework-based flow in the app process goes deaf at once — established WebSockets stop receiving (dying of ETIMEDOUT ~30 s in, their sends unACKed), and no NEW nw flow completes a handshake (URLSession and bare NWConnection fail identically, -1001 at deadline, SYN-ACKs arriving on the wire unprocessed) — while in the same seconds raw BSD sockets from the same process complete TCP handshakes AND full plaintext HTTP exchanges 100% (229/229 in one evening), other processes on the box are unaffected, and the process is idle (4–18% CPU, ~16 threads parked, ~30 fds). On tvOS, TCP for nw flows runs in-process (user-space networking); everything observed is consistent with the process's nw inbound path stalling as a unit — beneath CFNetwork, above the kernel-socket path.
The dose-response tying it to reader dormancy. The reader suspends its data task at high water and can then sit dormant for long stretches holding a full unread receive window (a closed TCP window on an idle established flow). Dormant-stretch length is set by media bitrate (≈ window size / rate). Same fleet, same wire, three runs in 30 hours:
A paused player is the limiting case (indefinite dormancy) and is where we've recorded the worst episode (11 unbroken minutes). The effect switches back on at will by returning to low-bitrate content.
Why the fix belongs at the reader even though the fault looks like Apple's. Nothing a session owner does dodges the stall once it starts — it hits fresh sessions and bare NWConnection equally. The escape is not entering the dormant state: the engine already ENDS the connection at the hard cap and re-requests on drain (#220's machinery); at low bitrates, though, suspend() engages first and the flow then sits dormant-established ~100 s per cycle. If the reader closed-and-rerequested at high water instead of suspending (or bounded how long a task may stay suspended before the conn is ended), the pathological state never forms — one extra range request per drain cycle (every ~100 s at 1 Mbps; ~2 ms connects on LAN), and a paused player would hold no dormant flow at all.
Happy to run any instrumented build — we have per-second probe arms (URLSession / NWConnection / raw-BSD) that make episodes and their absence directly measurable.