From e569fbc50ee4822016a7eb03ad3893a9cc414539 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Sat, 24 Oct 2026 03:53:05 +0000 Subject: [PATCH 1/3] Render strips serially on the audio thread instead of using rayon Fixes #886 Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 --- crates/mix/src/threads.rs | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/crates/mix/src/threads.rs b/crates/mix/src/threads.rs index c71fb3fc..f3e34b69 100644 --- a/crates/mix/src/threads.rs +++ b/crates/mix/src/threads.rs @@ -33,10 +33,10 @@ fn as_audio_thread(audio: bool, f: impl FnOnce()) { #[cfg(not(target_arch = "wasm32"))] pub(crate) fn run_strips(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); } @@ -86,7 +86,23 @@ 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"); + } } From 8cdcab26975e49491897e408b52714b269eff941 Mon Sep 17 00:00:00 2001 From: FredApps <153173854+FredApps@users.noreply.github.com> Date: Sun, 8 Nov 2026 02:05:36 +0100 Subject: [PATCH 2/3] fix(mix): keep audio thread tests native-only --- crates/mix/src/threads.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/mix/src/threads.rs b/crates/mix/src/threads.rs index f3e34b69..0238132b 100644 --- a/crates/mix/src/threads.rs +++ b/crates/mix/src/threads.rs @@ -47,7 +47,7 @@ pub(crate) fn run_strips(work: &mut [T], f: impl Fn(&mut T) + Sync + Se work.iter_mut().for_each(f); } -#[cfg(test)] +#[cfg(all(test, not(target_arch = "wasm32")))] mod audio_thread_tests { use super::*; From 26cafa85bb8a0011bb368ae5bd1034ae8193a4b9 Mon Sep 17 00:00:00 2001 From: FredApps <153173854+FredApps@users.noreply.github.com> Date: Sun, 8 Nov 2026 02:10:22 +0100 Subject: [PATCH 3/3] test(mix): report strip helper errors without unwrap --- crates/mix/src/threads.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/mix/src/threads.rs b/crates/mix/src/threads.rs index 0238132b..b9f05f61 100644 --- a/crates/mix/src/threads.rs +++ b/crates/mix/src/threads.rs @@ -67,7 +67,7 @@ mod audio_thread_tests { /// What `on_audio_thread` says inside each strip job, for strips run from a thread that is /// (or is not) the audio thread. - fn seen_by_strips(audio: bool) -> Vec { + fn seen_by_strips(audio: bool) -> Result, &'static str> { std::thread::spawn(move || { if audio { mark_audio_thread(); @@ -78,16 +78,16 @@ mod audio_thread_tests { std::thread::sleep(std::time::Duration::from_millis(1)); *w = Some(on_audio_thread()); }); - work.into_iter().map(|w| w.unwrap()).collect() + work.into_iter().collect::>>().ok_or("a strip did not run") }) .join() - .unwrap() + .map_err(|_| "the strip render thread panicked")? } #[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), "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"); + assert_eq!(seen_by_strips(true), Ok(vec![true; 64]), "strips rendered for the audio thread are on it"); + assert_eq!(seen_by_strips(false), Ok(vec![false; 64]), "the workers don't stay marked afterwards"); } #[test]