Skip to content

Commit 7ef3ed8

Browse files
authored
Merge pull request #81 from qorix-group/arkjedrz_remove-monitor-abstraction
hmon: approach to monitors rework
2 parents 4119793 + 0eca8fb commit 7ef3ed8

7 files changed

Lines changed: 451 additions & 293 deletions

File tree

examples/rust_supervised_app/src/main.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,14 @@ fn main_logic(args: &Args, stop: Arc<AtomicBool>) -> Result<(), Box<dyn std::err
6868
.add_deadline_monitor(MonitorTag::from("mon1"), builder)
6969
.with_supervisor_api_cycle(std::time::Duration::from_millis(50))
7070
.with_internal_processing_cycle(std::time::Duration::from_millis(50))
71-
.build();
71+
.build()
72+
.expect("Failed to build health monitor");
7273

7374
let mon = hm
7475
.get_deadline_monitor(MonitorTag::from("mon1"))
7576
.expect("Failed to get monitor");
7677

77-
hm.start();
78+
hm.start().expect("Failed to start health monitor");
7879

7980
if !lifecycle_client_rs::report_execution_state_running() {
8081
error!("Rust app FAILED to report execution state!");

src/health_monitoring_lib/rust/common.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
// SPDX-License-Identifier: Apache-2.0
1212
// *******************************************************************************
1313

14+
use crate::deadline::DeadlineEvaluationError;
15+
use crate::log::ScoreDebug;
1416
use crate::tag::MonitorTag;
1517
use core::hash::Hash;
1618
use core::time::Duration;
@@ -30,11 +32,30 @@ impl TimeRange {
3032
}
3133
}
3234

35+
/// A monitor with an evaluation handle available.
36+
pub(crate) trait Monitor {
37+
/// Get an evaluation handle for this monitor.
38+
///
39+
/// # NOTE
40+
///
41+
/// This evaluation handle is intended to be called from a background thread periodically.
42+
fn get_eval_handle(&self) -> MonitorEvalHandle;
43+
}
44+
3345
/// Errors that can occur during monitor evaluation.
34-
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, crate::log::ScoreDebug)]
46+
/// Contains failing monitor type.
47+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, ScoreDebug)]
48+
#[allow(dead_code)]
3549
pub(crate) enum MonitorEvaluationError {
36-
TooEarly,
37-
TooLate,
50+
Deadline(DeadlineEvaluationError),
51+
Heartbeat,
52+
Logic,
53+
}
54+
55+
impl From<DeadlineEvaluationError> for MonitorEvaluationError {
56+
fn from(value: DeadlineEvaluationError) -> Self {
57+
MonitorEvaluationError::Deadline(value)
58+
}
3859
}
3960

4061
/// Trait for evaluating monitors and reporting errors to be used by HealthMonitor.

src/health_monitoring_lib/rust/deadline/deadline_monitor.rs

Lines changed: 97 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,26 @@
1010
//
1111
// SPDX-License-Identifier: Apache-2.0
1212
// *******************************************************************************
13-
use super::common::DeadlineTemplate;
14-
use crate::common::{MonitorEvalHandle, MonitorEvaluationError, MonitorEvaluator, TimeRange};
13+
use crate::common::{Monitor, MonitorEvalHandle, MonitorEvaluationError, MonitorEvaluator};
14+
use crate::deadline::common::{DeadlineTemplate, StateIndex};
15+
use crate::deadline::deadline_state::{DeadlineState, DeadlineStateSnapshot};
16+
use crate::log::{error, warn, ScoreDebug};
17+
use crate::protected_memory::ProtectedMemoryAllocator;
1518
use crate::tag::{DeadlineTag, MonitorTag};
16-
use crate::{
17-
deadline::{
18-
common::StateIndex,
19-
deadline_state::{DeadlineState, DeadlineStateSnapshot},
20-
},
21-
protected_memory::ProtectedMemoryAllocator,
22-
};
19+
use crate::TimeRange;
2320
use core::hash::Hash;
24-
use std::{collections::HashMap, sync::Arc, time::Instant};
25-
26-
use crate::log::*;
21+
use std::collections::HashMap;
22+
use std::sync::Arc;
23+
use std::time::Instant;
24+
25+
/// Deadline evaluation errors.
26+
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, ScoreDebug)]
27+
pub(crate) enum DeadlineEvaluationError {
28+
/// Finished too early.
29+
TooEarly,
30+
/// Finished too late.
31+
TooLate,
32+
}
2733

2834
///
2935
/// Errors that can occur when working with DeadlineMonitor
@@ -65,7 +71,8 @@ impl DeadlineMonitorBuilder {
6571

6672
/// Builds the DeadlineMonitor with the configured deadlines.
6773
pub(crate) fn build(self, monitor_tag: MonitorTag, _allocator: &ProtectedMemoryAllocator) -> DeadlineMonitor {
68-
DeadlineMonitor::new(monitor_tag, self.deadlines)
74+
let inner = Arc::new(DeadlineMonitorInner::new(monitor_tag, self.deadlines));
75+
DeadlineMonitor::new(inner)
6976
}
7077

7178
// Used by FFI and config parsing code which prefer not to move builder instance
@@ -80,27 +87,9 @@ pub struct DeadlineMonitor {
8087
}
8188

8289
impl DeadlineMonitor {
83-
fn new(monitor_tag: MonitorTag, deadlines: HashMap<DeadlineTag, TimeRange>) -> Self {
84-
let mut active_deadlines = vec![];
85-
86-
let deadlines = deadlines
87-
.into_iter()
88-
.enumerate()
89-
.map(|(index, (deadline_tag, range))| {
90-
active_deadlines.push((deadline_tag, DeadlineState::new()));
91-
(deadline_tag, DeadlineTemplate::new(range, StateIndex::new(index)))
92-
})
93-
.collect();
94-
95-
Self {
96-
#[allow(clippy::arc_with_non_send_sync)] // This will be fixed once we add background thread
97-
inner: Arc::new(DeadlineMonitorInner {
98-
monitor_tag,
99-
deadlines,
100-
active_deadlines: active_deadlines.into(),
101-
monitor_starting_point: Instant::now(),
102-
}),
103-
}
90+
/// Create a new [`DeadlineMonitor`] instance.
91+
fn new(inner: Arc<DeadlineMonitorInner>) -> Self {
92+
Self { inner }
10493
}
10594

10695
/// Acquires a deadline instance for the given tag.
@@ -109,26 +98,12 @@ impl DeadlineMonitor {
10998
/// - Err(DeadlineMonitorError::DeadlineInUse) - if the deadline is already in use
11099
/// - Err(DeadlineMonitorError::DeadlineNotFound) - if the deadline tag is not registered
111100
pub fn get_deadline(&self, deadline_tag: DeadlineTag) -> Result<Deadline, DeadlineMonitorError> {
112-
if let Some(template) = self.inner.deadlines.get(&deadline_tag) {
113-
match template.acquire_deadline() {
114-
Some(range) => Ok(Deadline {
115-
range,
116-
deadline_tag,
117-
monitor: Arc::clone(&self.inner),
118-
state_index: template.assigned_state_index,
119-
}),
120-
None => Err(DeadlineMonitorError::DeadlineInUse),
121-
}
122-
} else {
123-
Err(DeadlineMonitorError::DeadlineNotFound)
124-
}
101+
self.inner.get_deadline(deadline_tag)
125102
}
103+
}
126104

127-
/// Handle for evaluation of all active deadlines and reporting any missed deadlines or underruns.
128-
///
129-
/// # NOTE
130-
/// This function is intended to be called from a background thread periodically.
131-
pub(crate) fn get_eval_handle(&self) -> MonitorEvalHandle {
105+
impl Monitor for DeadlineMonitor {
106+
fn get_eval_handle(&self) -> MonitorEvalHandle {
132107
MonitorEvalHandle::new(Arc::clone(&self.inner))
133108
}
134109
}
@@ -220,7 +195,7 @@ impl Deadline {
220195

221196
let expected = current.timestamp_ms();
222197
if expected < now {
223-
possible_err = (Some(MonitorEvaluationError::TooLate), now - expected);
198+
possible_err = (Some(DeadlineEvaluationError::TooLate), now - expected);
224199
return None; // Deadline missed, let state as is for BG thread to report
225200
}
226201

@@ -231,18 +206,18 @@ impl Deadline {
231206
// Finished too early, leave it for reporting by BG thread
232207

233208
current.set_underrun();
234-
possible_err = (Some(MonitorEvaluationError::TooEarly), earliest_time - now);
209+
possible_err = (Some(DeadlineEvaluationError::TooEarly), earliest_time - now);
235210
return Some(current);
236211
}
237212

238213
Some(DeadlineStateSnapshot::default()) // Reset to stopped state as all fine
239214
});
240215

241216
match possible_err {
242-
(Some(MonitorEvaluationError::TooEarly), val) => {
217+
(Some(DeadlineEvaluationError::TooEarly), val) => {
243218
error!("Deadline {:?} stopped too early by {} ms", self.deadline_tag, val);
244219
},
245-
(Some(MonitorEvaluationError::TooLate), val) => {
220+
(Some(DeadlineEvaluationError::TooLate), val) => {
246221
error!("Deadline {:?} stopped too late by {} ms", self.deadline_tag, val);
247222
},
248223
(None, _) => {},
@@ -286,35 +261,14 @@ struct DeadlineMonitorInner {
286261

287262
impl MonitorEvaluator for DeadlineMonitorInner {
288263
fn evaluate(&self, on_error: &mut dyn FnMut(&MonitorTag, MonitorEvaluationError)) {
289-
self.evaluate(on_error);
290-
}
291-
}
292-
293-
impl DeadlineMonitorInner {
294-
fn release_deadline(&self, deadline_tag: DeadlineTag) {
295-
if let Some(template) = self.deadlines.get(&deadline_tag) {
296-
template.release_deadline();
297-
} else {
298-
unreachable!("Releasing unknown deadline tag: {:?}", deadline_tag);
299-
}
300-
}
301-
302-
fn now(&self) -> u32 {
303-
let duration = self.monitor_starting_point.elapsed();
304-
// As u32 can hold up to ~49 days in milliseconds, this should be sufficient for our use case
305-
// We still have a room up to 60bits timestamp if needed in future
306-
u32::try_from(duration.as_millis()).expect("Monitor running for too long")
307-
}
308-
309-
fn evaluate(&self, mut on_failed: impl FnMut(&MonitorTag, MonitorEvaluationError)) {
310264
for (deadline_tag, deadline) in self.active_deadlines.iter() {
311265
let snapshot = deadline.snapshot();
312266
if snapshot.is_underrun() {
313267
// Deadline finished too early, report
314268
warn!("Deadline ({:?}) finished too early!", deadline_tag);
315269

316270
// Here we would normally report the underrun to the monitoring system
317-
on_failed(&self.monitor_tag, MonitorEvaluationError::TooEarly);
271+
on_error(&self.monitor_tag, DeadlineEvaluationError::TooEarly.into());
318272
} else if snapshot.is_running() {
319273
debug_assert!(
320274
snapshot.is_stopped(),
@@ -331,13 +285,66 @@ impl DeadlineMonitorInner {
331285
);
332286

333287
// Here we would normally report the missed deadline to the monitoring system
334-
on_failed(&self.monitor_tag, MonitorEvaluationError::TooLate);
288+
on_error(&self.monitor_tag, DeadlineEvaluationError::TooLate.into());
335289
}
336290
}
337291
}
338292
}
339293
}
340294

295+
impl DeadlineMonitorInner {
296+
fn new(monitor_tag: MonitorTag, deadlines: HashMap<DeadlineTag, TimeRange>) -> Self {
297+
let mut active_deadlines = vec![];
298+
299+
let deadlines = deadlines
300+
.into_iter()
301+
.enumerate()
302+
.map(|(index, (deadline_tag, range))| {
303+
active_deadlines.push((deadline_tag, DeadlineState::new()));
304+
(deadline_tag, DeadlineTemplate::new(range, StateIndex::new(index)))
305+
})
306+
.collect();
307+
308+
Self {
309+
monitor_tag,
310+
deadlines,
311+
active_deadlines: active_deadlines.into(),
312+
monitor_starting_point: Instant::now(),
313+
}
314+
}
315+
316+
fn release_deadline(&self, deadline_tag: DeadlineTag) {
317+
if let Some(template) = self.deadlines.get(&deadline_tag) {
318+
template.release_deadline();
319+
} else {
320+
unreachable!("Releasing unknown deadline tag: {:?}", deadline_tag);
321+
}
322+
}
323+
324+
pub(crate) fn get_deadline(self: &Arc<Self>, deadline_tag: DeadlineTag) -> Result<Deadline, DeadlineMonitorError> {
325+
if let Some(template) = self.deadlines.get(&deadline_tag) {
326+
match template.acquire_deadline() {
327+
Some(range) => Ok(Deadline {
328+
range,
329+
deadline_tag,
330+
monitor: self.clone(),
331+
state_index: template.assigned_state_index,
332+
}),
333+
None => Err(DeadlineMonitorError::DeadlineInUse),
334+
}
335+
} else {
336+
Err(DeadlineMonitorError::DeadlineNotFound)
337+
}
338+
}
339+
340+
fn now(&self) -> u32 {
341+
let duration = self.monitor_starting_point.elapsed();
342+
// As u32 can hold up to ~49 days in milliseconds, this should be sufficient for our use case
343+
// We still have a room up to 60bits timestamp if needed in future
344+
u32::try_from(duration.as_millis()).expect("Monitor running for too long")
345+
}
346+
}
347+
341348
#[score_testing_macros::test_mod_with_log]
342349
#[cfg(test)]
343350
mod tests {
@@ -410,7 +417,7 @@ mod tests {
410417

411418
drop(handle); // stop the deadline
412419

413-
monitor.inner.evaluate(|monitor_tag, deadline_failure| {
420+
monitor.inner.evaluate(&mut |monitor_tag, deadline_failure| {
414421
panic!(
415422
"Deadline {:?} should not have failed or underrun({:?})",
416423
monitor_tag, deadline_failure
@@ -426,10 +433,10 @@ mod tests {
426433

427434
drop(handle); // stop the deadline
428435

429-
monitor.inner.evaluate(|monitor_tag, deadline_failure| {
436+
monitor.inner.evaluate(&mut |monitor_tag, deadline_failure| {
430437
assert_eq!(
431438
deadline_failure,
432-
MonitorEvaluationError::TooEarly,
439+
DeadlineEvaluationError::TooEarly.into(),
433440
"Deadline {:?} should not have failed({:?})",
434441
monitor_tag,
435442
deadline_failure
@@ -444,10 +451,10 @@ mod tests {
444451

445452
// So deadline stop happens after evaluate, still it should be reported as failed
446453

447-
monitor.inner.evaluate(|monitor_tag, deadline_failure| {
454+
monitor.inner.evaluate(&mut |monitor_tag, deadline_failure| {
448455
assert_eq!(
449456
deadline_failure,
450-
MonitorEvaluationError::TooEarly,
457+
DeadlineEvaluationError::TooEarly.into(),
451458
"Deadline {:?} should not have failed({:?})",
452459
monitor_tag,
453460
deadline_failure
@@ -470,10 +477,10 @@ mod tests {
470477
let handle = deadline.start();
471478
assert_eq!(handle.err(), Some(DeadlineError::DeadlineAlreadyFailed));
472479

473-
monitor.inner.evaluate(|monitor_tag, deadline_failure| {
480+
monitor.inner.evaluate(&mut |monitor_tag, deadline_failure| {
474481
assert_eq!(
475482
deadline_failure,
476-
MonitorEvaluationError::TooEarly,
483+
DeadlineEvaluationError::TooEarly.into(),
477484
"Deadline {:?} should not have failed ({:?})",
478485
monitor_tag,
479486
deadline_failure
@@ -489,10 +496,10 @@ mod tests {
489496

490497
drop(handle); // stop the deadline
491498

492-
monitor.inner.evaluate(|monitor_tag, deadline_failure| {
499+
monitor.inner.evaluate(&mut |monitor_tag, deadline_failure| {
493500
assert_eq!(
494501
deadline_failure,
495-
MonitorEvaluationError::TooLate,
502+
DeadlineEvaluationError::TooLate.into(),
496503
"Deadline {:?} should not have failed({:?})",
497504
monitor_tag,
498505
deadline_failure
@@ -517,11 +524,11 @@ mod tests {
517524

518525
let mut cnt = 0;
519526

520-
monitor.inner.evaluate(|monitor_tag, deadline_failure| {
527+
monitor.inner.evaluate(&mut |monitor_tag, deadline_failure| {
521528
cnt += 1;
522529
assert_eq!(
523530
deadline_failure,
524-
MonitorEvaluationError::TooLate,
531+
DeadlineEvaluationError::TooLate.into(),
525532
"Deadline {:?} should not have failed({:?})",
526533
monitor_tag,
527534
deadline_failure

src/health_monitoring_lib/rust/deadline/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ mod common;
1515
mod deadline_monitor;
1616
mod deadline_state;
1717

18+
pub(crate) use deadline_monitor::DeadlineEvaluationError;
1819
pub use deadline_monitor::{
1920
DeadlineError, DeadlineHandle, DeadlineMonitor, DeadlineMonitorBuilder, DeadlineMonitorError,
2021
};

0 commit comments

Comments
 (0)