From 26e90d5bfc3deda97d53a350855612407a6f8369 Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Sat, 9 May 2026 23:22:39 +0200 Subject: [PATCH 1/2] fix(alsa): check PCM RT-safety before promotion --- Cargo.toml | 1 + src/host/alsa/mod.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index f334a9736..ea69fa9fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -115,6 +115,7 @@ jack = { version = "0.13", optional = true } [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd"))'.dependencies] alsa = "0.11" +alsa-sys = "0.4" libc = "0.2" audio_thread_priority = { version = "0.35", optional = true, default-features = false } jack = { version = "0.13", optional = true } diff --git a/src/host/alsa/mod.rs b/src/host/alsa/mod.rs index d53217b57..d8c8075b7 100644 --- a/src/host/alsa/mod.rs +++ b/src/host/alsa/mod.rs @@ -3,6 +3,7 @@ //! Default backend on Linux and BSD systems. extern crate alsa; +extern crate alsa_sys; extern crate libc; use std::{ @@ -988,6 +989,45 @@ fn output_stream_worker( fn boost_current_thread_priority( stream: &StreamInner, ) -> Result { + use alsa_sys::*; + let raw = unsafe { + (&stream.handle as *const alsa::pcm::PCM) + .cast::<*mut snd_pcm_t>() + .read() + }; + let pcm_type = unsafe { snd_pcm_type(raw) }; + + // Only promote to RT for kernel-backed and pure-computation plugins. Others can exhaust + // RLIMIT_RTTIME when they block or coordinate with non-RT servers and trigger SIGXCPU + // on an RT thread. + if !matches!( + pcm_type, + SND_PCM_TYPE_HW + | SND_PCM_TYPE_HOOKS + | SND_PCM_TYPE_NULL + | SND_PCM_TYPE_COPY + | SND_PCM_TYPE_LINEAR + | SND_PCM_TYPE_ALAW + | SND_PCM_TYPE_MULAW + | SND_PCM_TYPE_ADPCM + | SND_PCM_TYPE_RATE + | SND_PCM_TYPE_ROUTE + | SND_PCM_TYPE_PLUG + | SND_PCM_TYPE_LINEAR_FLOAT + | SND_PCM_TYPE_IEC958 + | SND_PCM_TYPE_SOFTVOL + ) { + let type_name = unsafe { + std::ffi::CStr::from_ptr(snd_pcm_type_name(pcm_type)) + .to_str() + .unwrap_or("unknown") + }; + return Err(Error::new( + ErrorKind::RealtimeDenied, + format!("PCM type '{type_name}' is not safe for RT promotion"), + )); + } + let period_frames = u32::try_from(stream.period_size).unwrap_or(0); audio_thread_priority::promote_current_thread_to_real_time(period_frames, stream.sample_rate) .map_err(Error::from) From da07ab65a974bceb9cad9311e22060b7e5f0a59d Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Sat, 9 May 2026 23:52:03 +0200 Subject: [PATCH 2/2] fix(alsa): address review points --- Cargo.toml | 4 ++-- src/host/alsa/mod.rs | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ea69fa9fb..15bd056f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ default = ["realtime-dbus"] # Promotes audio callback threads to real-time or high-priority scheduling for lower latency # Requires: (Linux/BSD) `rtprio` granted in `limits.conf` (e.g. `@audio - rtprio 95`) # Platform: Linux, DragonFly BSD, FreeBSD, NetBSD, Windows, Android -realtime = ["dep:audio_thread_priority"] +realtime = ["dep:audio_thread_priority", "dep:alsa-sys"] # D-Bus/rtkit support on top of `realtime` for RT scheduling on Linux/BSD desktop systems # Requires: D-Bus development libraries (libdbus-1-dev or equivalent) @@ -115,7 +115,7 @@ jack = { version = "0.13", optional = true } [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd"))'.dependencies] alsa = "0.11" -alsa-sys = "0.4" +alsa-sys = { version = "0.4", optional = true } libc = "0.2" audio_thread_priority = { version = "0.35", optional = true, default-features = false } jack = { version = "0.13", optional = true } diff --git a/src/host/alsa/mod.rs b/src/host/alsa/mod.rs index d8c8075b7..f98f7d01f 100644 --- a/src/host/alsa/mod.rs +++ b/src/host/alsa/mod.rs @@ -3,6 +3,7 @@ //! Default backend on Linux and BSD systems. extern crate alsa; +#[cfg(feature = "realtime")] extern crate alsa_sys; extern crate libc; @@ -990,6 +991,9 @@ fn boost_current_thread_priority( stream: &StreamInner, ) -> Result { use alsa_sys::*; + // SAFETY: `alsa::pcm::PCM` is `pub struct PCM(*mut snd_pcm_t, Cell)`. The crate + // does not expose a public `as_ptr()`, but we can cast and read from it. + // TODO: replace with `stream.handle.as_ptr()` once alsa-rs exposes it publicly. let raw = unsafe { (&stream.handle as *const alsa::pcm::PCM) .cast::<*mut snd_pcm_t>() @@ -1022,9 +1026,9 @@ fn boost_current_thread_priority( .to_str() .unwrap_or("unknown") }; - return Err(Error::new( + return Err(Error::with_message( ErrorKind::RealtimeDenied, - format!("PCM type '{type_name}' is not safe for RT promotion"), + format!("PCM type '{type_name}' is not eligible for real-time promotion"), )); }