Repository navigation
20261007 - Acknowledge RSPduo overloads outside the SDRplay event callback - #79
Merged
Merged
Conversation
…lback The event callback acked each PowerOverloadChange by calling sdrplay_api_Update() itself. Whenever another thread had an Update in flight, which is exactly when overloads arrive (the gain re-stage straight after Init(), and retune() jumping onto a strong signal), that deadlocked the SDRplay API: the ack queued behind the in-flight Update, which could not complete while the callback was stuck. Streaming stopped, every Update failed after 5 s with ServiceNotResponding, blah2 exited, and the service was left hung, so every later start found NumDevs=0. The callback now only records the overload state and a pending-ack bit, and process()'s control loop sends the acks under sdrplay_update_mutex on a 50 ms tick. Overload onsets are counted on state transitions, because an unacknowledged event repeats about every 125 ms. Measured on owl (RSPduo, API 3.15) with a standalone harness: the old pattern wedged 3/3 start-ups, acking from the callback with no lock at all wedged 8/8, and the deferred ack ran 13/13 start-ups and 5/5 frequency jumps onto 213 MHz clean. ClickUp 123zgec74cj. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Nodes that overload at start-up wedge the RSPduo. blah2 crash-loops on
NumDevs=0until the SDRplay service is force-reset on a start that happens not to overload, which has taken from 5 minutes to about 7 hours. It has hit 6 nodes across the last three releases (27 September, 4 October and 5 October).The cause is the overload ack.
event_callback()acked eachPowerOverloadChangeby callingsdrplay_api_Update()itself. When another thread had an Update in flight, that deadlocked the SDRplay API. Overloads arrive at exactly those moments: the gain re-stage straight afterInit(), andretune()jumping onto a strong signal. The API serialises Updates, so the ack queued behind the in-flight Update, which could not complete while the callback was stuck. Streaming stopped, every Update failed after 5 s withServiceNotResponding, blah2 exited, and the service was left hung.The exposure dates from v0.3.13 (2026-06-16), which added the post-Init gain Updates. The mutex added in ec84ee8 (July) is not the root cause: acking from the callback with no lock at all wedges just the same.
ClickUp: 123zgec74cj
Change
One file plus its header, in
src/capture/rspduo/:event_callback()now records the overload state and a per-tuner pending-ack bit, then returns. It never callssdrplay_api_Update()and never takes a lock.send_overload_acks()sends the pending acks undersdrplay_update_mutex, so an ack never overlaps aretune()or the start-up re-stage.process()'s control loop calls it on a 50 ms tick (it used to sleep 1 s). Stats still print once a second.Overload_Detectedevent, because an unacknowledged event repeats about every 125 ms.PowerOverloadChangeis logged on a change of state only, for the same reason.sdrplay_api_Tuner_Both(seen onUninit) now updates both tuners. Previously it was attributed to tuner B only.The post-Init gain/AGC re-stage and its Updates are unchanged.
Evidence
Bench work on a test node (RSPduo, API 3.15, 213 MHz illuminator) with a standalone harness that mirrors blah2's device setup:
Neither simpler explanation holds on its own. A blocked event thread alone does not stop Updates, and an unacknowledged overload alone does not stop Updates. The deadlock needs the callback to call back into the API while another Update is in flight.
Verification
The real blah2 image from this branch (CI
build-blah2,publish=false, commit 4319b30) againstghcr.io/offworldlabs/blah2:v0.6.3on the same test node. Test containers used a staged copy of the node's config with the DAB notch off, so tuner A overloads at start-up.NumDevs=1, thenFailed to update Tuner B gain: ServiceNotResponding5.4 s later)Retune Tuner A gain failed: ServiceNotRespondingon all 3 attemptsOn every fixed run the overload still fired, blah2 kept streaming with a map 0-1 s old, both tuners reached their configured gains, and nothing logged
ServiceNotRespondingor a failed ack. CI unit tests pass.Later the same day, with this build pinned as the node's blah2 and retina-gui #110 deployed, driving the real Configuration page:
ServiceNotResponding.ServiceNotResponding.Not covered: a soak longer than a few hours (both test nodes are left running this build), and the staged rollout.
Seen during testing but not caused by this change: two applies about a minute apart (Auto-Calibrate's auto-save, then a config change) restarted
sdrplay_apiServicewhile a freshly started blah2 held the device. The service segfaulted while releasing it, and the new service never registered the RSPduo, so blah2 hung at API open, before any code this PR touches. The cron watchdog recovered it within 7 minutes. That restart path is tracked separately.Docs
blah2-arm has no prose docs for RspDuo. The reasoning lives in the code comments, which this PR updates (the event callback, the mutex, the onset counters and the start-up block). The README does not describe the ack. retina-gui's
docs/features/auto-calibrate.mdreadsServiceNotRespondingas a hard overload, which is largely this bug. That needs updating in retina-gui once a release with this fix is pinned.Not in this PR
sdrplay-restart.shusespkill -x sdrplay_apiService, which can never match because comm is truncated to 15 characters. Fixing the match is not a one-liner:sdrplay.servicerestarts after 1 s, so a working kill would restart the service on every blah2 start with no settle time, which retina-gui found can wedge the device. It is tracked separately.Test plan
ServiceNotResponding🤖 Generated with Claude Code