use crate::fixed_point;
use crate::supply::SupplyBound;
use crate::time::{Duration, Service};
use crate::arrival::ArrivalBound;
use crate::wcet::JobCostModel;
const EPSILON: Duration = Duration::epsilon();
const EPSILON_SERVICE: Service = Service::in_interval(EPSILON);
pub type PolledCallbackPriority = i32;
pub fn is_higher_callback_priority_than(
a: PolledCallbackPriority,
b: PolledCallbackPriority,
) -> bool {
a < b
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CallbackType {
Timer,
EventSource,
PolledUnknownPrio,
Polled(PolledCallbackPriority),
}
impl CallbackType {
pub fn is_pp(&self) -> bool {
matches!(
self,
CallbackType::PolledUnknownPrio | CallbackType::Polled(_)
)
}
}
pub struct Callback<'a, 'b, AB: ArrivalBound + ?Sized, CM: JobCostModel + ?Sized> {
response_time_bound: Duration,
arrival_bound: &'a AB,
cost_model: &'b CM,
kind: CallbackType,
}
impl<'a, 'b, AB: ArrivalBound + ?Sized, CM: JobCostModel + ?Sized> Callback<'a, 'b, AB, CM> {
pub fn new(
response_time_bound: Duration,
arrival_bound: &'a AB,
cost_model: &'b CM,
kind: CallbackType,
) -> Callback<'a, 'b, AB, CM> {
Callback {
response_time_bound,
arrival_bound,
cost_model,
kind,
}
}
fn direct_rbf(
&self,
interfered_with: &CallbackType,
delta: Duration,
num_polling_points: usize,
) -> Service {
let effective_interval = (delta + self.response_time_bound).saturating_sub(EPSILON);
let arrived = self.arrival_bound.number_arrivals(effective_interval);
let n = match self.kind {
CallbackType::Timer | CallbackType::EventSource => arrived,
CallbackType::PolledUnknownPrio => arrived.min(num_polling_points + 1),
CallbackType::Polled(inf_prio) => match *interfered_with {
CallbackType::Polled(ref_prio) => arrived.min(
num_polling_points
+ is_higher_callback_priority_than(inf_prio, ref_prio) as usize,
),
_ => arrived.min(num_polling_points + 1),
},
};
self.cost_model.cost_of_jobs(n)
}
fn max_self_interfering_instances(&self, delta: Duration) -> usize {
let effective_interval = (delta + self.response_time_bound).saturating_sub(EPSILON);
self.arrival_bound
.number_arrivals(effective_interval)
.saturating_sub(1)
}
fn self_interference_rbf(&self, delta: Duration) -> Service {
self.cost_model
.cost_of_jobs(self.max_self_interfering_instances(delta))
}
fn marginal_execution_cost(&self, delta: Duration) -> Service {
let n = self.max_self_interfering_instances(delta);
self.cost_model.cost_of_jobs(n + 1) - self.cost_model.cost_of_jobs(n)
}
fn polling_point_bound(&self) -> usize {
self.arrival_bound.number_arrivals(self.response_time_bound)
}
fn subchain_polling_point_bound(subchain: &[&Callback<AB, CM>]) -> usize {
subchain.iter().map(|cb| cb.polling_point_bound()).sum()
}
}
#[allow(non_snake_case)]
pub fn rta_subchain<SBF, AB, CM>(
supply: &SBF,
workload: &[Callback<AB, CM>],
subchain: &[&Callback<AB, CM>],
limit: Duration,
) -> fixed_point::SearchResult
where
SBF: SupplyBound + ?Sized,
AB: ArrivalBound + ?Sized,
CM: JobCostModel + ?Sized,
{
let eoc = subchain.last().expect("subchain must not be empty");
debug_assert!(
subchain
.iter()
.all(|sc_cb| workload.iter().any(|wl_cb| std::ptr::eq(*sc_cb, wl_cb))),
"subchain not wholly part of workload"
);
let max_num_polling_points = Callback::subchain_polling_point_bound(subchain);
let rhs_S_star = |s_star: Duration| {
let di = workload
.iter()
.map(|cb| {
if std::ptr::eq(*eoc, cb) {
Service::none()
} else {
cb.direct_rbf(&eoc.kind, s_star, max_num_polling_points)
}
})
.sum();
let si = eoc.self_interference_rbf(s_star);
EPSILON_SERVICE + di + si
};
let S_star = fixed_point::search(supply, limit, rhs_S_star)?;
let supply_star = supply.provided_service(S_star);
let omega = eoc.marginal_execution_cost(S_star);
let rhs_R_star = supply_star.saturating_sub(EPSILON_SERVICE) + omega;
Ok(supply.service_time(rhs_R_star))
}