Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified .vscode/rustfmt.sh
100644 → 100755
Empty file.
48 changes: 48 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ license-file = "LICENSE.md"
libc = "0.2.177"
clap = { version = "4.5.49", features = ["derive"] }
signal-hook = "0.3.18"
score_log = { git = "https://github.com/eclipse-score/baselibs_rust.git", tag = "v0.0.3" }
score_testing_macros = { git = "https://github.com/eclipse-score/baselibs_rust.git", tag = "v0.0.3" }
stdout_logger = { git = "https://github.com/eclipse-score/baselibs_rust.git", tag = "v0.0.3" }

[workspace.lints.clippy]
std_instead_of_core = "warn"
Expand Down
2 changes: 2 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,5 @@ use_repo(python)
use_repo(toolchains_qnx, "toolchains_qnx_sdp")
use_repo(toolchains_qnx, "toolchains_qnx_qcc")
use_repo(toolchains_qnx, "toolchains_qnx_ifs")

bazel_dep(name = "score_baselibs_rust", version = "0.0.3")
209 changes: 106 additions & 103 deletions MODULE.bazel.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
+class DeadlineMonitor {
+get_deadline(&self, tag: Tag) -> Result<Deadline, Error>
+get_deadline_guard(&self, tag: Tag) -> Result<DeadlineGuard, Error>
+create_custom_deadline(&self, duration_range: DurationRange) -> Result<Deadline, Error>
+create_custom_deadline_guard(&self, duration_range: DurationRange) -> Result<DeadlineGuard, Error>
+create_custom_deadline(&self, duration_range: TimeRange) -> Result<Deadline, Error>
+create_custom_deadline_guard(&self, duration_range: TimeRange) -> Result<DeadlineGuard, Error>
+enable(&self) -> Result<(), Error>
+disable(&self) -> Result<(), Error>
~status(&self) -> Status
Expand All @@ -28,7 +28,7 @@ DeadlineMonitor ..> Error
DeadlineMonitor ..> Tag
DeadlineMonitor ..> Deadline : <<creates>>
DeadlineMonitor ..> DeadlineGuard : <<creates>>
DeadlineMonitor ..> DurationRange
DeadlineMonitor ..> TimeRange
!endsub

@enduml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
!startsub DeadlineMonitorBuilderClass
+class DeadlineMonitorBuilder {
+new() -> Self
+add_deadline(&mut self, tag: Tag, duration_range: DurationRange) -> &mut Self
+add_deadline(&mut self, tag: Tag, duration_range: TimeRange) -> &mut Self
+build(self) -> Result<DeadlineMonitor, Error>
}
!endsub
Expand All @@ -17,7 +17,7 @@
DeadlineMonitorBuilder ..> Error
DeadlineMonitorBuilder ..> Tag
DeadlineMonitorBuilder ..> DeadlineMonitor : <<consumes>>
DeadlineMonitorBuilder ..> DurationRange
DeadlineMonitorBuilder ..> TimeRange
!endsub

@enduml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

!startsub HeartbeatMonitorClass
+class HeartbeatMonitor {
+new(duration_range: DurationRange) -> Self
+new(duration_range: TimeRange) -> Self
+report_heartbeat(&self) -> Result<(), Error>
+enable(&self) -> Result<(), Error>
+disable(&self) -> Result<(), Error>
Expand All @@ -17,7 +17,7 @@
!startsub HeartbeatMonitorRel
HeartbeatMonitor *-- Status : owns
HeartbeatMonitor ..> Error
HeartbeatMonitor ..> DurationRange
HeartbeatMonitor ..> TimeRange
!endsub

@enduml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@startuml

+struct DurationRange
+struct TimeRange
{
min: Duration
max: Duration
Expand Down
11 changes: 10 additions & 1 deletion src/health_monitoring_lib/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,25 @@
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_test")
load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")

rust_library(
name = "health_monitoring_lib",
srcs = glob(["src/**/*.rs"]),
crate_name = "health_monitoring_lib",
proc_macro_deps = [
"@score_baselibs_rust//src/testing_macros:score_testing_macros",
],
visibility = ["//visibility:public"],
deps = [
"@score_baselibs_rust//src/log/score_log",
],
)

rust_test(
name = "tests",
crate = ":health_monitoring_lib",
deps = [
"@score_baselibs_rust//src/log/stdout_logger",
],
)
7 changes: 7 additions & 0 deletions src/health_monitoring_lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,10 @@ license-file.workspace = true

[lints]
workspace = true

[dependencies]
score_log.workspace = true
score_testing_macros.workspace = true

[dev-dependencies]
stdout_logger.workspace = true
44 changes: 44 additions & 0 deletions src/health_monitoring_lib/src/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// *******************************************************************************
// Copyright (c) 2026 Contributors to the Eclipse Foundation
//
// See the NOTICE file(s) distributed with this work for additional
// information regarding copyright ownership.
//
// This program and the accompanying materials are made available under the
// terms of the Apache License Version 2.0 which is available at
// <https://www.apache.org/licenses/LICENSE-2.0>
//
// SPDX-License-Identifier: Apache-2.0
// *******************************************************************************

use crate::log::*;
use core::time::Duration;

/// Unique identifier for deadlines.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, ScoreDebug)]
pub struct IdentTag(&'static str); // Internal representation as a leaked string slice for now. It can be also an str to u64 conversion. Since this is internal only, we can change it later if needed.

impl From<String> for IdentTag {
fn from(value: String) -> Self {
Self(value.leak())
}
}

impl From<&str> for IdentTag {
fn from(value: &str) -> Self {
Self(value.to_string().leak())
}
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct TimeRange {
pub min: Duration,
pub max: Duration,
}

impl TimeRange {
pub fn new(min: Duration, max: Duration) -> Self {
assert!(min <= max, "TimeRange min must be less than or equal to max");
Self { min, max }
}
}
125 changes: 125 additions & 0 deletions src/health_monitoring_lib/src/deadline/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// *******************************************************************************
// Copyright (c) 2026 Contributors to the Eclipse Foundation
//
// See the NOTICE file(s) distributed with this work for additional
// information regarding copyright ownership.
//
// This program and the accompanying materials are made available under the
// terms of the Apache License Version 2.0 which is available at
// <https://www.apache.org/licenses/LICENSE-2.0>
//
// SPDX-License-Identifier: Apache-2.0
// *******************************************************************************
use core::{
ops::Deref,
sync::atomic::{AtomicBool, Ordering},
};

use crate::TimeRange;

/// Index type for identifying states associated with deadlines.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(super) struct StateIndex(usize);

impl StateIndex {
pub fn new(index: usize) -> Self {
Self(index)
}
}

impl Deref for StateIndex {
type Target = usize;

fn deref(&self) -> &Self::Target {
&self.0
}
}

/// Template for a deadline, managing its range and usage state.
pub(super) struct DeadlineTemplate {
range: TimeRange,
is_in_use: AtomicBool,
pub assigned_state_index: StateIndex,
}

impl DeadlineTemplate {
pub(super) fn new(range: TimeRange, state_index: StateIndex) -> Self {
Self {
range,
is_in_use: AtomicBool::new(false),
assigned_state_index: state_index,
}
}

/// Attempts to acquire the deadline for use.
/// Returns Some(TimeRange) if successful, None if already in use.
pub(super) fn acquire_deadline(&self) -> Option<TimeRange> {
if self
.is_in_use
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
Some(self.range)
} else {
None
}
}

/// Releases the deadline, marking it as not in use.
pub(super) fn release_deadline(&self) {
self.is_in_use.store(false, Ordering::Relaxed);
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;

use crate::TimeRange;
use core::time::Duration;

#[test]
fn new_and_fields() {
let range = TimeRange::new(Duration::from_secs(1), Duration::from_secs(2));
let idx = StateIndex::new(7);
let tmpl = DeadlineTemplate::new(range, idx);
assert_eq!(tmpl.range.min, Duration::from_secs(1));
assert_eq!(tmpl.range.max, Duration::from_secs(2));
assert_eq!(*tmpl.assigned_state_index, 7);
assert!(!tmpl.is_in_use.load(Ordering::Relaxed));
}

#[test]
fn acquire_and_release_deadline() {
let range = TimeRange::new(Duration::from_secs(3), Duration::from_secs(4));
let idx = StateIndex::new(0);
let tmpl = Arc::new(DeadlineTemplate::new(range, idx));

// First acquire should succeed
assert_eq!(tmpl.acquire_deadline(), Some(range));
// Second acquire should fail
assert_eq!(tmpl.acquire_deadline(), None);

// Release and acquire again
tmpl.release_deadline();
assert_eq!(tmpl.acquire_deadline(), Some(range));
}

#[test]
fn concurrent_acquire() {
use std::thread;
let range = TimeRange::new(Duration::from_secs(5), Duration::from_secs(6));
let idx = StateIndex::new(1);
let tmpl = Arc::new(DeadlineTemplate::new(range, idx));

let tmpl1 = tmpl.clone();
let tmpl2 = tmpl.clone();
let h1 = thread::spawn(move || tmpl1.acquire_deadline());
let h2 = thread::spawn(move || tmpl2.acquire_deadline());
let r1 = h1.join().unwrap();
let r2 = h2.join().unwrap();
// Only one thread should succeed
assert!(matches!((r1, r2), (Some(_), None) | (None, Some(_))));
}
}
Loading
Loading