Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 26 additions & 10 deletions crates/mix/src/threads.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ fn as_audio_thread(audio: bool, f: impl FnOnce()) {
#[cfg(not(target_arch = "wasm32"))]
pub(crate) 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);
}
Expand All @@ -47,7 +47,7 @@ pub(crate) fn run_strips<T: Send>(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::*;

Expand All @@ -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<bool> {
fn seen_by_strips(audio: bool) -> Result<Vec<bool>, &'static str> {
std::thread::spawn(move || {
if audio {
mark_audio_thread();
Expand All @@ -78,15 +78,31 @@ 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::<Option<Vec<_>>>().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), "a worker rendering for the audio thread is 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]
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();
Comment thread
FredApps marked this conversation as resolved.
Resolved
});
work.into_iter().filter(|&p| p).count()
})
.join()
.unwrap();
assert_eq!(on_pool, 0, "the audio thread must not block on the rayon pool");
}
}
Loading