echeneis 0.1.0

Testing framework for non-blocking code
use std::panic::Location;

pub(crate) mod env;
pub(crate) mod thread;

pub(crate) trait TaskHandle {
    fn yield_now(&mut self, payload: YieldData);
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub(crate) enum YieldData {
    AtomicTransition(Option<&'static Location<'static>>),
    Complete,
    Terminated,
}

pub(crate) struct CheckedTaskHandle {
    current_steps: usize,
    max_steps: usize,
    preempted_location: Option<&'static Location<'static>>,
}

impl CheckedTaskHandle {
    pub(crate) fn new(max_steps: usize) -> Self {
        Self {
            current_steps: 0,
            max_steps,
            preempted_location: None,
        }
    }

    pub(crate) fn with_preempted_loc(
        mut self,
        preempted_location: &'static Location<'static>,
    ) -> Self {
        self.preempted_location.replace(preempted_location);
        self
    }

    pub(crate) fn step(&mut self, loc: Option<&'static Location<'static>>) {
        self.current_steps += 1;
        if self.current_steps > self.max_steps {
            panic!(
                "Maximum steps exceeded {}.\n\
                 Exceeded allowed limit of {} atomic transitions {}.
                 Did you call a spin-loop or similar somewhere?",
                if let Some(loc) = self.preempted_location {
                    format!(
                        "while another thread was preempted before completing the operation at {}",
                        loc
                    )
                } else {
                    "".into()
                },
                self.max_steps,
                if let Some(loc) = loc {
                    format!("while trying to evaluate the operation at {}", loc)
                } else {
                    "".into()
                }
            );
        }
    }
}

impl TaskHandle for CheckedTaskHandle {
    fn yield_now(&mut self, payload: YieldData) {
        match payload {
            YieldData::AtomicTransition(loc) => self.step(loc),
            YieldData::Complete => {}
            YieldData::Terminated => panic!("terminated"),
        }
    }
}