Skip to content

Commit 3da944a

Browse files
committed
hmon: add C++ interface to heartbeat monitor
Add C++ interface to heartbeat monitor.
1 parent fa7108a commit 3da944a

9 files changed

Lines changed: 721 additions & 2 deletions

File tree

src/health_monitoring_lib/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@ PROC_MACRO_DEPS = [
2828
CC_SOURCES = [
2929
"cpp/common.cpp",
3030
"cpp/deadline_monitor.cpp",
31+
"cpp/heartbeat_monitor.cpp",
3132
"cpp/health_monitor.cpp",
3233
]
3334

3435
CC_HDRS = [
3536
"cpp/include/score/hm/common.h",
3637
"cpp/include/score/hm/tag.h",
3738
"cpp/include/score/hm/deadline/deadline_monitor.h",
39+
"cpp/include/score/hm/heartbeat/heartbeat_monitor.h",
3840
"cpp/include/score/hm/health_monitor.h",
3941
]
4042

src/health_monitoring_lib/cpp/health_monitor.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ extern "C" {
1818
using namespace score::hm;
1919
using namespace score::hm::internal;
2020
using namespace score::hm::deadline;
21+
using namespace score::hm::heartbeat;
2122

2223
// Functions below must match functions defined in `crate::ffi`.
2324

@@ -30,9 +31,15 @@ FFICode health_monitor_builder_build(FFIHandle health_monitor_builder_handle,
3031
FFICode health_monitor_builder_add_deadline_monitor(FFIHandle health_monitor_builder_handle,
3132
const MonitorTag* monitor_tag,
3233
FFIHandle deadline_monitor_builder_handle);
34+
FFICode health_monitor_builder_add_heartbeat_monitor(FFIHandle health_monitor_builder_handle,
35+
const MonitorTag* monitor_tag,
36+
FFIHandle heartbeat_monitor_builder_handle);
3337
FFICode health_monitor_get_deadline_monitor(FFIHandle health_monitor_handle,
3438
const MonitorTag* monitor_tag,
3539
FFIHandle* deadline_monitor_handle_out);
40+
FFICode health_monitor_get_heartbeat_monitor(FFIHandle health_monitor_handle,
41+
const MonitorTag* monitor_tag,
42+
FFIHandle* heartbeat_monitor_handle_out);
3643
FFICode health_monitor_start(FFIHandle health_monitor_handle);
3744
FFICode health_monitor_destroy(FFIHandle health_monitor_handle);
3845
}
@@ -71,6 +78,20 @@ HealthMonitorBuilder HealthMonitorBuilder::add_deadline_monitor(const MonitorTag
7178
return std::move(*this);
7279
}
7380

81+
HealthMonitorBuilder HealthMonitorBuilder::add_heartbeat_monitor(const MonitorTag& monitor_tag,
82+
HeartbeatMonitorBuilder&& monitor) &&
83+
{
84+
auto monitor_handle = monitor.drop_by_rust();
85+
SCORE_LANGUAGE_FUTURECPP_PRECONDITION(monitor_handle.has_value());
86+
SCORE_LANGUAGE_FUTURECPP_PRECONDITION(health_monitor_builder_handle_.as_rust_handle().has_value());
87+
88+
auto result{health_monitor_builder_add_heartbeat_monitor(
89+
health_monitor_builder_handle_.as_rust_handle().value(), &monitor_tag, monitor_handle.value())};
90+
SCORE_LANGUAGE_FUTURECPP_ASSERT(result == kSuccess);
91+
92+
return std::move(*this);
93+
}
94+
7495
HealthMonitorBuilder HealthMonitorBuilder::with_internal_processing_cycle(std::chrono::milliseconds cycle_duration) &&
7596
{
7697
internal_processing_cycle_duration_ = cycle_duration;
@@ -122,6 +143,18 @@ score::cpp::expected<DeadlineMonitor, Error> HealthMonitor::get_deadline_monitor
122143
return score::cpp::expected<DeadlineMonitor, Error>(DeadlineMonitor{handle});
123144
}
124145

146+
score::cpp::expected<HeartbeatMonitor, Error> HealthMonitor::get_heartbeat_monitor(const MonitorTag& monitor_tag)
147+
{
148+
FFIHandle handle{nullptr};
149+
auto result{health_monitor_get_heartbeat_monitor(health_monitor_, &monitor_tag, &handle)};
150+
if (result != kSuccess)
151+
{
152+
return score::cpp::unexpected(static_cast<Error>(result));
153+
}
154+
155+
return score::cpp::expected<HeartbeatMonitor, Error>(HeartbeatMonitor{handle});
156+
}
157+
125158
void HealthMonitor::start()
126159
{
127160
auto result{health_monitor_start(health_monitor_)};
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/********************************************************************************
2+
* Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Apache License Version 2.0 which is available at
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* SPDX-License-Identifier: Apache-2.0
12+
********************************************************************************/
13+
#include "score/hm/heartbeat/heartbeat_monitor.h"
14+
15+
namespace {
16+
extern "C" {
17+
using namespace score::hm;
18+
using namespace score::hm::internal;
19+
using namespace score::hm::heartbeat;
20+
21+
FFICode heartbeat_monitor_builder_create(uint32_t range_min_ms, uint32_t range_max_ms, FFIHandle* heartbeat_monitor_builder_handle_out);
22+
FFICode heartbeat_monitor_builder_destroy(FFIHandle heartbeat_monitor_builder_handle);
23+
FFICode heartbeat_monitor_destroy(FFIHandle heartbeat_monitor_builder_handle);
24+
FFICode heartbeat_monitor_heartbeat(FFIHandle heartbeat_monitor_builder_handle);
25+
}
26+
27+
FFIHandle heartbeat_monitor_builder_create_wrapper(uint32_t range_min_ms, uint32_t range_max_ms)
28+
{
29+
FFIHandle handle{nullptr};
30+
auto result{heartbeat_monitor_builder_create(range_min_ms, range_max_ms, &handle)};
31+
SCORE_LANGUAGE_FUTURECPP_ASSERT(result == kSuccess);
32+
return handle;
33+
}
34+
35+
}
36+
37+
namespace score::hm::heartbeat
38+
{
39+
HeartbeatMonitorBuilder::HeartbeatMonitorBuilder(const TimeRange& range)
40+
: monitor_builder_handle_{heartbeat_monitor_builder_create_wrapper(range.min_ms(), range.max_ms()),
41+
&heartbeat_monitor_builder_destroy}
42+
{
43+
}
44+
45+
HeartbeatMonitor::HeartbeatMonitor(FFIHandle monitor_handle)
46+
: monitor_handle_{monitor_handle, &heartbeat_monitor_destroy}
47+
{
48+
}
49+
50+
void HeartbeatMonitor::heartbeat()
51+
{
52+
auto monitor_handle{monitor_handle_.as_rust_handle()};
53+
SCORE_LANGUAGE_FUTURECPP_PRECONDITION(monitor_handle.has_value());
54+
SCORE_LANGUAGE_FUTURECPP_ASSERT(heartbeat_monitor_heartbeat(monitor_handle.value()) == kSuccess);
55+
}
56+
57+
} // namespace score::hm::heartbeat

src/health_monitoring_lib/cpp/include/score/hm/health_monitor.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include <score/hm/common.h>
1717
#include <score/hm/deadline/deadline_monitor.h>
18+
#include <score/hm/heartbeat/heartbeat_monitor.h>
1819
#include <score/hm/tag.h>
1920

2021
namespace score::hm
@@ -42,6 +43,10 @@ class HealthMonitorBuilder final
4243
HealthMonitorBuilder add_deadline_monitor(const MonitorTag& monitor_tag,
4344
deadline::DeadlineMonitorBuilder&& monitor) &&;
4445

46+
/// Adds a heartbeat monitor for a specific identifier tag.
47+
HealthMonitorBuilder add_heartbeat_monitor(const MonitorTag& monitor_tag,
48+
heartbeat::HeartbeatMonitorBuilder&& monitor) &&;
49+
4550
/// Sets the cycle duration for supervisor API notifications.
4651
/// This duration determines how often the health monitor notifies the supervisor that the system is alive.
4752
HealthMonitorBuilder with_supervisor_api_cycle(std::chrono::milliseconds cycle_duration) &&;
@@ -72,6 +77,7 @@ class HealthMonitor final
7277
~HealthMonitor();
7378

7479
score::cpp::expected<deadline::DeadlineMonitor, Error> get_deadline_monitor(const MonitorTag& monitor_tag);
80+
score::cpp::expected<heartbeat::HeartbeatMonitor, Error> get_heartbeat_monitor(const MonitorTag& monitor_tag);
7581

7682
void start();
7783

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/********************************************************************************
2+
* Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Apache License Version 2.0 which is available at
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* SPDX-License-Identifier: Apache-2.0
12+
********************************************************************************/
13+
#ifndef SCORE_HM_HEARTBEAT_HEARTBEAT_MONITOR_H
14+
#define SCORE_HM_HEARTBEAT_HEARTBEAT_MONITOR_H
15+
16+
#include <score/expected.hpp>
17+
#include <score/hm/common.h>
18+
19+
namespace score::hm
20+
{
21+
// Forward declaration
22+
class HealthMonitor;
23+
class HealthMonitorBuilder;
24+
} // namespace score::hm
25+
26+
namespace score::hm::heartbeat
27+
{
28+
// Forward declaration
29+
class HeartbeatMonitor;
30+
31+
/// Builder for `HeartbeatMonitor`.
32+
class HeartbeatMonitorBuilder final : public internal::RustDroppable<HeartbeatMonitorBuilder>
33+
{
34+
public:
35+
/// Create a new `HeartbeatMonitorBuilder`.
36+
///
37+
/// - `range` - time range between heartbeats.
38+
HeartbeatMonitorBuilder(const TimeRange& range);
39+
40+
HeartbeatMonitorBuilder(const HeartbeatMonitorBuilder&) = delete;
41+
HeartbeatMonitorBuilder& operator=(const HeartbeatMonitorBuilder&) = delete;
42+
43+
HeartbeatMonitorBuilder(HeartbeatMonitorBuilder&&) = default;
44+
HeartbeatMonitorBuilder& operator=(HeartbeatMonitorBuilder&&) = delete;
45+
46+
protected:
47+
std::optional<internal::FFIHandle> __drop_by_rust_impl()
48+
{
49+
return monitor_builder_handle_.drop_by_rust();
50+
}
51+
52+
private:
53+
internal::DroppableFFIHandle monitor_builder_handle_;
54+
55+
// Allow to hide drop_by_rust implementation
56+
friend class internal::RustDroppable<HeartbeatMonitorBuilder>;
57+
58+
// Allow HealthMonitorBuilder to access drop_by_rust implementation
59+
friend class ::score::hm::HealthMonitorBuilder;
60+
};
61+
62+
class HeartbeatMonitor final
63+
{
64+
public:
65+
// Delete copy, allow move
66+
HeartbeatMonitor(const HeartbeatMonitor&) = delete;
67+
HeartbeatMonitor& operator=(const HeartbeatMonitor&) = delete;
68+
69+
HeartbeatMonitor(HeartbeatMonitor&& other) noexcept = default;
70+
HeartbeatMonitor& operator=(HeartbeatMonitor&& other) noexcept = default;
71+
72+
void heartbeat();
73+
74+
private:
75+
explicit HeartbeatMonitor(internal::FFIHandle monitor_handle);
76+
77+
// Only `HealthMonitor` is allowed to create `HeartbeatMonitor` instances.
78+
friend class score::hm::HealthMonitor;
79+
internal::DroppableFFIHandle monitor_handle_;
80+
};
81+
82+
} // namespace score::hm::heartbeat
83+
84+
#endif // SCORE_HM_HEARTBEAT_HEARTBEAT_MONITOR_H

src/health_monitoring_lib/cpp/tests/health_monitor_test.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,14 @@ TEST_F(HealthMonitorTest, TestName)
4444
.add_deadline(DeadlineTag("deadline_2"),
4545
TimeRange(std::chrono::milliseconds(100), std::chrono::milliseconds(200)));
4646

47+
// Setup heartbeat monitor construction.
48+
const MonitorTag heartbeat_monitor_tag{"heartbeat_monitor"};
49+
const TimeRange heartbeat_range{std::chrono::milliseconds{100}, std::chrono::milliseconds{200}};
50+
auto heartbeat_monitor_builder = heartbeat::HeartbeatMonitorBuilder(heartbeat_range);
51+
4752
auto hm = HealthMonitorBuilder()
4853
.add_deadline_monitor(deadline_monitor_tag, std::move(deadline_monitor_builder))
54+
.add_heartbeat_monitor(heartbeat_monitor_tag, std::move(heartbeat_monitor_builder))
4955
.with_internal_processing_cycle(std::chrono::milliseconds(50))
5056
.with_supervisor_api_cycle(std::chrono::milliseconds(50))
5157
.build();
@@ -62,9 +68,23 @@ TEST_F(HealthMonitorTest, TestName)
6268

6369
auto deadline_mon = std::move(*deadline_monitor_res);
6470

71+
// Obtain heartbeat monitor from HMON.
72+
auto heartbeat_monitor_res{hm.get_heartbeat_monitor(heartbeat_monitor_tag)};
73+
EXPECT_TRUE(heartbeat_monitor_res.has_value());
74+
75+
{
76+
// Try again to get the same monitor.
77+
auto heartbeat_monitor_res{hm.get_heartbeat_monitor(heartbeat_monitor_tag)};
78+
EXPECT_FALSE(heartbeat_monitor_res.has_value());
79+
}
80+
81+
auto heartbeat_monitor{std::move(*heartbeat_monitor_res)};
82+
6583
// Start HMON.
6684
hm.start();
6785

86+
heartbeat_monitor.heartbeat();
87+
6888
auto deadline_res = deadline_mon.get_deadline(DeadlineTag("deadline_1"));
6989

7090
{

0 commit comments

Comments
 (0)