mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-18 18:31:02 +03:00
fix(audio): three 100% CPU busy loops on the _pa path (#16229)
The Linux audio service in `--server` ignored the `Err` from `next_raw()`, so once the cm-side `_pa` peer closed, every iteration re-polled a dead socket: tokio-util's paused `Framed` issues one 0-byte read per poll and returns ready at once, never `Pending`. The thread never parked and burned a full core for the life of the process. Propagate instead, so `ServiceTmpl::run`'s existing backoff ends the inner loop and reconnects. Two sibling loops on the same audio path have the same shape: - `ipc::start_pa` (runs in `--cm`) ignored the `Err` from `psimple::Simple::read`, so a dead pulse handle spins there instead. - `start_voice_call`'s forwarding thread polls two channels with `try_recv` and has no blocking primitive at all: measured 99.8% of a core for the whole call, against 1.0% with a 1 ms pause (audio packets arrive every 10 ms). fix https://github.com/rustdesk/rustdesk/issues/16226 Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -598,6 +598,9 @@ impl<T: InvokeUiSession> Remote<T> {
|
|||||||
} else {
|
} else {
|
||||||
log::debug!("Failed to record local audio channel: {}", err);
|
log::debug!("Failed to record local audio channel: {}", err);
|
||||||
}
|
}
|
||||||
|
// Both arms fall through with nothing else in this loop blocking, so
|
||||||
|
// without a pause the thread spun a core for the whole voice call.
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1497,7 +1497,13 @@ pub async fn start_pa() {
|
|||||||
None, // Use default buffering attributes
|
None, // Use default buffering attributes
|
||||||
) {
|
) {
|
||||||
Ok(s) => loop {
|
Ok(s) => loop {
|
||||||
if let Ok(_) = s.read(&mut buf) {
|
// A dead pulse handle fails every read at once, so ignoring the
|
||||||
|
// error left nothing pacing this loop and it burned a core.
|
||||||
|
if let Err(err) = s.read(&mut buf) {
|
||||||
|
log::error!("Failed to read audio data:{}", err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
{
|
||||||
let out =
|
let out =
|
||||||
if buf.iter().filter(|x| **x != 0).next().is_none() {
|
if buf.iter().filter(|x| **x != 0).next().is_none() {
|
||||||
vec![]
|
vec![]
|
||||||
|
|||||||
@@ -124,7 +124,11 @@ mod pa_impl {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
if let Ok(data) = stream.next_raw().await {
|
{
|
||||||
|
// The `_pa` peer closing surfaces as `Err` here. Dropping it left the loop polling
|
||||||
|
// a dead socket -- one 0-byte read per poll, ready at once and never `Pending` --
|
||||||
|
// which burned a full core for the rest of the process lifetime.
|
||||||
|
let data = stream.next_raw().await?;
|
||||||
if data.len() == 0 {
|
if data.len() == 0 {
|
||||||
send_f32(&zero_audio_frame, &mut encoder, &sp);
|
send_f32(&zero_audio_frame, &mut encoder, &sp);
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
Reference in New Issue
Block a user