[fix](trx-server): prevent audio thread crash on ALSA EPIPE
CPAL error callbacks can fire millions of times per second on ALSA EPIPE (errno -32). Previously each invocation did a string allocation and mutex lock, saturating CPU and eventually crashing the server. - Use atomic swap in both input and output error callbacks so only the first error fires the expensive log+notify path; all subsequent hits cost a single atomic op and return immediately. - Replace stream.play()? with explicit error handling in run_playback so a play failure triggers stream recreation instead of permanently terminating the playback thread. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Stan Grams <stanislawgrams@gmail.com>
This commit is contained in:
+24
-11
@@ -317,9 +317,13 @@ fn run_capture(
|
||||
let stream_failed = stream_failed.clone();
|
||||
let stream_err_tx = stream_err_tx.clone();
|
||||
move |err| {
|
||||
input_err_logger.log(&err.to_string());
|
||||
stream_failed.store(true, Ordering::SeqCst);
|
||||
let _ = stream_err_tx.try_send(());
|
||||
// swap ensures only the first error does expensive work;
|
||||
// subsequent callbacks (can fire millions/s on ALSA EPIPE)
|
||||
// return immediately after a single atomic op.
|
||||
if !stream_failed.swap(true, Ordering::SeqCst) {
|
||||
input_err_logger.log(&err.to_string());
|
||||
let _ = stream_err_tx.try_send(());
|
||||
}
|
||||
}
|
||||
},
|
||||
None,
|
||||
@@ -503,9 +507,13 @@ fn run_playback(
|
||||
let stream_failed = stream_failed.clone();
|
||||
let stream_err_tx = stream_err_tx.clone();
|
||||
move |err| {
|
||||
output_err_logger.log(&err.to_string());
|
||||
stream_failed.store(true, Ordering::SeqCst);
|
||||
let _ = stream_err_tx.try_send(());
|
||||
// swap ensures only the first error does expensive work;
|
||||
// subsequent callbacks (can fire millions/s on ALSA EPIPE)
|
||||
// return immediately after a single atomic op.
|
||||
if !stream_failed.swap(true, Ordering::SeqCst) {
|
||||
output_err_logger.log(&err.to_string());
|
||||
let _ = stream_err_tx.try_send(());
|
||||
}
|
||||
}
|
||||
},
|
||||
None,
|
||||
@@ -521,10 +529,12 @@ fn run_playback(
|
||||
}
|
||||
};
|
||||
|
||||
if !playing {
|
||||
// stay paused until packets arrive
|
||||
} else {
|
||||
stream.play()?;
|
||||
if playing {
|
||||
if let Err(e) = stream.play() {
|
||||
warn!("Audio playback: stream.play failed, recreating: {}", e);
|
||||
std::thread::sleep(AUDIO_STREAM_RECOVERY_DELAY);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
@@ -544,7 +554,10 @@ fn run_playback(
|
||||
match rx.try_recv() {
|
||||
Ok(packet) => {
|
||||
if !playing {
|
||||
stream.play()?;
|
||||
if let Err(e) = stream.play() {
|
||||
warn!("Audio playback: stream.play failed, recreating: {}", e);
|
||||
break;
|
||||
}
|
||||
playing = true;
|
||||
info!("Audio playback: started");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user