mirror of
https://github.com/storytold/soundcraft.git
synced 2026-11-09 20:04:53 +00:00
Merge pull request #1006 from Silvertree2010/fix/886-audit-the-audio-callback-blocks
Render strips serially on the audio thread instead of using rayon
This commit is contained in:
1 file changed
+26
-10
+26
-10
@@ -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);
|
||||
}
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
});
|
||||
work.into_iter().filter(|&p| p).count()
|
||||
})
|
||||
.join()
|
||||
.unwrap();
|
||||
assert_eq!(on_pool, 0, "the audio thread must not block on the rayon pool");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user