AetherEngine 5.17.3
Fixed
EXC_RESOURCE memory-limit crash on the native loopback pipeline: the persistent source reader kept pulling from the origin while the muxer was correctly backpressure-stalled (#174).
The persistent reader (the loopback HLSVideoEngine/HLSSegmentProducer playback path's source fetch) applied window backpressure by blocking the URLSession delegate callback until the consumer drained below the 16 MB high-water mark. Blocking the delegate has no flow-control contract: whether the connection actually stops reading from the socket is a transport implementation detail. Plain HTTP/1.1 happens to park the transfer after a few MB of internal buffering, but the TLS/H2 path keeps reading at line rate and buffers the undelivered body in unbounded URLSession-internal allocations. With a realtime consumer, that buffering grows at line rate minus playback rate, goes cold, gets compressed rather than freed, and kills the app at the jetsam limit after a few minutes on a fast origin.
The reporter's evidence pinned this precisely: every engine thread idle or correctly backpressured at crash time, only the network thread active mid SSL_read; avioFetchedMB (bytes actually delivered to the reader) growing at playback rate while mallocMB grew 12x faster; vmCmp climbing ~500 MB per 30 s with rss flat; the growth invisible to Instruments' live-allocation tracking.
The reader now uses task suspend/resume, the contractual flow control ("a task, while suspended, produces no network traffic") that the streaming-mode reader already used:
- delivery never blocks; the task suspends once the sliding window exceeds the 16 MB high-water mark,
- the read loop resumes it when the drain crosses the new 8 MB low-water mark, and before parking in the frontier wait (a suspended task would never deliver),
- every reconnect and teardown path (
startPersistentConnection,markClosed,close, the optimistic-open fallback) balances a pending suspend before cancel so the task's suspend count stays level.
Reported by Enoch Abiodun (#174), with a debugger-level thread-stack analysis and memprobe series that led straight to the failing layer. Covered by Issue174PersistentReadBackpressureTests against a loopback origin that counts every byte it actually manages to serve: a stalled consumer must park the transfer, a resumed consumer must keep receiving, and teardown while suspended must release the task without hanging.