[fix](trx-server): avoid spectrum hang on overflow
Avoid SoapySDR overflow restart wedge and coalesce concurrent GetSpectrum requests.\n\nCo-authored-by: OpenAI Codex <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -323,6 +323,54 @@ pub async fn run_rig_task(
|
||||
|
||||
// Process each request
|
||||
while let Some(RigRequest { cmd, respond_to, .. }) = batch.pop() {
|
||||
if matches!(cmd, RigCommand::GetSpectrum) {
|
||||
let mut responders = vec![respond_to];
|
||||
let mut idx = 0;
|
||||
while idx < batch.len() {
|
||||
if matches!(batch[idx].cmd, RigCommand::GetSpectrum) {
|
||||
let req = batch.swap_remove(idx);
|
||||
responders.push(req.respond_to);
|
||||
} else {
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut cmd_ctx = CommandExecContext {
|
||||
rig: &mut rig,
|
||||
state: &mut state,
|
||||
machine: &mut machine,
|
||||
emitter: &emitter,
|
||||
poll_pause_until: &mut poll_pause_until,
|
||||
last_power_on: &mut last_power_on,
|
||||
state_tx: &state_tx,
|
||||
retry,
|
||||
histories: &histories,
|
||||
};
|
||||
let result = match time::timeout(
|
||||
COMMAND_EXEC_TIMEOUT,
|
||||
process_command(RigCommand::GetSpectrum, &mut cmd_ctx),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(result) => result,
|
||||
Err(_) => {
|
||||
error!(
|
||||
"Rig command GetSpectrum timed out after {:?}",
|
||||
COMMAND_EXEC_TIMEOUT
|
||||
);
|
||||
Err(RigError::communication(format!(
|
||||
"command timed out after {:?}",
|
||||
COMMAND_EXEC_TIMEOUT
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
||||
for responder in responders {
|
||||
let _ = responder.send(result.clone());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let cmd_label = format!("{:?}", cmd);
|
||||
let log_command = !matches!(&cmd, RigCommand::GetSpectrum);
|
||||
let started = Instant::now();
|
||||
|
||||
@@ -180,20 +180,24 @@ impl IqSource for RealIqSource {
|
||||
}
|
||||
|
||||
fn handle_read_error(&mut self, err: &str, streak: u32) -> Result<bool, String> {
|
||||
const OVERFLOW_RESTART_STREAK: u32 = 50;
|
||||
const NON_OVERFLOW_RESTART_STREAK: u32 = 10;
|
||||
|
||||
let err_lc = err.to_ascii_lowercase();
|
||||
let is_overrun = err_lc.contains("overflow") || err_lc.contains("overrun");
|
||||
|
||||
if is_overrun {
|
||||
// Overflow is often transient; avoid immediate stream restart churn.
|
||||
// Only restart after several consecutive overflow failures.
|
||||
if streak < 3 {
|
||||
return Ok(true);
|
||||
}
|
||||
tracing::warn!(
|
||||
"SoapySDR RX overflow persists (streak={}); restarting RX stream",
|
||||
// Some SoapySDR drivers can wedge inside deactivate/activate after
|
||||
// repeated overflow. Keep backing off reads, but avoid automatic
|
||||
// stream restart on overflow so the server remains responsive.
|
||||
if streak == OVERFLOW_RESTART_STREAK {
|
||||
tracing::error!(
|
||||
"SoapySDR RX overflow persists (streak={}); skipping automatic stream restart to avoid driver wedge",
|
||||
streak
|
||||
);
|
||||
} else if streak >= 10 {
|
||||
}
|
||||
return Ok(true);
|
||||
} else if streak >= NON_OVERFLOW_RESTART_STREAK {
|
||||
// Non-overflow errors at a high streak (e.g. reads on a
|
||||
// deactivated stream after a failed activate) — attempt a
|
||||
// full restart to recover.
|
||||
|
||||
Reference in New Issue
Block a user