response_time_analysis/ros2/
ecrts19.rs

1use crate::demand::RequestBound;
2use crate::fixed_point::{self, max_response_time, SearchResult};
3use crate::supply::SupplyBound;
4use crate::time::{Duration, Offset, Service};
5
6/// Bound the response time of an event source for a given supply and
7/// demand model, using the analysis proposed by
8/// [Casini et al. (2019)](https://people.mpi-sws.org/~bbb/papers/pdf/ecrts19-rev1.pdf).
9///
10/// If no fixed point is found below the divergence limit given by
11/// `limit`, return a [SearchFailure][fixed_point::SearchFailure]
12/// instead.
13///
14/// The bound is based on Lemma 1 of Casini et al. (2019).
15pub fn rta_event_source<SBF, RBF>(
16    supply: &SBF,
17    demand: &RBF,
18    limit: Duration,
19) -> fixed_point::SearchResult
20where
21    SBF: SupplyBound + ?Sized,
22    RBF: RequestBound + ?Sized,
23{
24    // right-hand side of Lemma 6 --- used to bound the max. busy-window length
25    let rhs_busy_window = |delta| demand.service_needed(delta);
26    // right-hand side of Lemma 1 --- used to bound the demand for a given offset
27    let rhs = |offset: Offset, _response| demand.service_needed(offset.closed_since_time_zero());
28
29    // solve the fixed point for all steps of the demand curve up to
30    // the maximum busy-window length and return the maximum (or divergence)
31    bound_response_time(supply, demand, rhs_busy_window, rhs, limit)
32}
33
34/// Bound the response time of a timer callback using
35/// Lemma 3 of
36/// [Casini et al. (2019)](https://people.mpi-sws.org/~bbb/papers/pdf/ecrts19-rev1.pdf).
37///
38/// # Parameters
39///
40/// - `supply`: the supply model of the shared executor
41/// - `own_demand`: model of the processor demand of the timer under analysis
42/// - `interfering_demand`: model of the processor demand of higher-priority timers
43/// - `blocking_bound`: a bound on the maximum delay due to lower-priority callbacks
44/// - `limit`: the divergence threshold at which the search for a fixed point is aborted
45///
46/// If no fixed point is found below the divergence limit given by
47/// `limit`, return a [SearchFailure][fixed_point::SearchFailure]
48/// instead.
49pub fn rta_timer<SBF, RBF1, RBF2>(
50    supply: &SBF,
51    own_demand: &RBF1,
52    interfering_demand: &RBF2,
53    blocking_bound: Service,
54    limit: Duration,
55) -> fixed_point::SearchResult
56where
57    SBF: SupplyBound + ?Sized,
58    RBF1: RequestBound + ?Sized,
59    RBF2: RequestBound + ?Sized,
60{
61    // right-hand side of Lemma 6
62    let rhs_bw = |delta| {
63        own_demand.service_needed(delta) + blocking_bound + interfering_demand.service_needed(delta)
64    };
65    // right-hand side of Lemma 3
66    let rhs = |offset: Offset, response: Duration| {
67        // length of the prefix interval before the offset
68        let prefix = offset.since_time_zero();
69        // cost of timer callback
70        let own_wcet = Duration::from(own_demand.least_wcet_in_interval(prefix + response));
71        // determine timeframe during which other callbacks can delay us
72        let interference_interval = if response > own_wcet {
73            prefix + response - own_wcet + Duration::epsilon()
74        } else {
75            prefix + Duration::epsilon()
76        };
77        own_demand.service_needed(prefix + Duration::epsilon())
78            + interfering_demand.service_needed(interference_interval)
79            + blocking_bound
80    };
81    bound_response_time(supply, own_demand, rhs_bw, rhs, limit)
82}
83
84/// Bound the response time of a polling-point-based callback using
85/// Lemmas 4 and 5 of
86/// [Casini et al. (2019)](https://people.mpi-sws.org/~bbb/papers/pdf/ecrts19-rev1.pdf).
87///
88/// # Parameters
89///
90/// - `supply`: the supply model of the shared executor
91/// - `own_demand`: model of the processor demand of the callback under analysis
92/// - `interfering_demand`: model of the processor demand of all higher-priority callbacks
93/// - `limit`: the divergence threshold at which the search for a fixed point is aborted
94///
95/// If no fixed point is found below the divergence limit given by
96/// `limit`, return a [SearchFailure][fixed_point::SearchFailure]
97/// instead.
98pub fn rta_polling_point_callback<SBF, RBF1, RBF2>(
99    supply: &SBF,
100    own_demand: &RBF1,
101    interfering_demand: &RBF2,
102    limit: Duration,
103) -> fixed_point::SearchResult
104where
105    SBF: SupplyBound + ?Sized,
106    RBF1: RequestBound + ?Sized,
107    RBF2: RequestBound + ?Sized,
108{
109    // right-hand side of Lemma 6
110    let rhs_bw =
111        |delta| own_demand.service_needed(delta) + interfering_demand.service_needed(delta);
112    // right-hand side of Eq (6), based on Lemmas 4 and 5
113    let rhs = |offset: Offset, response: Duration| {
114        // length of the prefix interval before the offset
115        let prefix = offset.since_time_zero();
116        // cost of pp-based callback under analysis
117        let own_wcet = Duration::from(own_demand.least_wcet_in_interval(prefix + response));
118        // determine timeframe during which other callbacks can delay us
119        let interference_interval = if response > own_wcet {
120            prefix + response - own_wcet + Duration::epsilon()
121        } else {
122            prefix + Duration::epsilon()
123        };
124        own_demand.service_needed(prefix + Duration::epsilon())
125            + interfering_demand.service_needed(interference_interval)
126    };
127    bound_response_time(supply, own_demand, rhs_bw, rhs, limit)
128}
129
130/// Bound the response time of a chain of callbacks using
131/// Lemma 8 of
132/// [Casini et al. (2019)](https://people.mpi-sws.org/~bbb/papers/pdf/ecrts19-rev1.pdf).
133///
134/// # Parameters
135///
136/// - `supply`: the supply model of the shared executor
137/// - `chain_last_callback`: model of the processor demand of the last
138///   callback of the chain under analysis
139/// - `chain_prefix`: model of the processor demand of the callbacks
140///   in the chain under analysis preceding the last callback
141/// - `full_chain`: model of the processor demand of the chain under analysis --- obviously, this must be consistent with the two prior parameters
142/// - `other_chains`: model of the processor demand of other callback
143///   chains allocated to the same executor
144/// - `limit`: the divergence threshold at which the search for a fixed point is aborted
145///
146/// If no fixed point is found below the divergence limit given by
147/// `limit`, return a [SearchFailure][fixed_point::SearchFailure]
148/// instead.
149pub fn rta_processing_chain<SBF, RBF1, RBF2, RBF3, RBF4>(
150    supply: &SBF,
151    chain_last_callback: &RBF1,
152    chain_prefix: &RBF2,
153    full_chain: &RBF3,
154    other_chains: &RBF4,
155    limit: Duration,
156) -> fixed_point::SearchResult
157where
158    SBF: SupplyBound + ?Sized,
159    RBF1: RequestBound + ?Sized,
160    RBF2: RequestBound + ?Sized,
161    RBF3: RequestBound + ?Sized,
162    RBF4: RequestBound + ?Sized,
163{
164    // for bounding the max. busy-window length
165    let rhs_bw = |delta| {
166        debug_assert_eq!(
167            full_chain.service_needed(delta),
168            chain_prefix.service_needed(delta) + chain_last_callback.service_needed(delta)
169        );
170        full_chain.service_needed(delta) + other_chains.service_needed(delta)
171    };
172
173    // right-hand side of Lemma 8
174    let rhs = |offset: Offset, response: Duration| {
175        let prefix = offset.since_time_zero();
176        let own_demand = chain_last_callback.service_needed(prefix + Duration::epsilon());
177        let own_wcet =
178            Duration::from(chain_last_callback.least_wcet_in_interval(prefix + response));
179        // determine timeframe during which other callbacks can delay us
180        let interference_interval = if response > own_wcet {
181            prefix + response - own_wcet + Duration::epsilon()
182        } else {
183            prefix + Duration::epsilon()
184        };
185        let other_demand = other_chains.service_needed(interference_interval);
186        let self_interference = chain_prefix.service_needed(interference_interval);
187        own_demand + self_interference + other_demand
188    };
189
190    bound_response_time(supply, full_chain, rhs_bw, rhs, limit)
191}
192
193/// Try to find a response-time bound for a given processor supply
194/// model and a given processor demand model.
195///
196/// The search for a fixed point will be aborted if the given
197/// divergence threshold indicated by `limit` is reached.
198///
199/// The fixed-point search relies on three relevant characterizations
200/// of processor demand:
201/// - `demand` is demand model of the callback under analysis from
202///    which all points are inferred at which the demand curve
203///    exhibits "steps".
204/// - `bw_demand_bound` is the right-hand side of the fixed-point
205///   equation describing the maximum busy-window length, i.e., the
206///   demand of "everything".
207/// - `offset_demand_bound` is the right-hand side of the fixed-point
208///   equation describing the response time for a given offset.
209fn bound_response_time<SBF, RBF, F, G>(
210    supply: &SBF,
211    demand: &RBF,
212    bw_demand_bound: F,
213    offset_demand_bound: G,
214    limit: Duration,
215) -> SearchResult
216where
217    SBF: SupplyBound + ?Sized,
218    RBF: RequestBound + ?Sized,
219    F: Fn(Duration) -> Service,
220    G: Fn(Offset, Duration) -> Service,
221{
222    // find a bound on the maximum busy-window
223    let max_bw = fixed_point::search(supply, limit, bw_demand_bound)?;
224    // Consider the search space of relevant offsets based on Lemma 7.
225    // That is, we have to look at all points where the demand curve "steps".
226    let offsets = demand
227        .steps_iter()
228        // Note that steps_iter() yields interval lengths, but we are interested in
229        // offsets. Since the length of an interval [0, A] is A+1, we need to subtract one
230        // to obtain the offset.
231        .map(Offset::closed_from_time_zero)
232        .take_while(|x| *x <= Offset::from_time_zero(max_bw));
233    // for each relevant offset in the search space,
234    let rta_bounds = offsets.map(|offset| {
235        let rhs = |delta| offset_demand_bound(offset, delta);
236        fixed_point::search_with_offset(supply, offset, limit, &rhs)
237    });
238    max_response_time(rta_bounds)
239}