Skip to content

Commit

Permalink
Merge pull request #574 from elfenpiff/iox2-573-deadline-property
Browse files Browse the repository at this point in the history
[#573] deadline property
  • Loading branch information
elfenpiff authored Jan 3, 2025
2 parents 7dbabc1 + c93f712 commit 06c5c21
Show file tree
Hide file tree
Showing 34 changed files with 752 additions and 22 deletions.
2 changes: 2 additions & 0 deletions config/iceoryx2.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ max-listeners = 16
max-notifiers = 16
max-nodes = 36
event-id-max-value = 4294967295
# deadline.secs = 1 # uncomment to enable deadline
# deadline.nanos = 0 # uncomment to enable deadline
# notifier-created-event = 1 # uncomment to enable setting
# notifier-dropped-event = 2 # uncomment to enable setting
# notifier-dead-event = 3 # uncomment to enable setting
3 changes: 2 additions & 1 deletion doc/release-notes/iceoryx2-unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
conflicts when merging.
-->

* Example text [#1](https://github.com/eclipse-iceoryx/iceoryx2/issues/1)
* Deadline property for event services
[#573](https://github.com/eclipse-iceoryx/iceoryx2/issues/573)

### Bugfixes

Expand Down
7 changes: 7 additions & 0 deletions examples/cxx/health_monitoring/src/central_daemon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
using namespace iox2;

constexpr iox::units::Duration CYCLE_TIME = iox::units::Duration::fromMilliseconds(100);
constexpr iox::units::Duration DEADLINE_SERVICE_1 = iox::units::Duration::fromMilliseconds(1500);
constexpr iox::units::Duration DEADLINE_SERVICE_2 = iox::units::Duration::fromMilliseconds(2000);

namespace {
void find_and_cleanup_dead_nodes();
Expand All @@ -47,6 +49,10 @@ auto main() -> int {

auto service_event_1 = node.service_builder(service_name_1)
.event()
// Defines the maximum timespan between two notifications for this service. The user of a
// notifier that send a notification after the deadline was already reached, receives an
// MISSED_DEADLINE error after the notification was delivered.
.deadline(DEADLINE_SERVICE_1)
// Whenever a new notifier is created the PublisherConnected event is emitted. this makes
// sense since in this example a notifier is always created after a new publisher was
// created.
Expand All @@ -67,6 +73,7 @@ auto main() -> int {

auto service_event_2 = node.service_builder(service_name_2)
.event()
.deadline(DEADLINE_SERVICE_2)
.notifier_created_event(iox::into<EventId>(PubSubEvent::PublisherConnected))
.notifier_dropped_event(iox::into<EventId>(PubSubEvent::PublisherDisconnected))
.notifier_dead_event(iox::into<EventId>(PubSubEvent::ProcessDied))
Expand Down
12 changes: 7 additions & 5 deletions examples/cxx/health_monitoring/src/subscriber.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@ auto main() -> int {

auto waitset = WaitSetBuilder().create<ServiceType::Ipc>().expect("");

// If the service has defined a deadline we will use it, otherwise
// we expect that the listener receive a message sent event after at most CYCLE_TIME_X
// so we add it as a deadline
auto listener_1_guard = waitset.attach_deadline(listener_1, CYCLE_TIME_1).expect("");
auto listener_2_guard = waitset.attach_deadline(listener_2, CYCLE_TIME_2).expect("");
auto deadline_1 = listener_1.deadline().value_or(CYCLE_TIME_1);
auto deadline_2 = listener_2.deadline().value_or(CYCLE_TIME_2);
auto listener_1_guard = waitset.attach_deadline(listener_1, deadline_1).expect("");
auto listener_2_guard = waitset.attach_deadline(listener_2, deadline_2).expect("");

auto missed_deadline = [](const ServiceName& service_name, const iox::units::Duration& cycle_time) {
std::cout << service_name.to_string().c_str() << ": voilated contract and did not send a message after "
Expand All @@ -64,15 +66,15 @@ auto main() -> int {

auto on_event = [&](const WaitSetAttachmentId<ServiceType::Ipc>& attachment_id) {
if (attachment_id.has_missed_deadline(listener_1_guard)) {
missed_deadline(service_name_1, CYCLE_TIME_1);
missed_deadline(service_name_1, deadline_1);
// one cause of a deadline it can be a dead node. usually our "central_daemon" would
// take care of monitoring but when the node and the central daemon crashed we take
// over here and check for dead nodes
find_and_cleanup_dead_nodes();
}

if (attachment_id.has_missed_deadline(listener_2_guard)) {
missed_deadline(service_name_2, CYCLE_TIME_2);
missed_deadline(service_name_2, deadline_2);
find_and_cleanup_dead_nodes();
}

Expand Down
7 changes: 7 additions & 0 deletions examples/rust/health_monitoring/central_daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use examples_common::PubSubEvent;
use iceoryx2::{node::NodeView, prelude::*};

const CYCLE_TIME: Duration = Duration::from_millis(100);
const DEADLINE_SERVICE_1: Duration = Duration::from_millis(1500);
const DEADLINE_SERVICE_2: Duration = Duration::from_millis(2000);

fn main() -> Result<(), Box<dyn std::error::Error>> {
let service_name_1 = ServiceName::new("service_1")?;
Expand All @@ -36,6 +38,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let _service_event_1 = node
.service_builder(&service_name_1)
.event()
// Defines the maximum timespan between two notifications for this service. The user of a
// notifier that send a notification after the deadline was already reached, receives an
// MISSED_DEADLINE error after the notification was delivered.
.deadline(DEADLINE_SERVICE_1)
// Whenever a new notifier is created the PublisherConnected event is emitted. this makes
// sense since in this example a notifier is always created after a new publisher was
// created.
Expand All @@ -56,6 +62,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let _service_event_2 = node
.service_builder(&service_name_2)
.event()
.deadline(DEADLINE_SERVICE_2)
.notifier_created_event(PubSubEvent::PublisherConnected.into())
.notifier_dropped_event(PubSubEvent::PublisherDisconnected.into())
.notifier_dead_event(PubSubEvent::ProcessDied.into())
Expand Down
14 changes: 8 additions & 6 deletions examples/rust/health_monitoring/subscriber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use iceoryx2::{
prelude::*,
};

const REACTION_BUFFER_MS: u64 = 100;
const REACTION_BUFFER_MS: u64 = 500;
const CYCLE_TIME_1: Duration = Duration::from_millis(1000 + REACTION_BUFFER_MS);
const CYCLE_TIME_2: Duration = Duration::from_millis(1500 + REACTION_BUFFER_MS);

Expand All @@ -41,10 +41,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

let waitset = WaitSetBuilder::new().create::<ipc::Service>()?;

// If the service has defined a deadline we will use it, otherwise
// we expect that the listener receive a message sent event after at most CYCLE_TIME_X
// so we add it as a deadline
let listener_1_guard = waitset.attach_deadline(&listener_1, CYCLE_TIME_1)?;
let listener_2_guard = waitset.attach_deadline(&listener_2, CYCLE_TIME_2)?;
let deadline_1 = listener_1.deadline().unwrap_or(CYCLE_TIME_1);
let deadline_2 = listener_2.deadline().unwrap_or(CYCLE_TIME_2);
let listener_1_guard = waitset.attach_deadline(&listener_1, deadline_1)?;
let listener_2_guard = waitset.attach_deadline(&listener_2, deadline_2)?;

let missed_deadline = |service_name, cycle_time| {
println!(
Expand All @@ -55,15 +57,15 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

let on_event = |attachment_id: WaitSetAttachmentId<ipc::Service>| {
if attachment_id.has_missed_deadline(&listener_1_guard) {
missed_deadline(&service_name_1, CYCLE_TIME_1);
missed_deadline(&service_name_1, deadline_1);
// one cause of a deadline it can be a dead node. usually our "central_daemon" would
// take care of monitoring but when the node and the central daemon crashed we take
// over here and check for dead nodes
find_and_cleanup_dead_nodes();
}

if attachment_id.has_missed_deadline(&listener_2_guard) {
missed_deadline(&service_name_2, CYCLE_TIME_2);
missed_deadline(&service_name_2, deadline_2);
find_and_cleanup_dead_nodes();
}

Expand Down
5 changes: 3 additions & 2 deletions iceoryx2-bb/posix/src/clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use iceoryx2_bb_elementary::enum_gen;
use iceoryx2_bb_log::fail;
use iceoryx2_pal_posix::posix::errno::Errno;
use iceoryx2_pal_posix::*;
use serde::{Deserialize, Serialize};
use std::time::Duration;

#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
Expand Down Expand Up @@ -63,7 +64,7 @@ impl From<TimeError> for NanosleepError {
}

/// Represents different low level clocks.
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[repr(i32)]
pub enum ClockType {
/// represents a steady clock which does not change when the system time
Expand Down Expand Up @@ -179,7 +180,7 @@ impl TimeBuilder {
}

/// Represents time under a specified [`ClockType`]
#[derive(Default, Clone, Copy, Eq, PartialEq, Hash, Debug)]
#[derive(Default, Clone, Copy, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
pub struct Time {
pub(crate) clock_type: ClockType,
pub(crate) seconds: u64,
Expand Down
6 changes: 6 additions & 0 deletions iceoryx2-ffi/cxx/include/iox2/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,12 @@ class Event {
auto notifier_dead_event() && -> iox::optional<size_t>;
/// Sets the event id value that is emitted if a notifier was identified as dead.
void set_notifier_dead_event(iox::optional<size_t> value) &&;
/// Defines the maximum allowed time between two consecutive notifications. If a notifiation
/// is not sent after the defined time, every [`Listener`]
/// that is attached to a [`WaitSet`] will be notified.
auto deadline() && -> iox::optional<iox::units::Duration>;
/// Sets the deadline of the event service.
void set_deadline(iox::optional<iox::units::Duration> value) &&;

private:
friend class Defaults;
Expand Down
10 changes: 10 additions & 0 deletions iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,8 @@ constexpr auto from<int, iox2::EventOpenOrCreateError>(const int value) noexcept
return iox2::EventOpenOrCreateError::OpenIncompatibleMessagingPattern;
case iox2_event_open_or_create_error_e_O_INCOMPATIBLE_ATTRIBUTES:
return iox2::EventOpenOrCreateError::OpenIncompatibleAttributes;
case iox2_event_open_or_create_error_e_O_INCOMPATIBLE_DEADLINE:
return iox2::EventOpenOrCreateError::OpenIncompatibleDeadline;
case iox2_event_open_or_create_error_e_O_INTERNAL_FAILURE:
return iox2::EventOpenOrCreateError::OpenInternalFailure;
case iox2_event_open_or_create_error_e_O_HANGS_IN_CREATION:
Expand Down Expand Up @@ -867,6 +869,10 @@ constexpr auto from<int, iox2::NotifierNotifyError>(const int value) noexcept ->
switch (error) {
case iox2_notifier_notify_error_e_EVENT_ID_OUT_OF_BOUNDS:
return iox2::NotifierNotifyError::EventIdOutOfBounds;
case iox2_notifier_notify_error_e_MISSED_DEADLINE:
return iox2::NotifierNotifyError::MissedDeadline;
case iox2_notifier_notify_error_e_UNABLE_TO_ACQUIRE_ELAPSED_TIME:
return iox2::NotifierNotifyError::UnableToAcquireElapsedTime;
}

IOX_UNREACHABLE();
Expand All @@ -879,6 +885,10 @@ from<iox2::NotifierNotifyError, iox2_notifier_notify_error_e>(const iox2::Notifi
switch (value) {
case iox2::NotifierNotifyError::EventIdOutOfBounds:
return iox2_notifier_notify_error_e_EVENT_ID_OUT_OF_BOUNDS;
case iox2::NotifierNotifyError::MissedDeadline:
return iox2_notifier_notify_error_e_MISSED_DEADLINE;
case iox2::NotifierNotifyError::UnableToAcquireElapsedTime:
return iox2_notifier_notify_error_e_UNABLE_TO_ACQUIRE_ELAPSED_TIME;
}

IOX_UNREACHABLE();
Expand Down
3 changes: 3 additions & 0 deletions iceoryx2-ffi/cxx/include/iox2/listener.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ class Listener : public FileDescriptorBased {
/// in detail.
auto blocking_wait_one() -> iox::expected<iox::optional<EventId>, ListenerWaitError>;

/// Returns the deadline of the corresponding [`Service`].
auto deadline() const -> iox::optional<iox::units::Duration>;

private:
template <ServiceType>
friend class PortFactoryListener;
Expand Down
6 changes: 4 additions & 2 deletions iceoryx2-ffi/cxx/include/iox2/notifier.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@
#ifndef IOX2_NOTIFIER_HPP
#define IOX2_NOTIFIER_HPP

#include "iox/duration.hpp"
#include "iox/expected.hpp"
#include "iox2/event_id.hpp"
#include "iox2/internal/iceoryx2.hpp"
#include "iox2/notifier_error.hpp"
#include "iox2/service_type.hpp"
#include "iox2/unique_port_id.hpp"

#include <cstdint>

namespace iox2 {
/// Represents the sending endpoint of an event based communication.
template <ServiceType S>
Expand Down Expand Up @@ -51,6 +50,9 @@ class Notifier {
/// [`NotifierNotifyError`].
auto notify_with_custom_event_id(EventId event_id) const -> iox::expected<size_t, NotifierNotifyError>;

/// Returns the deadline of the corresponding [`Service`].
auto deadline() const -> iox::optional<iox::units::Duration>;

private:
template <ServiceType>
friend class PortFactoryNotifier;
Expand Down
7 changes: 7 additions & 0 deletions iceoryx2-ffi/cxx/include/iox2/notifier_error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ enum class NotifierNotifyError : uint8_t {
/// is greater than the maximum supported [`EventId`] by the
/// [`Service`]
EventIdOutOfBounds,
/// The notification was delivered to all [`Listener`] ports
/// but the deadline contract, the maximum time span between two notifications, of the
/// [`Service`] was violated.
MissedDeadline,
/// The notification was delivered but the elapsed system time could not be acquired.
/// Therefore, it is unknown if the deadline was missed or not.
UnableToAcquireElapsedTime,
};

} // namespace iox2
Expand Down
9 changes: 9 additions & 0 deletions iceoryx2-ffi/cxx/include/iox2/service_builder_event.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ class ServiceBuilderEvent {
/// provided no event will be emitted.
auto notifier_dead_event(EventId event_id) && -> ServiceBuilderEvent&&;

/// Enables the deadline property of the service. There must be a notification emitted by any
/// [`Notifier`] after at least the provided `deadline`.
auto deadline(iox::units::Duration deadline) && -> ServiceBuilderEvent&&;

/// If the [`Service`] is created it disables sending an event when a notifier was dropped.
auto disable_notifier_dropped_event() && -> ServiceBuilderEvent&&;

Expand All @@ -74,6 +78,9 @@ class ServiceBuilderEvent {
/// as dead.
auto disable_notifier_dead_event() && -> ServiceBuilderEvent&&;

/// Disables the deadline property of the service. [`Notifier`]
/// can signal notifications at any rate.
auto disable_deadline() && -> ServiceBuilderEvent&&;

/// If the [`Service`] exists, it will be opened otherwise a new [`Service`] will be
/// created.
Expand Down Expand Up @@ -114,9 +121,11 @@ class ServiceBuilderEvent {
iox::optional<EventId> m_notifier_dead_event;
iox::optional<EventId> m_notifier_created_event;
iox::optional<EventId> m_notifier_dropped_event;
iox::optional<iox::units::Duration> m_deadline;
bool m_verify_notifier_dead_event = false;
bool m_verify_notifier_created_event = false;
bool m_verify_notifier_dropped_event = false;
bool m_verify_deadline = false;
};
} // namespace iox2

Expand Down
2 changes: 2 additions & 0 deletions iceoryx2-ffi/cxx/include/iox2/service_builder_event_error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ enum class EventOpenOrCreateError : uint8_t {
/// a corrupted
/// [`Service`]state.
OpenServiceInCorruptedState,
/// The [`Service`]s deadline settings are not equal the the user given requirements.
OpenIncompatibleDeadline,
/// The [`Service`] has the wrong messaging pattern.
OpenIncompatibleMessagingPattern,
/// The [`AttributeVerifier`] required attributes that the [`Service`] does
Expand Down
8 changes: 8 additions & 0 deletions iceoryx2-ffi/cxx/include/iox2/static_config_event.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#ifndef IOX2_STATIC_CONFIG_EVENT_HPP
#define IOX2_STATIC_CONFIG_EVENT_HPP

#include "iox/duration.hpp"
#include "iox/optional.hpp"
#include "iox2/event_id.hpp"
#include "iox2/iceoryx2.h"
Expand Down Expand Up @@ -46,6 +47,13 @@ class StaticConfigEvent {
/// Returns the emitted [`EventId`] when a notifier is identified as dead.
auto notifier_dead_event() const -> iox::optional<EventId>;

/// Returns the deadline of the service. If no new notification is signaled from any
/// [`Notifier`] after the given deadline, it is rated
/// as an error and all [`Listener`] that are attached
/// to a [`WaitSet`] are woken up and notified about the missed
/// deadline.
auto deadline() const -> iox::optional<iox::units::Duration>;

private:
template <ServiceType>
friend class PortFactoryEvent;
Expand Down
20 changes: 20 additions & 0 deletions iceoryx2-ffi/cxx/src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,26 @@ void Event::set_notifier_dead_event(iox::optional<size_t> value) && {
}
}

auto Event::deadline() && -> iox::optional<iox::units::Duration> {
uint64_t seconds = 0;
uint32_t nanoseconds = 0;
if (iox2_config_defaults_event_deadline(m_config, &seconds, &nanoseconds)) {
return { iox::units::Duration::fromSeconds(seconds) + iox::units::Duration::fromNanoseconds(nanoseconds) };
}

return iox::nullopt;
}

void Event::set_deadline(iox::optional<iox::units::Duration> value) && {
value
.and_then([&](auto value) {
const uint64_t seconds = value.toSeconds();
const uint32_t nanoseconds =
value.toNanoseconds() - (value.toSeconds() * iox::units::Duration::NANOSECS_PER_SEC);
iox2_config_defaults_event_set_deadline(m_config, &seconds, &nanoseconds);
})
.or_else([&] { iox2_config_defaults_event_set_deadline(m_config, nullptr, nullptr); });
}
/////////////////////////
// END: Event
/////////////////////////
Expand Down
12 changes: 12 additions & 0 deletions iceoryx2-ffi/cxx/src/listener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@ auto Listener<S>::id() const -> UniqueListenerId {
return UniqueListenerId { id_handle };
}

template <ServiceType S>
auto Listener<S>::deadline() const -> iox::optional<iox::units::Duration> {
uint64_t seconds = 0;
uint32_t nanoseconds = 0;

if (iox2_listener_deadline(&m_handle, &seconds, &nanoseconds)) {
return { iox::units::Duration::fromSeconds(seconds) + iox::units::Duration::fromNanoseconds(nanoseconds) };
}

return iox::nullopt;
}

void wait_callback(const iox2_event_id_t* event_id, iox2_callback_context context) {
auto* callback = internal::ctx_cast<iox::function<void(EventId)>>(context);
callback->value()(EventId(*event_id));
Expand Down
Loading

0 comments on commit 06c5c21

Please sign in to comment.