@@ -240,6 +240,37 @@ release.
240240 survives at ~ 72 dB SNR.
241241
242242### Fixed
243+ - Abandoning a live stream could leak a child that held the USB claim for
244+ the rest of the process: with the consumer gone, the 64 KB stdout pipe
245+ fills in milliseconds at capture rates and the child blocks inside
246+ write(); hackrf tools' SIGINT/SIGTERM handlers only set an exit flag that
247+ a blocked write never returns to check, and the breakout teardown ended
248+ at an unreaped terminate() -- so hackrf_transfer stayed frozen in
249+ write(), and every later open in the same process failed
250+ ` hackrf_open() failed: Resource busy (-1000) ` . Found on the Linux
251+ hardware verification run: a wall of 7 failures starting immediately
252+ after the two stream-breakout tests, unmoved by open-retries (the claim
253+ was held, not slow to release). Teardown now gives the clean interrupt a
254+ short window with the pipe open (well-behaved children still flush and
255+ exit cleanly -- tested), then closes the read end so a frozen write
256+ becomes EPIPE and the child can die (the kernel releases the USB claim
257+ on any death), then completes the terminate -> kill ladder. Three
258+ regression tests reproduce the frozen-writer state with a deaf, flooding
259+ stub and pin bounded teardown, immediate reopen, and the preserved
260+ clean-exit window.
261+ - Rapid back-to-back device operations could fail with
262+ ` hackrf_open() failed: Resource busy (-1000) ` : after a ` hackrf_* ` child
263+ exits, the kernel takes a moment to release its USB claim, and the next
264+ open can lose that race -- found on the first real-hardware Linux run,
265+ where 7 hardware tests failed with exactly this error (Windows' USB stack
266+ never exposed it). The library now absorbs it with bounded, backed-off
267+ retries in every acquisition mode: blocking/timed runs re-invoke, and
268+ streaming paths (sweep, monitor, the persistent receiver) respawn only if
269+ the child died busy BEFORE yielding anything -- once data has flowed, a
270+ busy error cannot be a stale-claim race and is raised as-is. Tunable via
271+ ` busy_retries ` (default 3) and ` busy_backoff ` (0.25 s, doubling);
272+ ` busy_retries=0 ` restores fail-fast. The test stub can now simulate the
273+ race (` busy_fails=N ` ), and five tests pin the behavior across modes.
243274- The ` atexit ` backstop never reaped an orphaned ` PersistentReceiver ` .
244275 ` PersistentReceiver ` registers itself in the live-handle registry, whose
245276 shutdown hook calls ` if h.is_alive(): h.stop() ` -- but ` is_alive() ` did not
0 commit comments