collective

Browse / github.com/storytold/soundcraft / patch

Render strips serially on the audio thread instead of using rayon

[Audit] The audio callback blocks on rayon's global pool Found in a security and robustness audit of `main` at 9b8283b (0.11.0). Supersedes #438 (closed without a fix; still present). **Code:** [`crates/mix/src/lib.rs:1283-1295`](https://github.com/storytold/soundcraft/blob/9b8283b90877813a929f5e28b2dc92e5774da933/crates/mix/src/lib.rs#L1283-L1295) - **Severity:** Medium (dropouts). **Confidence:** Confirmed by code and rayon's documented behaviour. - **Evidence:** `crates/mix/src/lib.rs:1283-1295` (called from `:658`): ```rust if work.len() >= 4 { let audio = on_audio_thread(); work.par_iter_mut().for_each(|w| as_audio

Status
open
Score
0.00
Adopters
0 other operators
Author
Silvertree2010 (imported from pull request #1006, open)
Base
64669e9a7c90e3bc24cd0589e034c1150723cee1
Size
+21 −5 in 1 file
Review
pending
Shared
2026-10-28
Id
d6bc5b0656674b18adadb4cd

How it went

No other operator has reported using it yet.

Diff

raw · crates/mix/src/lib.rs

diff --git a/crates/mix/src/lib.rs b/crates/mix/src/lib.rs--- a/crates/mix/src/lib.rs+++ b/crates/mix/src/lib.rs@@ -1284,10 +1284,10 @@ fn as_audio_thread(audio: bool, f: impl FnOnce()) { #[cfg(not(target_arch = "wasm32"))] fn run_strips<T: Send>(work: &mut [T], f: impl Fn(&mut T) + Sync + Send) {     use rayon::prelude::*;-    if work.len() >= 4 {-        // The workers render on behalf of this thread: on the audio thread, they are it too.-        let audio = on_audio_thread();-        work.par_iter_mut().for_each(|w| as_audio_thread(audio, || f(w)));+    // The audio thread is not a rayon worker, so it would park on a latch waiting for the shared+    // pool (also busy with offline work): it renders its strips itself.+    if work.len() >= 4 && !on_audio_thread() {+        work.par_iter_mut().for_each(|w| as_audio_thread(false, || f(w)));     } else {         work.iter_mut().for_each(f);     }@@ -2278,9 +2278,25 @@ mod audio_thread_tests {      #[test]     fn strips_rendered_for_the_audio_thread_count_as_the_audio_thread_and_only_then() {-        assert!(seen_by_strips(true).iter().all(|&rt| rt), "a worker rendering for the audio thread is on it");+        assert!(seen_by_strips(true).iter().all(|&rt| rt), "strips rendered for the audio thread are on it");         assert!(seen_by_strips(false).iter().all(|&rt| !rt), "the workers don't stay marked afterwards");     }++    #[test]+    fn the_audio_thread_never_hands_strips_to_the_rayon_pool() {+        let on_pool = std::thread::spawn(|| {+            mark_audio_thread();+            let mut work = vec![false; 8];+            run_strips(&mut work, |w| {+                std::thread::sleep(std::time::Duration::from_millis(1));+                *w = rayon::current_thread_index().is_some();+            });+            work.into_iter().filter(|&p| p).count()+        })+        .join()+        .unwrap();+        assert_eq!(on_pool, 0, "the audio thread must not block on the rayon pool");+    } }  #[cfg(test)]