diff --git a/config/README.md b/config/README.md index 5276b7b47..c56bf5080 100644 --- a/config/README.md +++ b/config/README.md @@ -63,6 +63,12 @@ Adjusting `global` settings ensures a non-interfering setup. * `defaults.event.max-nodes` - [int]: Maximum number of nodes. * `defaults.event.event-id-max-value` - [int]: Greatest value an [`EventId`] can have. +* `defaults.event.notifier-created-event` - [Option\]: If defined, + it defines the event id that is emitted when a new notifier is created. +* `defaults.event.notifier-dropped-event` - [Option\]: If defined, + it defines the event id that is emitted when a notifier is destroyed. +* `defaults.event.notifier-dead-event` - [Option\]: If defined, + it defines the event id that is emitted when a dead notifier is cleaned up. ### Service: Publish Subscribe Messaging Pattern diff --git a/config/iceoryx2.toml b/config/iceoryx2.toml index 00ca9fb52..ae1e7487b 100644 --- a/config/iceoryx2.toml +++ b/config/iceoryx2.toml @@ -38,3 +38,6 @@ max-listeners = 16 max-notifiers = 16 max-nodes = 36 event-id-max-value = 4294967295 +# notifier-created-event = 1 # uncomment to enable setting +# notifier-dropped-event = 2 # uncomment to enable setting +# notifier-dead-event = 3 # uncomment to enable setting diff --git a/doc/release-notes/iceoryx2-unreleased.md b/doc/release-notes/iceoryx2-unreleased.md index cfb430ba8..f88531fc8 100644 --- a/doc/release-notes/iceoryx2-unreleased.md +++ b/doc/release-notes/iceoryx2-unreleased.md @@ -23,6 +23,7 @@ * Make signal handling optional in `WaitSet` and `Node` [#528](https://github.com/eclipse-iceoryx/iceoryx2/issues/528) * Support dynamic data with reallocation for publish-subscribe communication [#532](https://github.com/eclipse-iceoryx/iceoryx2/issues/532) * Add benchmark for iceoryx2 queues [#535](https://github.com/eclipse-iceoryx/iceoryx2/issues/535) +* Add auto event mission for create, drop and dead notifiers [#550](https://github.com/eclipse-iceoryx/iceoryx2/issues/550) ### Bugfixes diff --git a/iceoryx2-ffi/cxx/include/iox/builder_addendum.hpp b/iceoryx2-ffi/cxx/include/iox/builder_addendum.hpp index 36bb15b63..a6dc9bce6 100644 --- a/iceoryx2-ffi/cxx/include/iox/builder_addendum.hpp +++ b/iceoryx2-ffi/cxx/include/iox/builder_addendum.hpp @@ -15,6 +15,16 @@ // NOLINTBEGIN(cppcoreguidelines-macro-usage) // NOLINTBEGIN(bugprone-macro-parentheses) +#define IOX_BUILDER_SWITCH(name) \ + public: \ + auto name()&& noexcept -> decltype(auto) { \ + m_##name = true; \ + return std::move(*this); \ + } \ + \ + private: \ + bool m_##name { false }; + #define IOX_BUILDER_PARAMETER(type, name, defaultValue) \ public: \ auto name(type const& value)&& noexcept -> decltype(auto) { \ diff --git a/iceoryx2-ffi/cxx/include/iox2/config.hpp b/iceoryx2-ffi/cxx/include/iox2/config.hpp index 832b517c7..05483b54e 100644 --- a/iceoryx2-ffi/cxx/include/iox2/config.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/config.hpp @@ -212,6 +212,18 @@ class Event { auto event_id_max_value() && -> size_t; /// Set the largest event id supported by the event service void set_event_id_max_value(size_t value) &&; + /// Defines the event id value that is emitted after a new notifier was created. + auto notifier_created_event() && -> iox::optional; + /// Sets the event id value that is emitted after a new notifier was created. + void set_notifier_created_event(iox::optional value) &&; + /// Defines the event id value that is emitted before a new notifier is dropped. + auto notifier_dropped_event() && -> iox::optional; + /// Sets the event id value that is emitted before a new notifier is dropped. + void set_notifier_dropped_event(iox::optional value) &&; + /// Defines the event id value that is emitted if a notifier was identified as dead. + auto notifier_dead_event() && -> iox::optional; + /// Sets the event id value that is emitted if a notifier was identified as dead. + void set_notifier_dead_event(iox::optional value) &&; private: friend class Defaults; diff --git a/iceoryx2-ffi/cxx/include/iox2/event_id.hpp b/iceoryx2-ffi/cxx/include/iox2/event_id.hpp index 8e5d51eb9..d85d4e887 100644 --- a/iceoryx2-ffi/cxx/include/iox2/event_id.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/event_id.hpp @@ -50,6 +50,12 @@ class EventId { }; auto operator<<(std::ostream& stream, const EventId& value) -> std::ostream&; +auto operator==(const EventId& lhs, const EventId& rhs) -> bool; +auto operator!=(const EventId& lhs, const EventId& rhs) -> bool; +auto operator<(const EventId& lhs, const EventId& rhs) -> bool; +auto operator<=(const EventId& lhs, const EventId& rhs) -> bool; +auto operator>(const EventId& lhs, const EventId& rhs) -> bool; +auto operator>=(const EventId& lhs, const EventId& rhs) -> bool; } // namespace iox2 #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/service_builder_event.hpp b/iceoryx2-ffi/cxx/include/iox2/service_builder_event.hpp index dd899e6f9..4eca1b80f 100644 --- a/iceoryx2-ffi/cxx/include/iox2/service_builder_event.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/service_builder_event.hpp @@ -17,6 +17,7 @@ #include "iox/expected.hpp" #include "iox2/attribute_specifier.hpp" #include "iox2/attribute_verifier.hpp" +#include "iox2/event_id.hpp" #include "iox2/internal/iceoryx2.hpp" #include "iox2/port_factory_event.hpp" #include "iox2/service_builder_event_error.hpp" @@ -49,6 +50,31 @@ class ServiceBuilderEvent { IOX_BUILDER_OPTIONAL(uint64_t, max_listeners); public: + /// If the [`Service`] is created it defines the event that shall be emitted by every + /// [`Notifier`] before it is dropped. If [`None`] is + /// provided a [`Notifier`] will not emit an event. + auto notifier_dropped_event(EventId event_id) && -> ServiceBuilderEvent&&; + + /// If the [`Service`] is created it defines the event that shall be emitted by every newly + /// created [`Notifier`]. + auto notifier_created_event(EventId event_id) && -> ServiceBuilderEvent&&; + + /// If the [`Service`] is created it defines the event that shall be emitted when a + /// [`Notifier`] is identified as dead. If [`None`] is + /// provided no event will be emitted. + auto notifier_dead_event(EventId event_id) && -> ServiceBuilderEvent&&; + + /// If the [`Service`] is created it disables sending an event when a notifier was dropped. + auto disable_notifier_dropped_event() && -> ServiceBuilderEvent&&; + + /// If the [`Service`] is created it disables sending an event when a new notifier was created. + auto disable_notifier_created_event() && -> ServiceBuilderEvent&&; + + /// If the [`Service`] is created it disables sending an event when a notifier was identified + /// as dead. + auto disable_notifier_dead_event() && -> ServiceBuilderEvent&&; + + /// If the [`Service`] exists, it will be opened otherwise a new [`Service`] will be /// created. auto open_or_create() && -> iox::expected, EventOpenOrCreateError>; @@ -84,6 +110,13 @@ class ServiceBuilderEvent { void set_parameters(); iox2_service_builder_event_h m_handle = nullptr; + + iox::optional m_notifier_dead_event; + iox::optional m_notifier_created_event; + iox::optional m_notifier_dropped_event; + bool m_verify_notifier_dead_event = false; + bool m_verify_notifier_created_event = false; + bool m_verify_notifier_dropped_event = false; }; } // namespace iox2 diff --git a/iceoryx2-ffi/cxx/include/iox2/static_config_event.hpp b/iceoryx2-ffi/cxx/include/iox2/static_config_event.hpp index 2749d3bf7..ea660cdb9 100644 --- a/iceoryx2-ffi/cxx/include/iox2/static_config_event.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/static_config_event.hpp @@ -13,6 +13,8 @@ #ifndef IOX2_STATIC_CONFIG_EVENT_HPP #define IOX2_STATIC_CONFIG_EVENT_HPP +#include "iox/optional.hpp" +#include "iox2/event_id.hpp" #include "iox2/iceoryx2.h" #include "iox2/internal/iceoryx2.hpp" @@ -35,6 +37,15 @@ class StaticConfigEvent { /// Returns the largest [`EventId`] that is supported by the service auto event_id_max_value() const -> size_t; + /// Returns the emitted [`EventId`] when a new notifier is created. + auto notifier_created_event() const -> iox::optional; + + /// Returns the emitted [`EventId`] when a notifier is dropped. + auto notifier_dropped_event() const -> iox::optional; + + /// Returns the emitted [`EventId`] when a notifier is identified as dead. + auto notifier_dead_event() const -> iox::optional; + private: template friend class PortFactoryEvent; diff --git a/iceoryx2-ffi/cxx/src/config.cpp b/iceoryx2-ffi/cxx/src/config.cpp index dbb440cab..e9aa283bb 100644 --- a/iceoryx2-ffi/cxx/src/config.cpp +++ b/iceoryx2-ffi/cxx/src/config.cpp @@ -201,6 +201,58 @@ auto Event::event_id_max_value() && -> size_t { void Event::set_event_id_max_value(size_t value) && { iox2_config_defaults_event_set_event_id_max_value(m_config, value); } + +auto Event::notifier_created_event() && -> iox::optional { + size_t value = 0; + if (iox2_config_defaults_event_notifier_created_event(m_config, &value)) { + return { value }; + } + + return iox::nullopt; +} + +void Event::set_notifier_created_event(iox::optional value) && { + if (value.has_value()) { + iox2_config_defaults_event_set_notifier_created_event(m_config, &*value); + } else { + iox2_config_defaults_event_set_notifier_created_event(m_config, nullptr); + } +} + +auto Event::notifier_dropped_event() && -> iox::optional { + size_t value = 0; + if (iox2_config_defaults_event_notifier_dropped_event(m_config, &value)) { + return { value }; + } + + return iox::nullopt; +} + +void Event::set_notifier_dropped_event(iox::optional value) && { + if (value.has_value()) { + iox2_config_defaults_event_set_notifier_dropped_event(m_config, &*value); + } else { + iox2_config_defaults_event_set_notifier_dropped_event(m_config, nullptr); + } +} + +auto Event::notifier_dead_event() && -> iox::optional { + size_t value = 0; + if (iox2_config_defaults_event_notifier_dead_event(m_config, &value)) { + return { value }; + } + + return iox::nullopt; +} + +void Event::set_notifier_dead_event(iox::optional value) && { + if (value.has_value()) { + iox2_config_defaults_event_set_notifier_dead_event(m_config, &*value); + } else { + iox2_config_defaults_event_set_notifier_dead_event(m_config, nullptr); + } +} + ///////////////////////// // END: Event ///////////////////////// diff --git a/iceoryx2-ffi/cxx/src/event_id.cpp b/iceoryx2-ffi/cxx/src/event_id.cpp index 9918d720b..883939fea 100644 --- a/iceoryx2-ffi/cxx/src/event_id.cpp +++ b/iceoryx2-ffi/cxx/src/event_id.cpp @@ -11,7 +11,6 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT #include "iox2/event_id.hpp" -#include "iox/assertions_addendum.hpp" namespace iox2 { EventId::EventId(const size_t value) @@ -30,4 +29,28 @@ auto operator<<(std::ostream& stream, const EventId& value) -> std::ostream& { stream << "EventId { m_value: " << value.as_value() << " }"; return stream; } + +auto operator==(const EventId& lhs, const EventId& rhs) -> bool { + return lhs.as_value() == rhs.as_value(); +} + +auto operator!=(const EventId& lhs, const EventId& rhs) -> bool { + return lhs.as_value() != rhs.as_value(); +} + +auto operator<(const EventId& lhs, const EventId& rhs) -> bool { + return lhs.as_value() < rhs.as_value(); +} + +auto operator<=(const EventId& lhs, const EventId& rhs) -> bool { + return lhs.as_value() <= rhs.as_value(); +} + +auto operator>(const EventId& lhs, const EventId& rhs) -> bool { + return lhs.as_value() > rhs.as_value(); +} + +auto operator>=(const EventId& lhs, const EventId& rhs) -> bool { + return lhs.as_value() >= rhs.as_value(); +} } // namespace iox2 diff --git a/iceoryx2-ffi/cxx/src/service_builder_event.cpp b/iceoryx2-ffi/cxx/src/service_builder_event.cpp index a77e6bcf4..5094c6e04 100644 --- a/iceoryx2-ffi/cxx/src/service_builder_event.cpp +++ b/iceoryx2-ffi/cxx/src/service_builder_event.cpp @@ -23,10 +23,74 @@ template void ServiceBuilderEvent::set_parameters() { m_max_notifiers.and_then([&](auto value) { iox2_service_builder_event_set_max_notifiers(&m_handle, value); }); m_max_listeners.and_then([&](auto value) { iox2_service_builder_event_set_max_listeners(&m_handle, value); }); + + if (m_verify_notifier_created_event) { + m_notifier_created_event + .and_then( + [&](auto value) { iox2_service_builder_event_set_notifier_created_event(&m_handle, value.as_value()); }) + .or_else([&]() { iox2_service_builder_event_disable_notifier_created_event(&m_handle); }); + } + + if (m_verify_notifier_dropped_event) { + m_notifier_dropped_event + .and_then( + [&](auto value) { iox2_service_builder_event_set_notifier_dropped_event(&m_handle, value.as_value()); }) + .or_else([&]() { iox2_service_builder_event_disable_notifier_dropped_event(&m_handle); }); + } + + if (m_verify_notifier_dead_event) { + m_notifier_dead_event + .and_then( + [&](auto value) { iox2_service_builder_event_set_notifier_dead_event(&m_handle, value.as_value()); }) + .or_else([&]() { iox2_service_builder_event_disable_notifier_dead_event(&m_handle); }); + } + m_max_nodes.and_then([](auto) { IOX_TODO(); }); m_event_id_max_value.and_then([](auto) { IOX_TODO(); }); } +template +auto ServiceBuilderEvent::notifier_dropped_event(EventId event_id) && -> ServiceBuilderEvent&& { + m_notifier_dropped_event.emplace(event_id); + m_verify_notifier_dropped_event = true; + return std::move(*this); +} + +template +auto ServiceBuilderEvent::notifier_created_event(EventId event_id) && -> ServiceBuilderEvent&& { + m_notifier_created_event.emplace(event_id); + m_verify_notifier_created_event = true; + return std::move(*this); +} + +template +auto ServiceBuilderEvent::notifier_dead_event(EventId event_id) && -> ServiceBuilderEvent&& { + m_notifier_dead_event.emplace(event_id); + m_verify_notifier_dead_event = true; + return std::move(*this); +} + +template +auto ServiceBuilderEvent::disable_notifier_dropped_event() && -> ServiceBuilderEvent&& { + m_notifier_dropped_event.reset(); + m_verify_notifier_dropped_event = true; + return std::move(*this); +} + +template +auto ServiceBuilderEvent::disable_notifier_created_event() && -> ServiceBuilderEvent&& { + m_notifier_created_event.reset(); + m_verify_notifier_created_event = true; + return std::move(*this); +} + +template +auto ServiceBuilderEvent::disable_notifier_dead_event() && -> ServiceBuilderEvent&& { + m_notifier_dead_event.reset(); + m_verify_notifier_dead_event = true; + return std::move(*this); +} + template auto ServiceBuilderEvent::open_or_create() && -> iox::expected, EventOpenOrCreateError> { set_parameters(); diff --git a/iceoryx2-ffi/cxx/src/static_config_event.cpp b/iceoryx2-ffi/cxx/src/static_config_event.cpp index 4ca44550b..00415d5d7 100644 --- a/iceoryx2-ffi/cxx/src/static_config_event.cpp +++ b/iceoryx2-ffi/cxx/src/static_config_event.cpp @@ -20,13 +20,40 @@ StaticConfigEvent::StaticConfigEvent(iox2_static_config_event_t value) auto StaticConfigEvent::max_nodes() const -> size_t { return m_value.max_nodes; } + auto StaticConfigEvent::max_notifiers() const -> size_t { return m_value.max_notifiers; } + auto StaticConfigEvent::max_listeners() const -> size_t { return m_value.max_listeners; } + auto StaticConfigEvent::event_id_max_value() const -> size_t { return m_value.event_id_max_value; } + +auto StaticConfigEvent::notifier_created_event() const -> iox::optional { + if (!m_value.has_notifier_created_event) { + return iox::nullopt; + } + + return { EventId(m_value.notifier_created_event) }; +} + +auto StaticConfigEvent::notifier_dropped_event() const -> iox::optional { + if (!m_value.has_notifier_dropped_event) { + return iox::nullopt; + } + + return { EventId(m_value.notifier_dropped_event) }; +} + +auto StaticConfigEvent::notifier_dead_event() const -> iox::optional { + if (!m_value.has_notifier_dead_event) { + return iox::nullopt; + } + + return { EventId(m_value.notifier_dead_event) }; +} } // namespace iox2 diff --git a/iceoryx2-ffi/cxx/tests/src/config_tests.cpp b/iceoryx2-ffi/cxx/tests/src/config_tests.cpp index c767cf473..39e486a03 100644 --- a/iceoryx2-ffi/cxx/tests/src/config_tests.cpp +++ b/iceoryx2-ffi/cxx/tests/src/config_tests.cpp @@ -41,6 +41,39 @@ TEST(Config, defaults_event_max_listeners) { ASSERT_THAT(config.defaults().event().max_listeners(), Eq(test_value)); } +TEST(Config, defaults_event_notifier_created_event) { + const auto test_value = iox::optional(12); + auto config = Config(); + + config.defaults().event().set_notifier_created_event(test_value); + ASSERT_THAT(config.defaults().event().notifier_created_event(), Eq(test_value)); + + config.defaults().event().set_notifier_created_event(iox::nullopt); + ASSERT_THAT(config.defaults().event().notifier_created_event(), Eq(iox::nullopt)); +} + +TEST(Config, defaults_event_notifier_dropped_event) { + const auto test_value = iox::optional(13); + auto config = Config(); + + config.defaults().event().set_notifier_dropped_event(test_value); + ASSERT_THAT(config.defaults().event().notifier_dropped_event(), Eq(test_value)); + + config.defaults().event().set_notifier_dropped_event(iox::nullopt); + ASSERT_THAT(config.defaults().event().notifier_dropped_event(), Eq(iox::nullopt)); +} + +TEST(Config, defaults_event_notifier_dead_event) { + const auto test_value = iox::optional(14); + auto config = Config(); + + config.defaults().event().set_notifier_dead_event(test_value); + ASSERT_THAT(config.defaults().event().notifier_dead_event(), Eq(test_value)); + + config.defaults().event().set_notifier_dead_event(iox::nullopt); + ASSERT_THAT(config.defaults().event().notifier_dead_event(), Eq(iox::nullopt)); +} + TEST(Config, defaults_event_max_notifiers) { const auto test_value = 45; auto config = Config(); diff --git a/iceoryx2-ffi/cxx/tests/src/service_event_tests.cpp b/iceoryx2-ffi/cxx/tests/src/service_event_tests.cpp index 463858841..3a4d0f722 100644 --- a/iceoryx2-ffi/cxx/tests/src/service_event_tests.cpp +++ b/iceoryx2-ffi/cxx/tests/src/service_event_tests.cpp @@ -91,6 +91,9 @@ TYPED_TEST(ServiceEventTest, service_settings_are_applied) { constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; constexpr uint64_t NUMBER_OF_NOTIFIERS = 5; constexpr uint64_t NUMBER_OF_LISTENERS = 7; + const auto create_event_id = EventId(12); + const auto dropped_event_id = EventId(13); + const auto dead_event_id = EventId(14); const auto service_name = iox2_testing::generate_service_name(); @@ -99,6 +102,9 @@ TYPED_TEST(ServiceEventTest, service_settings_are_applied) { .event() .max_notifiers(NUMBER_OF_NOTIFIERS) .max_listeners(NUMBER_OF_LISTENERS) + .notifier_created_event(create_event_id) + .notifier_dropped_event(dropped_event_id) + .notifier_dead_event(dead_event_id) .create() .expect(""); @@ -106,6 +112,9 @@ TYPED_TEST(ServiceEventTest, service_settings_are_applied) { ASSERT_THAT(static_config.max_notifiers(), Eq(NUMBER_OF_NOTIFIERS)); ASSERT_THAT(static_config.max_listeners(), Eq(NUMBER_OF_LISTENERS)); + ASSERT_THAT(static_config.notifier_created_event(), Eq(iox::optional(create_event_id))); + ASSERT_THAT(static_config.notifier_dropped_event(), Eq(iox::optional(dropped_event_id))); + ASSERT_THAT(static_config.notifier_dead_event(), Eq(iox::optional(dead_event_id))); } TYPED_TEST(ServiceEventTest, open_fails_with_incompatible_max_notifiers_requirements) { @@ -205,6 +214,47 @@ TYPED_TEST(ServiceEventTest, service_name_is_set) { ASSERT_THAT(service_name.to_string(), Eq(sut_service_name.to_string())); } +TYPED_TEST(ServiceEventTest, notifier_emits_create_and_drop_events) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + const auto create_event_id = EventId(21); + const auto dropped_event_id = EventId(31); + + const auto service_name = iox2_testing::generate_service_name(); + + auto node = NodeBuilder().create().expect(""); + auto service = node.service_builder(service_name) + .event() + .notifier_created_event(create_event_id) + .notifier_dropped_event(dropped_event_id) + .create() + .expect(""); + + auto listener = service.listener_builder().create().expect(""); + + { + auto notifier = service.notifier_builder().create(); + + auto counter = 0; + listener + .try_wait_all([&](auto event_id) { + EXPECT_THAT(event_id, Eq(create_event_id)); + counter++; + }) + .expect(""); + ASSERT_THAT(counter, Eq(1)); + } + + auto counter = 0; + listener + .try_wait_all([&](auto event_id) { + EXPECT_THAT(event_id, Eq(dropped_event_id)); + counter++; + }) + .expect(""); + ASSERT_THAT(counter, Eq(1)); +} + + TYPED_TEST(ServiceEventTest, notification_is_received_with_try_wait_one) { this->notifier.notify().expect(""); diff --git a/iceoryx2-ffi/ffi/src/api/config.rs b/iceoryx2-ffi/ffi/src/api/config.rs index 3b99e99c7..b528833a2 100644 --- a/iceoryx2-ffi/ffi/src/api/config.rs +++ b/iceoryx2-ffi/ffi/src/api/config.rs @@ -76,7 +76,7 @@ pub(super) struct ConfigOwner { #[repr(C)] #[repr(align(8))] // align_of() pub struct iox2_config_storage_t { - internal: [u8; 3560], // size_of() + internal: [u8; 3608], // size_of() } /// Contains the iceoryx2 config @@ -1558,6 +1558,171 @@ pub unsafe extern "C" fn iox2_config_defaults_event_set_max_listeners( config.value.as_mut().value.defaults.event.max_listeners = value; } +/// Returns the event id value that is emitted when a new notifier is created. It returns `true` +/// if a value is emitted and sets the provided `value`, otherwise it returns `false`. +/// +/// # Safety +/// +/// * `handle` - A valid non-owning [`iox2_config_h_ref`]. +/// * `value` - points to a valid memory location +#[no_mangle] +pub unsafe extern "C" fn iox2_config_defaults_event_notifier_created_event( + handle: iox2_config_h_ref, + value: *mut c_size_t, +) -> bool { + handle.assert_non_null(); + debug_assert!(!value.is_null()); + + let config = &*handle.as_type(); + if let Some(v) = config + .value + .as_ref() + .value + .defaults + .event + .notifier_created_event + { + *value = v; + true + } else { + false + } +} + +/// Sets the event id value that is emitted when a new notifier is created. If `value` is `NULL` +/// no event will be emitted, otherwise the provided value will be used. +/// +/// # Safety +/// +/// * `handle` - A valid non-owning [`iox2_config_h_ref`]. +#[no_mangle] +pub unsafe extern "C" fn iox2_config_defaults_event_set_notifier_created_event( + handle: iox2_config_h_ref, + value: *const c_size_t, +) { + handle.assert_non_null(); + + let config = &mut *handle.as_type(); + + config + .value + .as_mut() + .value + .defaults + .event + .notifier_created_event = if value.is_null() { None } else { Some(*value) }; +} + +/// Returns the event id value that is emitted when a notifier is dropped. It returns `true` +/// if a value is emitted and sets the provided `value`, otherwise it returns `false`. +/// +/// # Safety +/// +/// * `handle` - A valid non-owning [`iox2_config_h_ref`]. +/// * `value` - points to a valid memory location +#[no_mangle] +pub unsafe extern "C" fn iox2_config_defaults_event_notifier_dropped_event( + handle: iox2_config_h_ref, + value: *mut c_size_t, +) -> bool { + handle.assert_non_null(); + debug_assert!(!value.is_null()); + + let config = &*handle.as_type(); + if let Some(v) = config + .value + .as_ref() + .value + .defaults + .event + .notifier_dropped_event + { + *value = v; + true + } else { + false + } +} + +/// Sets the event id value that is emitted when a notifier is dropped. If `value` is `NULL` +/// no event will be emitted, otherwise the provided value will be used. +/// +/// # Safety +/// +/// * `handle` - A valid non-owning [`iox2_config_h_ref`]. +#[no_mangle] +pub unsafe extern "C" fn iox2_config_defaults_event_set_notifier_dropped_event( + handle: iox2_config_h_ref, + value: *const c_size_t, +) { + handle.assert_non_null(); + + let config = &mut *handle.as_type(); + + config + .value + .as_mut() + .value + .defaults + .event + .notifier_dropped_event = if value.is_null() { None } else { Some(*value) }; +} + +/// Returns the event id value that is emitted when a notifier is identified as dead. It returns +/// `true` if a value is emitted and sets the provided `value`, otherwise it returns `false`. +/// +/// # Safety +/// +/// * `handle` - A valid non-owning [`iox2_config_h_ref`]. +/// * `value` - points to a valid memory location +#[no_mangle] +pub unsafe extern "C" fn iox2_config_defaults_event_notifier_dead_event( + handle: iox2_config_h_ref, + value: *mut c_size_t, +) -> bool { + handle.assert_non_null(); + debug_assert!(!value.is_null()); + + let config = &*handle.as_type(); + if let Some(v) = config + .value + .as_ref() + .value + .defaults + .event + .notifier_dead_event + { + *value = v; + true + } else { + false + } +} + +/// Sets the event id value that is emitted when a notifier is identified as dead. If `value` is `NULL` +/// no event will be emitted, otherwise the provided value will be used. +/// +/// # Safety +/// +/// * `handle` - A valid non-owning [`iox2_config_h_ref`]. +#[no_mangle] +pub unsafe extern "C" fn iox2_config_defaults_event_set_notifier_dead_event( + handle: iox2_config_h_ref, + value: *const c_size_t, +) { + handle.assert_non_null(); + + let config = &mut *handle.as_type(); + + config + .value + .as_mut() + .value + .defaults + .event + .notifier_dead_event = if value.is_null() { None } else { Some(*value) }; +} + /// Returns the maximum amount of supported [`iox2_notifier_h`](crate::api::iox2_notifier_h) /// /// # Safety diff --git a/iceoryx2-ffi/ffi/src/api/service_builder_event.rs b/iceoryx2-ffi/ffi/src/api/service_builder_event.rs index cf596e666..d37d0ee21 100644 --- a/iceoryx2-ffi/ffi/src/api/service_builder_event.rs +++ b/iceoryx2-ffi/ffi/src/api/service_builder_event.rs @@ -46,6 +46,12 @@ pub enum iox2_event_open_or_create_error_e { O_INCOMPATIBLE_MESSAGING_PATTERN, #[CustomString = "incompatible attributes"] O_INCOMPATIBLE_ATTRIBUTES, + #[CustomString = "incompatible notifier_created_event"] + O_INCOMPATIBLE_NOTIFIER_CREATED_EVENT, + #[CustomString = "incompatible notifier_dropped_event"] + O_INCOMPATIBLE_NOTIFIER_DROPPED_EVENT, + #[CustomString = "incompatible notifier_dead_event"] + O_INCOMPATIBLE_NOTIFIER_DEAD_EVENT, #[CustomString = "internal failure"] O_INTERNAL_FAILURE, #[CustomString = "hangs in creation"] @@ -118,6 +124,15 @@ impl IntoCInt for EventOpenError { EventOpenError::IsMarkedForDestruction => { iox2_event_open_or_create_error_e::O_IS_MARKED_FOR_DESTRUCTION } + EventOpenError::IncompatibleNotifierCreatedEvent => { + iox2_event_open_or_create_error_e::O_INCOMPATIBLE_NOTIFIER_CREATED_EVENT + } + EventOpenError::IncompatibleNotifierDroppedEvent => { + iox2_event_open_or_create_error_e::O_INCOMPATIBLE_NOTIFIER_DROPPED_EVENT + } + EventOpenError::IncompatibleNotifierDeadEvent => { + iox2_event_open_or_create_error_e::O_INCOMPATIBLE_NOTIFIER_DEAD_EVENT + } }) as c_int } } @@ -180,6 +195,222 @@ pub unsafe extern "C" fn iox2_event_open_or_create_error_string( error.as_str_literal().as_ptr() as *const c_char } +/// Sets the event id value that shall be emitted if a notifier was identified as dead. +/// +/// # Arguments +/// +/// * `service_builder_handle` - Must be a valid [`iox2_service_builder_event_h_ref`] +/// obtained by [`iox2_service_builder_event`](crate::iox2_service_builder_event). +/// * `value` - the value of the event id that will be emitted. +/// +/// # Safety +/// +/// * `service_builder_handle` must be valid handles +#[no_mangle] +pub unsafe extern "C" fn iox2_service_builder_event_set_notifier_dead_event( + service_builder_handle: iox2_service_builder_event_h_ref, + value: c_size_t, +) { + iox2_service_builder_event_set_notifier_dead_event_impl( + service_builder_handle, + Some(EventId::new(value as _)), + ); +} + +/// Disables event id notification when a notifier was identified as dead. +/// +/// # Arguments +/// +/// * `service_builder_handle` - Must be a valid [`iox2_service_builder_event_h_ref`] +/// obtained by [`iox2_service_builder_event`](crate::iox2_service_builder_event). +/// +/// # Safety +/// +/// * `service_builder_handle` must be valid handles +#[no_mangle] +pub unsafe extern "C" fn iox2_service_builder_event_disable_notifier_dead_event( + service_builder_handle: iox2_service_builder_event_h_ref, +) { + iox2_service_builder_event_set_notifier_dead_event_impl(service_builder_handle, None); +} + +#[no_mangle] +unsafe fn iox2_service_builder_event_set_notifier_dead_event_impl( + service_builder_handle: iox2_service_builder_event_h_ref, + value: Option, +) { + service_builder_handle.assert_non_null(); + + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; + + match service_builder_struct.service_type { + iox2_service_type_e::IPC => { + let service_builder = + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().ipc); + + let service_builder = ManuallyDrop::into_inner(service_builder.event); + service_builder_struct.set(ServiceBuilderUnion::new_ipc_event(match value { + Some(value) => service_builder.notifier_dead_event(value), + None => service_builder.disable_notifier_dead_event(), + })); + } + iox2_service_type_e::LOCAL => { + let service_builder = + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().local); + + let service_builder = ManuallyDrop::into_inner(service_builder.event); + service_builder_struct.set(ServiceBuilderUnion::new_local_event(match value { + Some(value) => service_builder.notifier_dead_event(value), + None => service_builder.disable_notifier_dead_event(), + })); + } + } +} + +/// Sets the event id value that shall be emitted after a notifier was created. +/// +/// # Arguments +/// +/// * `service_builder_handle` - Must be a valid [`iox2_service_builder_event_h_ref`] +/// obtained by [`iox2_service_builder_event`](crate::iox2_service_builder_event). +/// * `value` - the value of the event id that will be emitted. +/// +/// # Safety +/// +/// * `service_builder_handle` must be valid handles +#[no_mangle] +pub unsafe extern "C" fn iox2_service_builder_event_set_notifier_created_event( + service_builder_handle: iox2_service_builder_event_h_ref, + value: c_size_t, +) { + iox2_service_builder_event_set_notifier_created_event_impl( + service_builder_handle, + Some(EventId::new(value as _)), + ); +} + +/// Disables the event id value that shall be emitted after a notifier was created. +/// +/// # Arguments +/// +/// * `service_builder_handle` - Must be a valid [`iox2_service_builder_event_h_ref`] +/// obtained by [`iox2_service_builder_event`](crate::iox2_service_builder_event). +/// +/// # Safety +/// +/// * `service_builder_handle` must be valid handles +#[no_mangle] +pub unsafe extern "C" fn iox2_service_builder_event_disable_notifier_created_event( + service_builder_handle: iox2_service_builder_event_h_ref, +) { + iox2_service_builder_event_set_notifier_created_event_impl(service_builder_handle, None); +} + +#[no_mangle] +unsafe fn iox2_service_builder_event_set_notifier_created_event_impl( + service_builder_handle: iox2_service_builder_event_h_ref, + value: Option, +) { + service_builder_handle.assert_non_null(); + + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; + + match service_builder_struct.service_type { + iox2_service_type_e::IPC => { + let service_builder = + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().ipc); + + let service_builder = ManuallyDrop::into_inner(service_builder.event); + service_builder_struct.set(ServiceBuilderUnion::new_ipc_event(match value { + Some(value) => service_builder.notifier_created_event(value), + None => service_builder.disable_notifier_created_event(), + })); + } + iox2_service_type_e::LOCAL => { + let service_builder = + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().local); + + let service_builder = ManuallyDrop::into_inner(service_builder.event); + service_builder_struct.set(ServiceBuilderUnion::new_local_event(match value { + Some(value) => service_builder.notifier_created_event(value), + None => service_builder.disable_notifier_created_event(), + })); + } + } +} + +/// Sets the event id value that shall be emitted before a notifier is dropped. +/// +/// # Arguments +/// +/// * `service_builder_handle` - Must be a valid [`iox2_service_builder_event_h_ref`] +/// obtained by [`iox2_service_builder_event`](crate::iox2_service_builder_event). +/// * `value` - the value of the event id that will be emitted. +/// +/// # Safety +/// +/// * `service_builder_handle` must be valid handles +#[no_mangle] +pub unsafe extern "C" fn iox2_service_builder_event_set_notifier_dropped_event( + service_builder_handle: iox2_service_builder_event_h_ref, + value: c_size_t, +) { + iox2_service_builder_event_set_notifier_dropped_event_impl( + service_builder_handle, + Some(EventId::new(value as _)), + ); +} + +/// Disables the event id value that shall be emitted before a notifier is dropped. +/// +/// # Arguments +/// +/// * `service_builder_handle` - Must be a valid [`iox2_service_builder_event_h_ref`] +/// obtained by [`iox2_service_builder_event`](crate::iox2_service_builder_event). +/// +/// # Safety +/// +/// * `service_builder_handle` must be valid handles +#[no_mangle] +pub unsafe extern "C" fn iox2_service_builder_event_disable_notifier_dropped_event( + service_builder_handle: iox2_service_builder_event_h_ref, +) { + iox2_service_builder_event_set_notifier_dropped_event_impl(service_builder_handle, None); +} + +#[no_mangle] +unsafe fn iox2_service_builder_event_set_notifier_dropped_event_impl( + service_builder_handle: iox2_service_builder_event_h_ref, + value: Option, +) { + service_builder_handle.assert_non_null(); + + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; + + match service_builder_struct.service_type { + iox2_service_type_e::IPC => { + let service_builder = + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().ipc); + + let service_builder = ManuallyDrop::into_inner(service_builder.event); + service_builder_struct.set(ServiceBuilderUnion::new_ipc_event(match value { + Some(value) => service_builder.notifier_dropped_event(value), + None => service_builder.disable_notifier_dropped_event(), + })); + } + iox2_service_type_e::LOCAL => { + let service_builder = + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().local); + + let service_builder = ManuallyDrop::into_inner(service_builder.event); + service_builder_struct.set(ServiceBuilderUnion::new_local_event(match value { + Some(value) => service_builder.notifier_dropped_event(value), + None => service_builder.disable_notifier_dropped_event(), + })); + } + } +} + /// Sets the max notifiers for the builder /// /// # Arguments diff --git a/iceoryx2-ffi/ffi/src/api/static_config_event.rs b/iceoryx2-ffi/ffi/src/api/static_config_event.rs index 3ff0fda3f..9dc3a3f09 100644 --- a/iceoryx2-ffi/ffi/src/api/static_config_event.rs +++ b/iceoryx2-ffi/ffi/src/api/static_config_event.rs @@ -12,7 +12,7 @@ #![allow(non_camel_case_types)] -use iceoryx2::service::static_config::event::StaticConfig; +use iceoryx2::{prelude::EventId, service::static_config::event::StaticConfig}; #[derive(Clone, Copy)] #[repr(C)] @@ -21,6 +21,12 @@ pub struct iox2_static_config_event_t { pub max_listeners: usize, pub max_nodes: usize, pub event_id_max_value: usize, + pub notifier_dead_event: usize, + pub has_notifier_dead_event: bool, + pub notifier_dropped_event: usize, + pub has_notifier_dropped_event: bool, + pub notifier_created_event: usize, + pub has_notifier_created_event: bool, } impl From<&StaticConfig> for iox2_static_config_event_t { @@ -30,6 +36,21 @@ impl From<&StaticConfig> for iox2_static_config_event_t { max_listeners: c.max_listeners(), max_nodes: c.max_nodes(), event_id_max_value: c.event_id_max_value(), + notifier_dead_event: c + .notifier_dead_event() + .unwrap_or(EventId::new(0)) + .as_value(), + has_notifier_dead_event: c.notifier_dead_event().is_some(), + notifier_dropped_event: c + .notifier_dropped_event() + .unwrap_or(EventId::new(0)) + .as_value(), + has_notifier_dropped_event: c.notifier_dropped_event().is_some(), + notifier_created_event: c + .notifier_created_event() + .unwrap_or(EventId::new(0)) + .as_value(), + has_notifier_created_event: c.notifier_created_event().is_some(), } } } diff --git a/iceoryx2/src/config.rs b/iceoryx2/src/config.rs index 58199e483..35279519c 100644 --- a/iceoryx2/src/config.rs +++ b/iceoryx2/src/config.rs @@ -272,6 +272,12 @@ pub struct Event { pub max_nodes: usize, /// The largest event id supported by the event service pub event_id_max_value: usize, + /// Defines the event id value that is emitted after a new notifier was created. + pub notifier_created_event: Option, + /// Defines the event id value that is emitted before a new notifier is dropped. + pub notifier_dropped_event: Option, + /// Defines the event id value that is emitted if a notifier was identified as dead. + pub notifier_dead_event: Option, } /// Represents the configuration that iceoryx2 will utilize. It is divided into two sections: @@ -333,6 +339,9 @@ impl Default for Config { max_notifiers: 16, max_nodes: 36, event_id_max_value: 4294967295, + notifier_created_event: None, + notifier_dropped_event: None, + notifier_dead_event: None, }, }, } diff --git a/iceoryx2/src/node/mod.rs b/iceoryx2/src/node/mod.rs index c7c25866d..a5d39f930 100644 --- a/iceoryx2/src/node/mod.rs +++ b/iceoryx2/src/node/mod.rs @@ -158,7 +158,7 @@ use crate::{config::Config, service::config_scheme::node_details_config}; use iceoryx2_bb_container::semantic_string::SemanticString; use iceoryx2_bb_elementary::CallbackProgression; use iceoryx2_bb_lock_free::mpmc::container::ContainerHandle; -use iceoryx2_bb_log::{debug, fail, fatal_panic, warn}; +use iceoryx2_bb_log::{debug, fail, fatal_panic, trace, warn}; use iceoryx2_bb_posix::clock::{nanosleep, NanosleepError, Time}; use iceoryx2_bb_posix::process::{Process, ProcessId}; use iceoryx2_bb_posix::signal::SignalHandler; @@ -890,15 +890,15 @@ impl Node { let cleanup_call = |node_state| { if let NodeState::Dead(dead_node) = node_state { let node_id = *dead_node.id(); - warn!(from origin, "Dead node ({:?}) detected", node_id); + debug!(from origin, "Dead node ({:?}) detected", node_id); match dead_node.remove_stale_resources() { Ok(_) => { cleanup_state.cleanups += 1; - debug!(from origin, "The dead node ({:?}) was successfully removed.", node_id) + trace!(from origin, "The dead node ({:?}) was successfully removed.", node_id) } Err(e) => { cleanup_state.failed_cleanups += 1; - warn!(from origin, "Unable to remove dead node {:?} ({:?}).", node_id, e) + trace!(from origin, "Unable to remove dead node {:?} ({:?}).", node_id, e) } } } diff --git a/iceoryx2/src/port/notifier.rs b/iceoryx2/src/port/notifier.rs index 1bb06b277..d4470fe71 100644 --- a/iceoryx2/src/port/notifier.rs +++ b/iceoryx2/src/port/notifier.rs @@ -181,10 +181,18 @@ pub struct Notifier { event_id_max_value: usize, dynamic_notifier_handle: Option, notifier_id: UniqueNotifierId, + on_drop_notification: Option, } impl Drop for Notifier { fn drop(&mut self) { + if let Some(event_id) = self.on_drop_notification { + if let Err(e) = self.notify_with_custom_event_id(event_id) { + warn!(from self, "Unable to send notifier_dropped_event {:?} due to ({:?}).", + event_id, e); + } + } + if let Some(handle) = self.dynamic_notifier_handle { self.listener_connections .service_state @@ -200,6 +208,26 @@ impl Notifier { pub(crate) fn new( service: &Service, default_event_id: EventId, + ) -> Result { + let mut new_self = Self::new_without_auto_event_emission(service, default_event_id)?; + + let static_config = service.__internal_state().static_config.event(); + new_self.on_drop_notification = static_config.notifier_dropped_event.map(EventId::new); + + if let Some(event_id) = static_config.notifier_created_event() { + if let Err(e) = new_self.notify_with_custom_event_id(event_id) { + warn!(from new_self, + "The new notifier was unable to send out the notifier_created_event: {:?} due to ({:?}).", + event_id, e); + } + } + + Ok(new_self) + } + + pub(crate) fn new_without_auto_event_emission( + service: &Service, + default_event_id: EventId, ) -> Result { let msg = "Unable to create Notifier port"; let origin = "Notifier::new()"; @@ -212,6 +240,7 @@ impl Notifier { .event() .listeners; + let static_config = service.__internal_state().static_config.event(); let mut new_self = Self { listener_connections: ListenerConnections::new( listener_list.capacity(), @@ -219,13 +248,10 @@ impl Notifier { ), default_event_id, listener_list_state: unsafe { UnsafeCell::new(listener_list.get_state()) }, - event_id_max_value: service - .__internal_state() - .static_config - .event() - .event_id_max_value, + event_id_max_value: static_config.event_id_max_value, dynamic_notifier_handle: None, notifier_id, + on_drop_notification: None, }; new_self.populate_listener_channels(); diff --git a/iceoryx2/src/service/builder/event.rs b/iceoryx2/src/service/builder/event.rs index 90fc50418..ac56a5b30 100644 --- a/iceoryx2/src/service/builder/event.rs +++ b/iceoryx2/src/service/builder/event.rs @@ -43,6 +43,15 @@ pub enum EventOpenError { IncompatibleAttributes, /// Errors that indicate either an implementation issue or a wrongly configured system. InternalFailure, + /// The event id that is emitted for a newly created [`Notifier`](crate::port::notifier::Notifier) + /// does not fit the required event id. + IncompatibleNotifierCreatedEvent, + /// The event id that is emitted if a [`Notifier`](crate::port::notifier::Notifier) is dropped + /// does not fit the required event id. + IncompatibleNotifierDroppedEvent, + /// The event id that is emitted if a [`Notifier`](crate::port::notifier::Notifier) is + /// identified as dead does not fit the required event id. + IncompatibleNotifierDeadEvent, /// The [`Service`]s creation timeout has passed and it is still not initialized. Can be caused /// by a process that crashed during [`Service`] creation. HangsInCreation, @@ -168,6 +177,9 @@ pub struct Builder { verify_max_listeners: bool, verify_max_nodes: bool, verify_event_id_max_value: bool, + verify_notifier_created_event: bool, + verify_notifier_dropped_event: bool, + verify_notifier_dead_event: bool, } impl Builder { @@ -178,6 +190,9 @@ impl Builder { verify_max_listeners: false, verify_max_nodes: false, verify_event_id_max_value: false, + verify_notifier_dead_event: false, + verify_notifier_created_event: false, + verify_notifier_dropped_event: false, }; new_self.base.service_config.messaging_pattern = MessagingPattern::Event( @@ -232,6 +247,54 @@ impl Builder { self } + /// If the [`Service`] is created it defines the event that shall be emitted by every newly + /// created [`Notifier`](crate::port::notifier::Notifier). + pub fn notifier_created_event(mut self, value: EventId) -> Self { + self.config_details().notifier_created_event = Some(value.as_value()); + self.verify_notifier_created_event = true; + self + } + + /// If the [`Service`] is created it disables the event that shall be emitted by every newly + /// created [`Notifier`](crate::port::notifier::Notifier). + pub fn disable_notifier_created_event(mut self) -> Self { + self.config_details().notifier_created_event = None; + self.verify_notifier_created_event = true; + self + } + + /// If the [`Service`] is created it defines the event that shall be emitted by every + /// [`Notifier`](crate::port::notifier::Notifier) before it is dropped. + pub fn notifier_dropped_event(mut self, value: EventId) -> Self { + self.config_details().notifier_dropped_event = Some(value.as_value()); + self.verify_notifier_dropped_event = true; + self + } + + /// If the [`Service`] is created it disables the event that shall be emitted by every + /// [`Notifier`](crate::port::notifier::Notifier) before it is dropped. + pub fn disable_notifier_dropped_event(mut self) -> Self { + self.config_details().notifier_dropped_event = None; + self.verify_notifier_dropped_event = true; + self + } + + /// If the [`Service`] is created it defines the event that shall be emitted when a + /// [`Notifier`](crate::port::notifier::Notifier) is identified as dead. + pub fn notifier_dead_event(mut self, value: EventId) -> Self { + self.config_details().notifier_dead_event = Some(value.as_value()); + self.verify_notifier_dead_event = true; + self + } + + /// If the [`Service`] is created it disables the event that shall be emitted when a + /// [`Notifier`](crate::port::notifier::Notifier) is identified as dead. + pub fn disable_notifier_dead_event(mut self) -> Self { + self.config_details().notifier_dead_event = None; + self.verify_notifier_dead_event = true; + self + } + /// If the [`Service`] exists, it will be opened otherwise a new [`Service`] will be /// created. pub fn open_or_create(self) -> Result, EventOpenOrCreateError> { @@ -522,6 +585,30 @@ impl Builder { msg, existing_settings.max_nodes, required_settings.max_nodes); } + if self.verify_notifier_created_event + && existing_settings.notifier_created_event != required_settings.notifier_created_event + { + fail!(from self, with EventOpenError::IncompatibleNotifierCreatedEvent, + "{} since the notifier_created_event id is {:?} but the value {:?} is required.", + msg, existing_settings.notifier_created_event, required_settings.notifier_created_event); + } + + if self.verify_notifier_dropped_event + && existing_settings.notifier_dropped_event != required_settings.notifier_dropped_event + { + fail!(from self, with EventOpenError::IncompatibleNotifierDroppedEvent, + "{} since the notifier_dropped_event id is {:?} but the value {:?} is required.", + msg, existing_settings.notifier_dropped_event, required_settings.notifier_dropped_event); + } + + if self.verify_notifier_dead_event + && existing_settings.notifier_dead_event != required_settings.notifier_dead_event + { + fail!(from self, with EventOpenError::IncompatibleNotifierDeadEvent, + "{} since the notifier_dead_event id is {:?} but the value {:?} is required.", + msg, existing_settings.notifier_dead_event, required_settings.notifier_dead_event); + } + Ok(*existing_settings) } } diff --git a/iceoryx2/src/service/mod.rs b/iceoryx2/src/service/mod.rs index d97b78ce5..92b9c657f 100644 --- a/iceoryx2/src/service/mod.rs +++ b/iceoryx2/src/service/mod.rs @@ -54,6 +54,9 @@ //! .max_notifiers(12) //! .max_listeners(2) //! .event_id_max_value(32) +//! .notifier_created_event(EventId::new(999)) +//! .notifier_dropped_event(EventId::new(0)) +//! .notifier_dead_event(EventId::new(2000)) //! // if the service already exists, open it, otherwise create it //! .open_or_create()?; //! @@ -329,23 +332,99 @@ impl Drop for ServiceState { } pub(crate) mod internal { + use builder::event::EventOpenError; use config_scheme::static_config_storage_config; use dynamic_config::{PortCleanupAction, RemoveDeadNodeResult}; + use port_factory::PortFactory; use crate::{ - node::NodeId, + node::{NodeBuilder, NodeId}, port::{ listener::remove_connection_of_listener, + notifier::Notifier, port_identifiers::UniquePortId, publisher::{ remove_data_segment_of_publisher, remove_publisher_from_all_connections, remove_subscriber_from_all_connections, }, }, + prelude::EventId, }; use super::*; + fn send_dead_node_signal(service_id: &ServiceId, config: &config::Config) { + let origin = "send_dead_node_signal()"; + + let service_details = match details::(config, &service_id.0.into()) { + Ok(Some(service_details)) => service_details, + Ok(None) => return, + Err(e) => { + warn!(from origin, + "Unable to acquire service details to emit dead node signal to waiting listeners for the service id {:?} due to ({:?})", + service_id, e); + return; + } + }; + + let service_name = service_details.static_details.name(); + + let mut config = config.clone(); + config.global.node.cleanup_dead_nodes_on_creation = false; + config.global.node.cleanup_dead_nodes_on_destruction = false; + + let node = match NodeBuilder::new().config(&config).create::() { + Ok(node) => node, + Err(e) => { + warn!(from origin, + "Unable to create node to emit dead node signal to waiting listeners on the service {} due to ({:?}).", + service_name, e); + return; + } + }; + + let service = match node.service_builder(service_name).event().open() { + Ok(service) => service, + Err(EventOpenError::DoesNotExist) => return, + Err(e) => { + warn!(from origin, + "Unable to open event service to emit dead node signal to waiting listeners on the service {} due to ({:?}).", + service_name, e); + return; + } + }; + + if service.dynamic_config().number_of_listeners() == 0 { + return; + } + + let event_id = match service.static_config().notifier_dead_event { + Some(event_id) => event_id, + None => return, + }; + + let notifier = match Notifier::new_without_auto_event_emission( + &service.service, + EventId::new(0), + ) { + Ok(notifier) => notifier, + Err(e) => { + warn!(from origin, + "Unable to create notifier to send dead node signal to waiting listeners on the service {} due to ({:?})", + service_name, e); + return; + } + }; + + if let Err(e) = notifier.notify_with_custom_event_id(EventId::new(event_id)) { + warn!(from origin, + "Unable to send dead node signal to waiting listeners on service {} due to ({:?})", + service_name, e); + } + + trace!(from origin, "Send dead node signal on service {}.", service_name); + } + pub(crate) trait ServiceInternal { fn __internal_from_state(state: ServiceState) -> S; @@ -375,6 +454,7 @@ pub(crate) mod internal { } }; + let mut number_of_dead_node_notifications = 0; let cleanup_port_resources = |port_id| { match port_id { UniquePortId::Publisher(ref id) => { @@ -399,7 +479,9 @@ pub(crate) mod internal { return PortCleanupAction::SkipPort; } } - UniquePortId::Notifier(_) => (), + UniquePortId::Notifier(_) => { + number_of_dead_node_notifications += 1; + } UniquePortId::Listener(ref id) => { if let Err(e) = unsafe { remove_connection_of_listener::(id, config) } { debug!(from origin, "Failed to remove the listeners ({:?}) connection ({:?}).", id, e); @@ -440,6 +522,8 @@ pub(crate) mod internal { e); } } + } else if number_of_dead_node_notifications != 0 { + send_dead_node_signal::(service_id, config); } Ok(()) diff --git a/iceoryx2/src/service/static_config/event.rs b/iceoryx2/src/service/static_config/event.rs index 6c6e39e91..ef426954e 100644 --- a/iceoryx2/src/service/static_config/event.rs +++ b/iceoryx2/src/service/static_config/event.rs @@ -24,11 +24,14 @@ //! println!("max listeners: {:?}", event.static_config().max_listeners()); //! println!("max notifiers: {:?}", event.static_config().max_notifiers()); //! println!("event id max value: {:?}", event.static_config().event_id_max_value()); +//! println!("notifier created event: {:?}", event.static_config().notifier_created_event()); +//! println!("notifier dropped event: {:?}", event.static_config().notifier_dropped_event()); +//! println!("notifier dead event: {:?}", event.static_config().notifier_dead_event()); //! //! # Ok(()) //! # } //! ``` -use crate::config; +use crate::{config, prelude::EventId}; use serde::{Deserialize, Serialize}; /// The static configuration of an [`MessagingPattern::Event`](crate::service::messaging_pattern::MessagingPattern::Event) @@ -40,6 +43,9 @@ pub struct StaticConfig { pub(crate) max_listeners: usize, pub(crate) max_nodes: usize, pub(crate) event_id_max_value: usize, + pub(crate) notifier_created_event: Option, + pub(crate) notifier_dropped_event: Option, + pub(crate) notifier_dead_event: Option, } impl StaticConfig { @@ -49,6 +55,9 @@ impl StaticConfig { max_listeners: config.defaults.event.max_listeners, max_nodes: config.defaults.event.max_nodes, event_id_max_value: config.defaults.event.event_id_max_value, + notifier_created_event: config.defaults.event.notifier_created_event, + notifier_dropped_event: config.defaults.event.notifier_dropped_event, + notifier_dead_event: config.defaults.event.notifier_dead_event, } } @@ -72,4 +81,19 @@ impl StaticConfig { pub fn event_id_max_value(&self) -> usize { self.event_id_max_value } + + /// Returns the emitted [`EventId`] when a new notifier is created. + pub fn notifier_created_event(&self) -> Option { + self.notifier_created_event.map(EventId::new) + } + + /// Returns the emitted [`EventId`] when a notifier is dropped. + pub fn notifier_dropped_event(&self) -> Option { + self.notifier_dropped_event.map(EventId::new) + } + + /// Returns the emitted [`EventId`] when a notifier is identified as dead. + pub fn notifier_dead_event(&self) -> Option { + self.notifier_dead_event.map(EventId::new) + } } diff --git a/iceoryx2/tests/node_death_tests.rs b/iceoryx2/tests/node_death_tests.rs index 452ef4adf..bd40422fc 100644 --- a/iceoryx2/tests/node_death_tests.rs +++ b/iceoryx2/tests/node_death_tests.rs @@ -20,7 +20,6 @@ mod node_death_tests { use iceoryx2::prelude::*; use iceoryx2::service::Service; use iceoryx2::testing::*; - use iceoryx2_bb_log::{set_log_level, LogLevel}; use iceoryx2_bb_posix::unique_system_id::UniqueSystemId; use iceoryx2_bb_testing::watchdog::Watchdog; use iceoryx2_bb_testing::{assert_that, test_fail}; @@ -191,7 +190,6 @@ mod node_death_tests { #[test] fn dead_node_is_removed_from_event_service() { - set_log_level(LogLevel::Error); let _watchdog = Watchdog::new(); const NUMBER_OF_BAD_NODES: usize = 3; const NUMBER_OF_GOOD_NODES: usize = 4; @@ -272,6 +270,49 @@ mod node_death_tests { } } + #[test] + fn notifier_of_dead_node_emits_death_event_when_configured() { + let _watchdog = Watchdog::new(); + let mut config = generate_isolated_config(); + let service_name = generate_service_name(); + let notifier_dead_event = EventId::new(8); + config.global.node.cleanup_dead_nodes_on_creation = false; + + let mut dead_node = S::create_test_node(&config).node; + let node = NodeBuilder::new() + .config(&config) + .create::() + .unwrap(); + + let dead_service = dead_node + .service_builder(&service_name) + .event() + .notifier_dead_event(notifier_dead_event) + .notifier_created_event(EventId::new(0)) + .notifier_dropped_event(EventId::new(0)) + .create() + .unwrap(); + let dead_notifier = dead_service.notifier_builder().create().unwrap(); + + let service = node.service_builder(&service_name).event().open().unwrap(); + let listener = service.listener_builder().create().unwrap(); + + S::staged_death(&mut dead_node); + core::mem::forget(dead_notifier); + + assert_that!(Node::::cleanup_dead_nodes(&config), eq CleanupState { cleanups: 1, failed_cleanups: 0}); + + let mut received_events = 0; + listener + .try_wait_all(|event| { + assert_that!(event, eq notifier_dead_event); + received_events += 1; + }) + .unwrap(); + + assert_that!(received_events, eq 1); + } + #[test] fn event_service_is_removed_when_last_node_dies() { let service_name = generate_service_name(); diff --git a/iceoryx2/tests/service_event_tests.rs b/iceoryx2/tests/service_event_tests.rs index 4dfabcc0a..f990cb6b5 100644 --- a/iceoryx2/tests/service_event_tests.rs +++ b/iceoryx2/tests/service_event_tests.rs @@ -283,16 +283,25 @@ mod service_event { .max_nodes(7) .max_notifiers(4) .max_listeners(5) + .notifier_dead_event(EventId::new(8)) + .notifier_dropped_event(EventId::new(9)) + .notifier_created_event(EventId::new(10)) .create() .unwrap(); assert_that!(sut.static_config().max_nodes(), eq 7); assert_that!(sut.static_config().max_notifiers(), eq 4); assert_that!(sut.static_config().max_listeners(), eq 5); + assert_that!(sut.static_config().notifier_dead_event(), eq Some(EventId::new(8))); + assert_that!(sut.static_config().notifier_dropped_event(), eq Some(EventId::new(9))); + assert_that!(sut.static_config().notifier_created_event(), eq Some(EventId::new(10))); let sut2 = node.service_builder(&service_name).event().open().unwrap(); assert_that!(sut2.static_config().max_nodes(), eq 7); assert_that!(sut2.static_config().max_notifiers(), eq 4); assert_that!(sut2.static_config().max_listeners(), eq 5); + assert_that!(sut2.static_config().notifier_dead_event(), eq Some(EventId::new(8))); + assert_that!(sut2.static_config().notifier_dropped_event(), eq Some(EventId::new(9))); + assert_that!(sut2.static_config().notifier_created_event(), eq Some(EventId::new(10))); } #[test] @@ -386,6 +395,78 @@ mod service_event { assert_that!(received_events, eq 1); } + #[test] + fn notifier_emits_create_and_dropped_event_id() { + let service_name = generate_name(); + let config = generate_isolated_config(); + let node = NodeBuilder::new().config(&config).create::().unwrap(); + + let sut = node + .service_builder(&service_name) + .event() + .disable_notifier_created_event() + .disable_notifier_dropped_event() + .create() + .unwrap(); + + let sut2 = node.service_builder(&service_name).event().open().unwrap(); + + let listener = sut.listener_builder().create().unwrap(); + let notifier = sut2.notifier_builder().create().unwrap(); + + let mut received_events = 0; + for _ in listener.try_wait_one().unwrap().iter() { + received_events += 1; + } + assert_that!(received_events, eq 0); + + drop(notifier); + + let mut received_events = 0; + for _ in listener.try_wait_one().unwrap().iter() { + received_events += 1; + } + assert_that!(received_events, eq 0); + } + + #[test] + fn notifier_emits_nothing_when_no_events_are_configured() { + let service_name = generate_name(); + let config = generate_isolated_config(); + let node = NodeBuilder::new().config(&config).create::().unwrap(); + let notifier_created = EventId::new(31); + let notifier_dropped = EventId::new(28); + + let sut = node + .service_builder(&service_name) + .event() + .notifier_created_event(notifier_created) + .notifier_dropped_event(notifier_dropped) + .create() + .unwrap(); + + let sut2 = node.service_builder(&service_name).event().open().unwrap(); + + let listener = sut.listener_builder().create().unwrap(); + let notifier = sut2.notifier_builder().create().unwrap(); + + let mut received_events = 0; + for event in listener.try_wait_one().unwrap().iter() { + assert_that!(*event, eq notifier_created); + received_events += 1; + } + assert_that!(received_events, eq 1); + + drop(notifier); + + let mut received_events = 0; + for event in listener.try_wait_one().unwrap().iter() { + assert_that!(*event, eq notifier_dropped); + received_events += 1; + } + assert_that!(received_events, eq 1); + } + #[test] fn communication_with_max_notifiers_and_listeners_single_notification() { const MAX_LISTENERS: usize = 4;