use crate::tasks::{self, ConvX};
use eva_common::EResult;
use log::warn;
use serde::{Deserialize, Deserializer};
use std::cmp::Ordering;
use std::time::Duration;
use std::time::Instant;
pub struct Loop {
next_iter: Instant,
interval: Duration,
int_micros: i64,
t: Instant,
report: bool,
task_kind: Option<tasks::Kind>,
marked: bool,
}
#[negative_impl::negative_impl]
impl !Send for Loop {}
impl Loop {
pub fn prepare0(interval: Duration) -> Self {
Self::prepare(interval, Duration::default(), false)
}
pub fn prepare_reported(interval: Duration, shift: Duration) -> Self {
Self::prepare(interval, shift, true)
}
pub fn prepare(interval: Duration, shift: Duration, report: bool) -> Self {
let task_kind: Option<tasks::Kind> = if let Some(ch) = tasks::thread_name().chars().next() {
match ch {
'I' => {
tasks::wait_can_run_input();
Some(tasks::Kind::Input)
}
'P' => {
tasks::wait_can_run_program();
Some(tasks::Kind::Program)
}
'O' => {
tasks::wait_can_run_output();
None
}
_ => None,
}
} else {
None
};
tasks::sleep(shift);
let now = Instant::now();
Loop {
next_iter: now + interval,
interval,
int_micros: i64::try_from(interval.as_micros()).unwrap(),
t: now,
report,
task_kind,
marked: task_kind.is_none(),
}
}
pub fn tick(&mut self) -> bool {
if !self.marked {
if let Some(kind) = self.task_kind {
tasks::mark_thread_ready(kind);
}
self.marked = true;
}
let t = Instant::now();
let result = match t.cmp(&self.next_iter) {
Ordering::Greater => false,
Ordering::Equal => true,
Ordering::Less => {
tasks::sleep(self.next_iter - t);
true
}
};
if result {
self.next_iter += self.interval;
} else {
self.next_iter = Instant::now() + self.interval;
warn!(
"{} loop timeout ({:?} + {:?})",
tasks::thread_name(),
self.interval,
self.next_iter.elapsed()
);
}
if self.report {
let t = Instant::now();
#[allow(clippy::cast_possible_truncation)]
let jitter = (self.int_micros - (t.duration_since(self.t)).as_micros() as i64)
.unsigned_abs()
.as_u16_max();
tasks::report_jitter(jitter);
self.t = t;
};
result
}
}
pub(crate) fn parse_interval(s: &str) -> EResult<u64> {
if let Some(v) = s.strip_suffix("ms") {
Ok(v.parse::<u64>()? * 1_000_000)
} else if let Some(v) = s.strip_suffix("us") {
Ok(v.parse::<u64>()? * 1_000)
} else if let Some(v) = s.strip_suffix("ns") {
Ok(v.parse::<u64>()?)
} else if let Some(v) = s.strip_suffix('s') {
Ok(v.parse::<u64>()? * 1_000_000_000)
} else {
Ok(s.parse::<u64>()? * 1_000_000_000)
}
}
#[allow(dead_code)]
#[inline]
pub(crate) fn deserialize_interval_as_nanos<'de, D>(deserializer: D) -> Result<u64, D::Error>
where
D: Deserializer<'de>,
{
let buf = String::deserialize(deserializer)?;
parse_interval(&buf).map_err(serde::de::Error::custom)
}
#[allow(dead_code)]
#[inline]
pub(crate) fn deserialize_opt_interval_as_nanos<'de, D>(
deserializer: D,
) -> Result<Option<u64>, D::Error>
where
D: Deserializer<'de>,
{
let buf: Option<String> = Option::deserialize(deserializer)?;
if let Some(b) = buf {
Ok(Some(parse_interval(&b).map_err(serde::de::Error::custom)?))
} else {
Ok(None)
}
}