1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/*! The *request-bound function* (RBF) abstraction and related definitions

This module provides a trait to express request-bound functions (RBFs),
which are an abstraction to characterize the total demand for processor
service by one or more tasks.

*/

use auto_impl::auto_impl;

use crate::time::{Duration, Offset, Service};

/// The general interface for (arbitrarily shaped) processor demand.
/// This can represent the demand of a single task, or the cumulative
/// demand of multiple tasks.
#[auto_impl(&, Box, Rc)]
pub trait RequestBound {
    /// Bound the total amount of service needed in an interval of length `delta`.
    fn service_needed(&self, delta: Duration) -> Service {
        self.job_cost_iter(delta).sum()
    }

    /// Bound the total amount of service needed by up to `max_jobs`
    /// in an interval of length `delta`.
    fn service_needed_by_n_jobs(&self, delta: Duration, max_jobs: usize) -> Service {
        // take the max_jobs largest job costs
        itertools::sorted(self.job_cost_iter(delta))
            .rev()
            .take(max_jobs)
            .sum()
    }

    /// Expose the smallest WCET of any job encountered in an interval of length `delta`.
    fn least_wcet_in_interval(&self, delta: Duration) -> Service;

    /// Yield an iterator over the interval lengths (i.e., values of `delta` in
    /// [RequestBound::service_needed]) at which the cumulative demand changes.
    ///
    /// More precisely, this iterator yields every value of `delta` such that:
    ///
    /// `self.service_needed(delta - 1) < self.service_needed(delta)`.
    fn steps_iter<'a>(&'a self) -> Box<dyn Iterator<Item = Duration> + 'a>;

    /// Expose an iterator over the individual costs that comprise the overall demand.
    fn job_cost_iter<'a>(&'a self, delta: Duration) -> Box<dyn Iterator<Item = Service> + 'a>;
}

/// A refined interface for processor demand that represents the
/// total demand of a collection of individual sources of demand.
#[auto_impl(&, Box, Rc)]
pub trait AggregateRequestBound: RequestBound {
    /// Bound the total amount of service needed in an interval of
    /// length `delta`, subject to the constraint that no individual
    /// source of demand contributes more than `max_jobs` worth of
    /// demand.
    fn service_needed_by_n_jobs_per_component(&self, delta: Duration, max_jobs: usize) -> Service;
}

/// Yield the sequence of *offsets* at which the request bound
/// function "steps", i.e., where the procesor demand increases.
///
/// More precisely, the iterator yields values of `A` such that
///
/// `rb.service_needed(A.since_time_zero()) < rb.service_needed(A.closed_since_time_zero())`
///
/// which is equivalent to
///
/// `rb.service_needed(Duration::from(A)) < rb.service_needed(Duration::from(A) + Duration::epsilon())`.
///
/// It always yields `A=0` since [RequestBound::steps_iter]
/// necessarily yields `delta=1`, which results in `A=0` being the
/// first element yielded by `step_offsets`.
pub fn step_offsets(rb: &'_ (impl RequestBound + ?Sized)) -> impl Iterator<Item = Offset> + '_ {
    rb.steps_iter().map(Offset::closed_from_time_zero)
}

mod aggregate;
mod rbf;
mod slice;

pub use aggregate::Aggregate;
pub use rbf::RBF;
pub use slice::Slice;