use super::super::{
Access, Engine, NativeSession, Runtime,
tests::{buffer, mock, runtime_without_workers},
};
use super::*;
use std::{
sync::{atomic::AtomicUsize, mpsc},
time::Duration,
};
#[test]
fn finalization_reserves_capacity_and_blocks_native_admission_until_publication() {
let runtime = runtime_without_workers();
let graph = mock(
&runtime,
Engine::Gpu,
&buffer(&runtime),
Access::Write,
|| Ok(()),
);
let completion = graph.submit().unwrap();
let core = &runtime.inner.core;
let run = next_action(&mut core.state.lock().unwrap(), None).unwrap();
assert!(matches!(run, Action::Run(..)));
perform(core, run);
let finish = next_action(&mut core.state.lock().unwrap(), None).unwrap();
assert!(matches!(finish, Action::Finish(..)));
let ticket = {
let mut state = core.state.lock().unwrap();
assert!(state.pending.is_empty());
assert_eq!(state.occupied(), 1);
assert!(!completion.is_complete());
let ticket = state.sequence();
state.native.push_back(ticket);
assert!(!state.native_ready(ticket));
ticket
};
perform(core, finish);
assert!(completion.is_complete());
let mut state = core.state.lock().unwrap();
assert!(state.native_ready(ticket));
assert_eq!(state.occupied(), 1);
state.native.pop_front();
assert_eq!(state.occupied(), 0);
}
#[test]
fn native_waiter_runs_after_prior_submissions_before_later_compute() {
let runtime = Runtime::new().unwrap();
let order = Arc::new(AtomicUsize::new(0));
let (entered, entering) = mpsc::channel();
let (release, released) = mpsc::channel();
let released = Mutex::new(released);
let first = mock(&runtime, Engine::Gpu, &buffer(&runtime), Access::Write, {
let order = order.clone();
move || {
assert_eq!(order.fetch_add(1, Ordering::SeqCst), 0);
entered.send(()).unwrap();
released
.lock()
.unwrap()
.recv_timeout(Duration::from_secs(10))
.unwrap();
Ok(())
}
})
.submit()
.unwrap();
entering.recv_timeout(Duration::from_secs(10)).unwrap();
let (native_entered, native_entering) = mpsc::channel();
let (native_release, native_released) = mpsc::channel();
let native = std::thread::spawn({
let runtime = runtime.clone();
let order = order.clone();
move || {
let mut session = unsafe { NativeSession::new(&runtime, (), |_| Ok(())) };
unsafe {
session.run(|_| {
assert_eq!(order.fetch_add(1, Ordering::SeqCst), 1);
native_entered.send(()).unwrap();
native_released
.recv_timeout(Duration::from_secs(10))
.unwrap();
})
}
.unwrap();
}
});
let core = &runtime.inner.core;
let state = core.state.lock().unwrap();
let (state, timeout) = core
.changed
.wait_timeout_while(state, Duration::from_secs(10), |state| {
state.native.is_empty()
})
.unwrap();
assert!(!timeout.timed_out());
assert_eq!(state.occupied(), 2);
drop(state);
let later = mock(&runtime, Engine::Gpu, &buffer(&runtime), Access::Write, {
let order = order.clone();
move || {
assert_eq!(order.fetch_add(1, Ordering::SeqCst), 2);
Ok(())
}
})
.submit()
.unwrap();
release.send(()).unwrap();
native_entering
.recv_timeout(Duration::from_secs(10))
.unwrap();
assert!(first.is_complete());
assert!(!later.is_complete());
native_release.send(()).unwrap();
native.join().unwrap();
later.wait().unwrap();
assert_eq!(order.load(Ordering::SeqCst), 3);
}