use std::future::Future;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Waited {
Done,
Lost,
TimedOut,
}
pub(crate) trait MappingWait {
type Yield: Future<Output = Result<(), Self::Error>>;
type Error;
fn now_ms(&self) -> f64;
fn poll(&mut self);
fn done(&self) -> bool;
fn lost(&self) -> bool;
fn yield_task(&mut self) -> Self::Yield;
}
pub(crate) async fn wait_for_mapping<W: MappingWait>(
wait: &mut W,
timeout_ms: f64,
) -> Result<Waited, W::Error> {
let deadline = wait.now_ms() + timeout_ms;
loop {
wait.poll();
if wait.done() {
return Ok(Waited::Done);
}
if wait.lost() {
return Ok(Waited::Lost);
}
if wait.now_ms() > deadline {
return Ok(Waited::TimedOut);
}
wait.yield_task().await?;
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::Cell;
use std::rc::Rc;
struct Fake {
clock: Rc<Cell<f64>>,
gpu_ms: f64,
yield_ms: f64,
mapped: bool,
lost_at: Option<f64>,
polls: u32,
}
impl Fake {
fn new(gpu_ms: f64, yield_ms: f64) -> Self {
Self {
clock: Rc::new(Cell::new(0.0)),
gpu_ms,
yield_ms,
mapped: false,
lost_at: None,
polls: 0,
}
}
}
impl MappingWait for Fake {
type Yield = std::future::Ready<Result<(), ()>>;
type Error = ();
fn now_ms(&self) -> f64 {
self.clock.get()
}
fn poll(&mut self) {
self.polls += 1;
if self.clock.get() >= self.gpu_ms {
self.mapped = true;
}
}
fn done(&self) -> bool {
self.mapped
}
fn lost(&self) -> bool {
self.lost_at.is_some_and(|at| self.clock.get() >= at)
}
fn yield_task(&mut self) -> Self::Yield {
self.clock.set(self.clock.get() + self.yield_ms);
std::future::ready(Ok(()))
}
}
fn run(wait: &mut Fake, timeout_ms: f64) -> Waited {
let mut future = std::pin::pin!(wait_for_mapping(wait, timeout_ms));
let mut context = std::task::Context::from_waker(std::task::Waker::noop());
match future.as_mut().poll(&mut context) {
std::task::Poll::Ready(waited) => waited.expect("the fake never fails"),
std::task::Poll::Pending => panic!("the fake's yields are ready"),
}
}
#[test]
fn a_yield_longer_than_the_deadline_still_finds_a_finished_mapping() {
let mut wait = Fake::new(5.0, 60_000.0);
assert_eq!(run(&mut wait, 30_000.0), Waited::Done);
}
#[test]
fn a_mapping_that_never_finishes_times_out_after_a_poll_past_the_deadline() {
let mut wait = Fake::new(f64::INFINITY, 4.0);
assert_eq!(run(&mut wait, 30.0), Waited::TimedOut);
assert_eq!(wait.polls, 9);
}
#[test]
fn a_short_wait_polls_until_the_gpu_finishes() {
let mut wait = Fake::new(10.0, 4.0);
assert_eq!(run(&mut wait, 30_000.0), Waited::Done);
assert_eq!(wait.polls, 4);
}
#[test]
fn a_lost_device_ends_the_wait() {
let mut wait = Fake::new(f64::INFINITY, 4.0);
wait.lost_at = Some(8.0);
assert_eq!(run(&mut wait, 30_000.0), Waited::Lost);
}
}