1use crate::curve::{AggregateExt, Curve};
4use crate::iterators::curve::CurveDeltaIterator;
5use crate::iterators::task::TaskDemandIterator;
6use crate::iterators::{CurveIterator, ReclassifyIterator};
7use crate::server::ActualServerExecution;
8use crate::system::System;
9use crate::task::curve_types::{
10 ActualTaskExecution, AvailableTaskExecution, HigherPriorityTaskDemand,
11};
12use crate::time::{TimeUnit, UnitNumber};
13use crate::window::WindowEnd;
14use crate::window::{Demand, Window};
15
16pub mod curve_types {
17 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
21 pub struct TaskDemand;
22
23 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
25 pub struct HigherPriorityTaskDemand;
26
27 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
29 pub struct AvailableTaskExecution;
30
31 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
33 pub struct ActualTaskExecution;
34}
35
36#[derive(Debug, Clone, Copy)]
39pub struct Task {
40 pub offset: TimeUnit,
42 pub demand: TimeUnit,
45 pub interval: TimeUnit,
47}
48
49impl Task {
50 #[must_use]
55 pub fn new<I: Into<TimeUnit>>(demand: I, interval: I, offset: I) -> Self {
56 let demand = demand.into();
57 let interval = interval.into();
58
59 if interval < demand {
60 panic!("Task can't have an interval shorter than its demand!")
61 }
62
63 Task {
64 offset: offset.into(),
65 demand,
66 interval,
67 }
68 }
69
70 #[must_use]
73 pub fn higher_priority_task_demand_iter(
74 tasks: &[Self],
75 index: usize,
76 ) -> impl CurveIterator<CurveKind = HigherPriorityTaskDemand> + Clone + '_ {
77 tasks[..index]
78 .iter()
79 .map(|task| task.into_iter())
80 .aggregate::<ReclassifyIterator<_, _>>()
81 }
82
83 #[must_use]
88 pub fn available_execution_curve_impl<'a, HPTD, ASEC>(
89 constrained_server_execution_curve: ASEC,
90 higher_priority_task_demand: HPTD,
91 ) -> impl CurveIterator<CurveKind = AvailableTaskExecution> + Clone + 'a
92 where
93 HPTD: CurveIterator<CurveKind = HigherPriorityTaskDemand> + Clone + 'a,
94 ASEC: CurveIterator<CurveKind = ActualServerExecution> + Clone + 'a,
95 {
96 let delta = CurveDeltaIterator::new(
97 constrained_server_execution_curve,
98 higher_priority_task_demand,
99 );
100
101 delta
102 .remaining_supply()
103 .reclassify::<AvailableTaskExecution>()
104 }
105
106 #[must_use]
111 pub fn original_actual_execution_curve_iter<'a>(
112 system: &'a System,
113 server_index: usize,
114 task_index: usize,
115 ) -> impl CurveIterator<CurveKind = ActualTaskExecution> + Clone + 'a {
116 let asec = system.original_actual_execution_curve_iter(server_index);
117 let hptd = Task::higher_priority_task_demand_iter(
118 system.as_servers()[server_index].as_tasks(),
119 task_index,
120 );
121
122 let available_execution_curve = Task::available_execution_curve_impl(asec, hptd);
123
124 let task_demand_curve =
125 system.as_servers()[server_index].as_tasks()[task_index].into_iter();
126
127 CurveDeltaIterator::new(available_execution_curve, task_demand_curve)
128 .overlap::<ActualTaskExecution>()
129 }
130
131 #[must_use]
132 pub fn fixed_actual_execution_curve_iter<'a>(
133 system: &'a System,
134 server_index: usize,
135 task_index: usize,
136 ) -> impl CurveIterator<CurveKind = ActualTaskExecution> + Clone + 'a {
137 let asec = system.fixed_actual_execution_curve_iter(server_index);
138 let hptd = Task::higher_priority_task_demand_iter(
139 system.as_servers()[server_index].as_tasks(),
140 task_index,
141 );
142
143 let available_execution_curve = Task::available_execution_curve_impl(asec, hptd);
144
145 let task_demand_curve =
146 system.as_servers()[server_index].as_tasks()[task_index].into_iter();
147
148 CurveDeltaIterator::new(available_execution_curve, task_demand_curve)
149 .overlap::<ActualTaskExecution>()
150 }
151
152 #[must_use]
164 pub fn original_worst_case_response_time(
165 system: &System,
166 server_index: usize,
167 task_index: usize,
168 arrival_before: TimeUnit,
169 ) -> TimeUnit {
170 let swh = arrival_before;
171
172 let actual_execution_time_iter =
173 Task::original_actual_execution_curve_iter(system, server_index, task_index);
174
175 let task = &system.as_servers()[server_index].as_tasks()[task_index];
176
177 let last_job = (swh - task.offset - TimeUnit::ONE) / task.interval;
179
180 let total_execution = (last_job + 1) * task.demand;
181 let mut provided = WindowEnd::Finite(TimeUnit::ZERO);
182
183 let actual_execution_time: Curve<_> = actual_execution_time_iter
184 .take_while_curve(|task| {
185 let take = provided < total_execution;
186 provided += task.length();
187 take
188 })
189 .collect_curve();
190
191 assert!(
193 task.job_arrival(last_job) < swh,
194 "Last job should arrive before the system wide hyper period"
195 );
196
197 assert!(
199 swh <= task.job_arrival(last_job + 1),
200 "The job after the last job would arrive after or at the system wide hyper period"
201 );
202
203 assert!(
204 WindowEnd::Finite((last_job + 1) * task.demand) <= actual_execution_time.capacity(),
205 "There should be enough capacity for the last job"
206 );
207
208 (0..=last_job)
209 .into_iter()
210 .map(|job| {
211 let arrival = task.job_arrival(job);
212 let t = (job + 1) * task.demand;
213
214 Task::time_to_provide(&actual_execution_time, t) - arrival
215 })
216 .max()
217 .unwrap_or(TimeUnit::ZERO)
218 }
219
220 #[must_use]
221 pub fn fixed_worst_case_response_time(
222 system: &System,
223 server_index: usize,
224 task_index: usize,
225 arrival_before: TimeUnit,
226 ) -> TimeUnit {
227 let swh = arrival_before;
228
229 let actual_execution_time_iter =
230 Task::fixed_actual_execution_curve_iter(system, server_index, task_index);
231
232 let task = &system.as_servers()[server_index].as_tasks()[task_index];
233
234 let last_job = (swh - task.offset - TimeUnit::ONE) / task.interval;
236
237 let total_execution = (last_job + 1) * task.demand;
238 let mut provided = WindowEnd::Finite(TimeUnit::ZERO);
239
240 let actual_execution_time: Curve<_> = actual_execution_time_iter
241 .take_while_curve(|task| {
242 let take = provided < total_execution;
243 provided += task.length();
244 take
245 })
246 .collect_curve();
247
248 assert!(
250 task.job_arrival(last_job) < swh,
251 "Last job should arrive before the system wide hyper period"
252 );
253
254 assert!(
256 swh <= task.job_arrival(last_job + 1),
257 "The job after the last job would arrive after or at the system wide hyper period"
258 );
259
260 assert!(
261 WindowEnd::Finite((last_job + 1) * task.demand) <= actual_execution_time.capacity(),
262 "There should be enough capacity for the last job"
263 );
264
265 (0..=last_job)
266 .into_iter()
267 .map(|job| {
268 let arrival = task.job_arrival(job);
269 let t = (job + 1) * task.demand;
270
271 Task::time_to_provide(&actual_execution_time, t) - arrival
272 })
273 .max()
274 .unwrap_or(TimeUnit::ZERO)
275 }
276
277 #[must_use]
284 pub fn time_to_provide(
285 actual_execution_time: &Curve<ActualTaskExecution>,
286 t: TimeUnit,
287 ) -> TimeUnit {
288 let (index, sum) = actual_execution_time
297 .as_windows()
298 .iter()
299 .enumerate()
300 .scan(TimeUnit::ZERO, |acc, (index, window)| {
301 match window.length() {
302 WindowEnd::Finite(length) => {
303 *acc += length;
304 (*acc < t).then(|| (index + 1, *acc))
305 }
306 WindowEnd::Infinite => None,
307 }
308 })
309 .last()
310 .unwrap_or((0, TimeUnit::ZERO));
311
312 let b = t - sum;
314
315 debug_assert!(
317 b > TimeUnit::ZERO,
318 "There should be remaining demand, but b = {:?}",
319 b
320 );
321
322 actual_execution_time.as_windows()[index].start + b
323 }
324
325 #[must_use]
329 pub fn job_arrival(&self, job_index: UnitNumber) -> TimeUnit {
330 self.offset + job_index * self.interval
331 }
332}
333
334impl IntoIterator for Task {
335 type Item = Window<Demand>;
336 type IntoIter = TaskDemandIterator;
337
338 fn into_iter(self) -> Self::IntoIter {
342 TaskDemandIterator::new(self)
343 }
344}