use crate::curve::{AggregateExt, Curve};
use crate::iterators::curve::CurveDeltaIterator;
use crate::iterators::task::TaskDemandIterator;
use crate::iterators::{CurveIterator, ReclassifyIterator};
use crate::server::ActualServerExecution;
use crate::system::System;
use crate::task::curve_types::{
ActualTaskExecution, AvailableTaskExecution, HigherPriorityTaskDemand,
};
use crate::time::{TimeUnit, UnitNumber};
use crate::window::WindowEnd;
use crate::window::{Demand, Window};
pub mod curve_types {
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
pub struct TaskDemand;
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
pub struct HigherPriorityTaskDemand;
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
pub struct AvailableTaskExecution;
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
pub struct ActualTaskExecution;
}
#[derive(Debug, Clone, Copy)]
pub struct Task {
pub offset: TimeUnit,
pub demand: TimeUnit,
pub interval: TimeUnit,
}
impl Task {
#[must_use]
pub fn new<I: Into<TimeUnit>>(demand: I, interval: I, offset: I) -> Self {
let demand = demand.into();
let interval = interval.into();
if interval < demand {
panic!("Task can't have an interval shorter than its demand!")
}
Task {
offset: offset.into(),
demand,
interval,
}
}
#[must_use]
pub fn higher_priority_task_demand_iter(
tasks: &[Self],
index: usize,
) -> impl CurveIterator<CurveKind = HigherPriorityTaskDemand> + Clone + '_ {
tasks[..index]
.iter()
.map(|task| task.into_iter())
.aggregate::<ReclassifyIterator<_, _>>()
}
#[must_use]
pub fn available_execution_curve_impl<'a, HPTD, ASEC>(
constrained_server_execution_curve: ASEC,
higher_priority_task_demand: HPTD,
) -> impl CurveIterator<CurveKind = AvailableTaskExecution> + Clone + 'a
where
HPTD: CurveIterator<CurveKind = HigherPriorityTaskDemand> + Clone + 'a,
ASEC: CurveIterator<CurveKind = ActualServerExecution> + Clone + 'a,
{
let delta = CurveDeltaIterator::new(
constrained_server_execution_curve,
higher_priority_task_demand,
);
delta
.remaining_supply()
.reclassify::<AvailableTaskExecution>()
}
#[must_use]
pub fn original_actual_execution_curve_iter<'a>(
system: &'a System,
server_index: usize,
task_index: usize,
) -> impl CurveIterator<CurveKind = ActualTaskExecution> + Clone + 'a {
let asec = system.original_actual_execution_curve_iter(server_index);
let hptd = Task::higher_priority_task_demand_iter(
system.as_servers()[server_index].as_tasks(),
task_index,
);
let available_execution_curve = Task::available_execution_curve_impl(asec, hptd);
let task_demand_curve =
system.as_servers()[server_index].as_tasks()[task_index].into_iter();
CurveDeltaIterator::new(available_execution_curve, task_demand_curve)
.overlap::<ActualTaskExecution>()
}
#[must_use]
pub fn fixed_actual_execution_curve_iter<'a>(
system: &'a System,
server_index: usize,
task_index: usize,
) -> impl CurveIterator<CurveKind = ActualTaskExecution> + Clone + 'a {
let asec = system.fixed_actual_execution_curve_iter(server_index);
let hptd = Task::higher_priority_task_demand_iter(
system.as_servers()[server_index].as_tasks(),
task_index,
);
let available_execution_curve = Task::available_execution_curve_impl(asec, hptd);
let task_demand_curve =
system.as_servers()[server_index].as_tasks()[task_index].into_iter();
CurveDeltaIterator::new(available_execution_curve, task_demand_curve)
.overlap::<ActualTaskExecution>()
}
#[must_use]
pub fn original_worst_case_response_time(
system: &System,
server_index: usize,
task_index: usize,
arrival_before: TimeUnit,
) -> TimeUnit {
let swh = arrival_before;
let actual_execution_time_iter =
Task::original_actual_execution_curve_iter(system, server_index, task_index);
let task = &system.as_servers()[server_index].as_tasks()[task_index];
let last_job = (swh - task.offset - TimeUnit::ONE) / task.interval;
let total_execution = (last_job + 1) * task.demand;
let mut provided = WindowEnd::Finite(TimeUnit::ZERO);
let actual_execution_time: Curve<_> = actual_execution_time_iter
.take_while_curve(|task| {
let take = provided < total_execution;
provided += task.length();
take
})
.collect_curve();
assert!(
task.job_arrival(last_job) < swh,
"Last job should arrive before the system wide hyper period"
);
assert!(
swh <= task.job_arrival(last_job + 1),
"The job after the last job would arrive after or at the system wide hyper period"
);
assert!(
WindowEnd::Finite((last_job + 1) * task.demand) <= actual_execution_time.capacity(),
"There should be enough capacity for the last job"
);
(0..=last_job)
.into_iter()
.map(|job| {
let arrival = task.job_arrival(job);
let t = (job + 1) * task.demand;
Task::time_to_provide(&actual_execution_time, t) - arrival
})
.max()
.unwrap_or(TimeUnit::ZERO)
}
#[must_use]
pub fn fixed_worst_case_response_time(
system: &System,
server_index: usize,
task_index: usize,
arrival_before: TimeUnit,
) -> TimeUnit {
let swh = arrival_before;
let actual_execution_time_iter =
Task::fixed_actual_execution_curve_iter(system, server_index, task_index);
let task = &system.as_servers()[server_index].as_tasks()[task_index];
let last_job = (swh - task.offset - TimeUnit::ONE) / task.interval;
let total_execution = (last_job + 1) * task.demand;
let mut provided = WindowEnd::Finite(TimeUnit::ZERO);
let actual_execution_time: Curve<_> = actual_execution_time_iter
.take_while_curve(|task| {
let take = provided < total_execution;
provided += task.length();
take
})
.collect_curve();
assert!(
task.job_arrival(last_job) < swh,
"Last job should arrive before the system wide hyper period"
);
assert!(
swh <= task.job_arrival(last_job + 1),
"The job after the last job would arrive after or at the system wide hyper period"
);
assert!(
WindowEnd::Finite((last_job + 1) * task.demand) <= actual_execution_time.capacity(),
"There should be enough capacity for the last job"
);
(0..=last_job)
.into_iter()
.map(|job| {
let arrival = task.job_arrival(job);
let t = (job + 1) * task.demand;
Task::time_to_provide(&actual_execution_time, t) - arrival
})
.max()
.unwrap_or(TimeUnit::ZERO)
}
#[must_use]
pub fn time_to_provide(
actual_execution_time: &Curve<ActualTaskExecution>,
t: TimeUnit,
) -> TimeUnit {
let (index, sum) = actual_execution_time
.as_windows()
.iter()
.enumerate()
.scan(TimeUnit::ZERO, |acc, (index, window)| {
match window.length() {
WindowEnd::Finite(length) => {
*acc += length;
(*acc < t).then(|| (index + 1, *acc))
}
WindowEnd::Infinite => None,
}
})
.last()
.unwrap_or((0, TimeUnit::ZERO));
let b = t - sum;
debug_assert!(
b > TimeUnit::ZERO,
"There should be remaining demand, but b = {:?}",
b
);
actual_execution_time.as_windows()[index].start + b
}
#[must_use]
pub fn job_arrival(&self, job_index: UnitNumber) -> TimeUnit {
self.offset + job_index * self.interval
}
}
impl IntoIterator for Task {
type Item = Window<Demand>;
type IntoIter = TaskDemandIterator;
fn into_iter(self) -> Self::IntoIter {
TaskDemandIterator::new(self)
}
}