use std::sync::{Arc, Mutex, PoisonError};
use std::thread::ThreadId;
use rayon::prelude::*;
use super::*;
use crate::platform::threads::Workers;
const CHUNK: usize = 1024;
#[derive(Default)]
struct RanOn {
threads: Vec<ThreadId>,
workers: usize,
}
type Recorded = Arc<Mutex<RanOn>>;
struct Adding {
values: Vec<f32>,
total: f32,
recorded: Recorded,
}
impl Adding {
fn new(recorded: Recorded) -> Self {
Self {
values: (0..100_000).map(|at| (at as f32).sin()).collect(),
total: 0.0,
recorded,
}
}
}
impl Game for Adding {
type Meshes = NoMeshes;
type Sounds = NoSounds;
type InputActions = NoInputActions;
type Skyboxes = NoSkyboxes;
type SurfaceStyles = NoSurfaceStyles;
type PostEffects = NoPostEffects;
fn tick(&mut self, _ctx: &mut TickContext<'_, Self>) {
let recorded = Arc::clone(&self.recorded);
self.total = self
.values
.par_chunks(CHUNK)
.map(|chunk| {
recorded
.lock()
.unwrap_or_else(PoisonError::into_inner)
.threads
.push(std::thread::current().id());
chunk.iter().sum::<f32>()
})
.collect::<Vec<f32>>()
.iter()
.sum();
recorded
.lock()
.unwrap_or_else(PoisonError::into_inner)
.workers = rayon::current_num_threads();
}
fn frame(&mut self, _ctx: &mut FrameContext<'_, Self>) {}
}
#[test]
fn a_parallel_iterator_runs_on_the_engines_workers_in_every_session_of_one_process() {
for title in ["headless workers", "headless workers again"] {
let recorded = Recorded::default();
let started = Session::new(raw(title), UVec2::splat(SIDE), |_ctx| {
Ok(Adding::new(Arc::clone(&recorded)))
});
let Ok(mut session) = started else {
eprintln!("skipped: this machine has no usable graphics adapter");
return;
};
session.tick();
let ran = recorded.lock().unwrap_or_else(PoisonError::into_inner);
assert_eq!(
ran.workers,
Workers::here_again().count(),
"{title} reached the pool the engine built, not one of rayon's own"
);
assert!(!ran.threads.is_empty(), "{title} ran its chunks somewhere");
assert!(
ran.threads
.iter()
.all(|ran| *ran != std::thread::current().id()),
"{title} ran no chunk on the thread it runs the game on"
);
}
}