#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Readiness {
Pending,
Ready,
OnCapture,
}
#[derive(Clone, Default)]
pub struct Fence(std::rc::Rc<std::cell::RefCell<State>>);
#[derive(Default)]
struct State {
revision: Option<u32>,
result: Option<Result<(), String>>,
}
impl Fence {
pub fn begin(&self, revision: u32) -> bool {
let mut s = self.0.borrow_mut();
if s.revision == Some(revision) {
return false;
}
*s = State {
revision: Some(revision),
result: None,
};
true
}
pub fn complete(&self, revision: u32, result: Result<(), String>) {
let mut s = self.0.borrow_mut();
if s.revision == Some(revision) && s.result.is_none() {
s.result = Some(result);
}
}
pub fn poll(&self) -> Result<Readiness, String> {
match self.0.borrow().result.as_ref() {
None => Ok(Readiness::Pending),
Some(Ok(())) => Ok(Readiness::Ready),
Some(Err(e)) => Err(e.clone()),
}
}
}
pub type Hold = fn() -> bool;
static HOLDS: std::sync::Mutex<Vec<Hold>> = std::sync::Mutex::new(Vec::new());
pub fn register_hold(hold: Hold) {
HOLDS
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
.push(hold);
}
pub fn holds_settled() -> bool {
let holds = HOLDS
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
.clone();
holds.iter().all(|hold| hold())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn checkpoints_do_not_rearm_or_accept_late_completions() {
let f = Fence::default();
assert!(f.begin(1));
assert!(!f.begin(1));
assert_eq!(f.poll(), Ok(Readiness::Pending));
assert!(f.begin(2));
f.clone().complete(1, Ok(()));
assert_eq!(f.poll(), Ok(Readiness::Pending));
f.complete(2, Ok(()));
assert_eq!(f.poll(), Ok(Readiness::Ready));
assert!(!f.begin(2));
}
#[test]
fn a_hold_defers_captures_until_it_settles() {
use std::sync::atomic::{AtomicBool, Ordering};
static SETTLED: AtomicBool = AtomicBool::new(false);
assert!(holds_settled());
register_hold(|| SETTLED.load(Ordering::Relaxed));
assert!(!holds_settled());
SETTLED.store(true, Ordering::Relaxed);
assert!(holds_settled());
}
#[test]
fn errors_and_windows_are_independent() {
let a = Fence::default();
let b = Fence::default();
a.begin(7);
b.begin(7);
a.complete(7, Err("window closed".into()));
a.complete(7, Ok(()));
assert_eq!(a.poll(), Err("window closed".into()));
assert_eq!(b.poll(), Ok(Readiness::Pending));
a.begin(8);
a.complete(8, Ok(()));
assert_eq!(a.poll(), Ok(Readiness::Ready));
}
}