From 1ebb726432236e5eb0fd5dc7fb123c5802da757a Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Tue, 8 Oct 2024 18:00:43 +0200 Subject: [PATCH 1/8] [#390] Add C++ example and implement notifier --- config/iceoryx2.toml | 2 +- examples/README.md | 24 ++++---- examples/cxx/CMakeLists.txt | 1 + examples/cxx/event_multiplexing/BUILD.bazel | 35 +++++++++++ .../cxx/event_multiplexing/CMakeLists.txt | 22 +++++++ examples/cxx/event_multiplexing/README.md | 56 ++++++++++++++++++ .../cxx/event_multiplexing/src/notifier.cpp | 59 +++++++++++++++++++ examples/cxx/event_multiplexing/src/wait.cpp | 43 ++++++++++++++ 8 files changed, 229 insertions(+), 13 deletions(-) create mode 100644 examples/cxx/event_multiplexing/BUILD.bazel create mode 100644 examples/cxx/event_multiplexing/CMakeLists.txt create mode 100644 examples/cxx/event_multiplexing/README.md create mode 100644 examples/cxx/event_multiplexing/src/notifier.cpp create mode 100644 examples/cxx/event_multiplexing/src/wait.cpp diff --git a/config/iceoryx2.toml b/config/iceoryx2.toml index 883e81909..7c66d1335 100644 --- a/config/iceoryx2.toml +++ b/config/iceoryx2.toml @@ -37,4 +37,4 @@ subscriber-expired-connection-buffer = 128 max-listeners = 16 max-notifiers = 16 max-nodes = 36 -event-id-max-value = 32 +event-id-max-value = 4294967296 diff --git a/examples/README.md b/examples/README.md index ca8f65c39..87831fca8 100644 --- a/examples/README.md +++ b/examples/README.md @@ -70,15 +70,15 @@ These types are demonstrated in the complex data types example. ## Overview -| Name | Language | Description | -| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| complex data types | [C++](cxx/complex_data_types) [Rust](rust/complex_data_types) | Send zero-copy compatible versions of `Vec` and `String`. Introduces `PlacementDefault` trait for large data types to perform an in place initialization where otherwise a stack overflow would be encountered. | -| discovery | [C](c/discovery) [C++](cxx/discovery) [Rust](rust/discovery) | List all available services in a system. | -| docker | [all](rust/docker) | Communicate between different docker containers and the host. | -| domains | [C](c/domains) [C++](cxx/domains) [Rust](rust/domains) | Establish separate domains that operate independently from one another. | -| event | [C](c/event) [C++](cxx/event) [Rust](rust/event) | Push notifications - send event signals to wakeup processes that are waiting for them. | -| event multiplexing | [Rust](rust/event_multiplexing) | Wait on multiple listeners or sockets with a single call. The WaitSet demultiplexes incoming events and notifies the user. | -| publish subscribe | [C](c/publish_subscribe) [C++](cxx/publish_subscribe) [Rust](rust/publish_subscribe) | Communication between multiple processes with a [publish subscribe messaging pattern](https://en.wikipedia.org/wiki/Publish–subscribe_pattern). | -| publish subscribe dynamic data | [Rust](rust/publish_subscribe_dynamic_data) | Communication between multiple processes with a [publish subscribe messaging pattern](https://en.wikipedia.org/wiki/Publish–subscribe_pattern) and payload data that consists of a slice of shared memory compatible types. | -| publish subscribe with user header | [C](c/publish_subscribe_with_user_header) [C++](cxx/publish_subscribe_with_user_header) [Rust](rust/publish_subscribe_with_user_header) | Add a user header to the payload (samples) to transfer additional information. | -| service attributes | [Rust](rust/service_attributes) | Creates a service with custom attributes that are available to every endpoint. If the attributes are not compatible the service will not open. | +| Name | Language | Description | +| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| complex data types | [Rust](rust/complex_data_types) | Send zero-copy compatible versions of `Vec` and `String`. Introduces `PlacementDefault` trait for large data types to perform an in place initialization where otherwise a stack overflow would be encountered. | +| discovery | [C](c/discovery) [C++](cxx/discovery) [Rust](rust/discovery) | List all available services in a system. | +| docker | [all](rust/docker) | Communicate between different docker containers and the host. | +| domains | [C](c/domains) [C++](cxx/domains) [Rust](rust/domains) | Establish separate domains that operate independently from one another. | +| event | [C](c/event) [C++](cxx/event) [Rust](rust/event) | Push notifications - send event signals to wakeup processes that are waiting for them. | +| event multiplexing | [C++](rust/event_multiplexing) [Rust](rust/event_multiplexing) | Wait on multiple listeners or sockets with a single call. The WaitSet demultiplexes incoming events and notifies the user. | +| publish subscribe | [C](c/publish_subscribe) [C++](cxx/publish_subscribe) [Rust](rust/publish_subscribe) | Communication between multiple processes with a [publish subscribe messaging pattern](https://en.wikipedia.org/wiki/Publish–subscribe_pattern). | +| publish subscribe dynamic data | [Rust](rust/publish_subscribe_dynamic_data) | Communication between multiple processes with a [publish subscribe messaging pattern](https://en.wikipedia.org/wiki/Publish–subscribe_pattern) and payload data that has a dynamic size. | +| publish subscribe with user header | [C](c/publish_subscribe_with_user_header) [C++](cxx/publish_subscribe_with_user_header) [Rust](rust/publish_subscribe_with_user_header) | Add a user header to the payload (samples) to transfer additional information. | +| service attributes | [Rust](rust/service_attributes) | Creates a service with custom attributes that are available to every endpoint. If the attributes are not compatible the service will not open. | diff --git a/examples/cxx/CMakeLists.txt b/examples/cxx/CMakeLists.txt index 80468fd26..01d57e7ed 100644 --- a/examples/cxx/CMakeLists.txt +++ b/examples/cxx/CMakeLists.txt @@ -20,6 +20,7 @@ add_subdirectory(complex_data_types) add_subdirectory(discovery) add_subdirectory(domains) add_subdirectory(event) +add_subdirectory(event_multiplexing) add_subdirectory(publish_subscribe) add_subdirectory(publish_subscribe_dynamic_data) add_subdirectory(publish_subscribe_with_user_header) diff --git a/examples/cxx/event_multiplexing/BUILD.bazel b/examples/cxx/event_multiplexing/BUILD.bazel new file mode 100644 index 000000000..02eabfb34 --- /dev/null +++ b/examples/cxx/event_multiplexing/BUILD.bazel @@ -0,0 +1,35 @@ +# Copyright (c) 2024 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 Software License 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +# which is available at https://opensource.org/licenses/MIT. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT + +load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") + +cc_binary( + name = "example_cxx_event_multiplexing_wait", + srcs = [ + "src/wait.cpp", + ], + deps = [ + "@iceoryx//:iceoryx_hoofs", + "//:iceoryx2-cxx", + ], +) + +cc_binary( + name = "example_cxx_event_multiplexing_notifier", + srcs = [ + "src/notifier.cpp", + ], + deps = [ + "@iceoryx//:iceoryx_hoofs", + "//:iceoryx2-cxx", + ], +) diff --git a/examples/cxx/event_multiplexing/CMakeLists.txt b/examples/cxx/event_multiplexing/CMakeLists.txt new file mode 100644 index 000000000..e9cd398d4 --- /dev/null +++ b/examples/cxx/event_multiplexing/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (c) 2024 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 Software License 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +# which is available at https://opensource.org/licenses/MIT. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT + +cmake_minimum_required(VERSION 3.22) +project(example_cxx_event_multiplexing LANGUAGES CXX) + +find_package(iceoryx2-cxx 0.4.1 REQUIRED) + +add_executable(example_cxx_event_multiplexing_wait src/wait.cpp) +target_link_libraries(example_cxx_event_multiplexing_wait iceoryx2-cxx::static-lib-cxx) + +add_executable(example_cxx_event_multiplexing_notifier src/notifier.cpp) +target_link_libraries(example_cxx_event_multiplexing_notifier iceoryx2-cxx::static-lib-cxx) diff --git a/examples/cxx/event_multiplexing/README.md b/examples/cxx/event_multiplexing/README.md new file mode 100644 index 000000000..f1abdf2ae --- /dev/null +++ b/examples/cxx/event_multiplexing/README.md @@ -0,0 +1,56 @@ +# Event + +Before proceeding, all dependencies need to be installed. You can find +instructions in the [C++ Examples Readme](../README.md). + +## Running The Example + +This example demonstrates iceoryx2's event multiplexing mechanism, +called the `WaitSet`. It allows waiting, with a single call, on +multiple `Listener` ports as well as external file descriptor-based +events such as `sockets`. + +In this setup, the `wait` process monitors an arbitrary number of +services, which the user can specify via the command line option `-s`. +The `notifier` can define the service to which it will send event +notifications using the `-s` option and specify the event ID with +the `-e` option. + +In the example below, we are waiting for events on the services `fuu` and +`bar`. Service `fuu` is notified with event ID `123`, and service `bar` is +notified with event ID `456`. + +### Terminal 1 + +```sh +cargo run --example event_multiplexing_wait -- -s "fuu" -s "bar" +``` + +### Terminal 2 + +```sh +cargo run --example event_multiplexing_notifier -- -s "fuu" -e 123 +``` + +### Terminal 3 + +```sh +cargo run --example event_multiplexing_notifier -- -s "bar" -e 456 +``` + +Feel free to instantiate multiple notifiers for the same service with the same +or different event id's. Or to for different services. + +## Technical Details + +The `WaitSet` utilizes `epoll`, `select`, or other event-multiplexing +mechanisms. Before the `WaitSet` can monitor a specific event, it must first be +attached using `WaitSet::attach()`, which returns a RAII `Guard`. This `Guard` +automatically detaches the attachment when it goes out of scope. + +The `WaitSet::**_wait()` calls require a closure that is invoked for each +triggered attachment and provides the `AttachmentId`. The user can either use +`AttachmentId::originates_from($ATTACHED_OBJECT$)` to identify the object +associated with the `AttachmentId`, or set up a +`HashMap::>` to quickly access the +corresponding object. diff --git a/examples/cxx/event_multiplexing/src/notifier.cpp b/examples/cxx/event_multiplexing/src/notifier.cpp new file mode 100644 index 000000000..2e8e2d28e --- /dev/null +++ b/examples/cxx/event_multiplexing/src/notifier.cpp @@ -0,0 +1,59 @@ +// Copyright (c) 2024 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 Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#include + +#include "iox/cli_definition.hpp" +#include "iox/duration.hpp" +#include "iox2/event_id.hpp" +#include "iox2/node.hpp" +#include "iox2/service_name.hpp" +#include "iox2/service_type.hpp" + +constexpr iox::units::Duration CYCLE_TIME = iox::units::Duration::fromSeconds(1); + +// NOLINTBEGIN +struct Args { + IOX_CLI_DEFINITION(Args); + IOX_CLI_OPTIONAL(iox::string<256>, service, { "my_funky_service" }, 's', "service", "The name of the service."); + IOX_CLI_OPTIONAL( + uint64_t, event_id, { 0 }, 'e', "event_id", "The event id that shall be used to trigger the service."); +}; +// NOLINTEND + + +auto main(int argc, char** argv) -> int { + using namespace iox2; + auto args = Args::parse(argc, argv, "Notifier of the event multiplexing example."); + + auto event_id = EventId(args.event_id()); + auto service_name = ServiceName::create(args.service().c_str()).expect("valid service name"); + + auto node = NodeBuilder().create().expect("successful node creation"); + + auto service = + node.service_builder(service_name).event().open_or_create().expect("successful service creation/opening"); + auto max_event_id = service.static_config().event_id_max_value(); + + auto notifier = service.notifier_builder().create().expect("successful notifier creation"); + + while (node.wait(CYCLE_TIME).has_value()) { + notifier.notify_with_custom_event_id(event_id).expect("notification"); + + std::cout << "[service: \"" << service_name.to_string().c_str() << "\"] Trigger event with id " << event_id + << "..." << std::endl; + } + + std::cout << "exit" << std::endl; + + return 0; +} diff --git a/examples/cxx/event_multiplexing/src/wait.cpp b/examples/cxx/event_multiplexing/src/wait.cpp new file mode 100644 index 000000000..80349c674 --- /dev/null +++ b/examples/cxx/event_multiplexing/src/wait.cpp @@ -0,0 +1,43 @@ +// Copyright (c) 2024 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 Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#include "iox/duration.hpp" +#include "iox2/node.hpp" +#include "iox2/service_name.hpp" +#include "iox2/service_type.hpp" + +#include + +constexpr iox::units::Duration CYCLE_TIME = iox::units::Duration::fromSeconds(1); + +auto main() -> int { + using namespace iox2; + auto node = NodeBuilder().create().expect("successful node creation"); + + auto service = node.service_builder(ServiceName::create("MyEventName").expect("valid service name")) + .event() + .open_or_create() + .expect("successful service creation/opening"); + + auto listener = service.listener_builder().create().expect("successful listener creation"); + + while (node.wait(iox::units::Duration::zero()).has_value()) { + listener.timed_wait_one(CYCLE_TIME).and_then([](auto maybe_event_id) { + maybe_event_id.and_then( + [](auto event_id) { std::cout << "event was triggered with id: " << event_id << std::endl; }); + }); + } + + std::cout << "exit" << std::endl; + + return 0; +} From 61c489df66f96cdfa64dcb18a927b5ef705b02ad Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Tue, 8 Oct 2024 19:51:04 +0200 Subject: [PATCH 2/8] [#390] First wait C++ version --- .../cxx/event_multiplexing/src/notifier.cpp | 6 +- examples/cxx/event_multiplexing/src/wait.cpp | 70 +++++++++++++++---- 2 files changed, 58 insertions(+), 18 deletions(-) diff --git a/examples/cxx/event_multiplexing/src/notifier.cpp b/examples/cxx/event_multiplexing/src/notifier.cpp index 2e8e2d28e..37de959f4 100644 --- a/examples/cxx/event_multiplexing/src/notifier.cpp +++ b/examples/cxx/event_multiplexing/src/notifier.cpp @@ -24,13 +24,11 @@ constexpr iox::units::Duration CYCLE_TIME = iox::units::Duration::fromSeconds(1) // NOLINTBEGIN struct Args { IOX_CLI_DEFINITION(Args); - IOX_CLI_OPTIONAL(iox::string<256>, service, { "my_funky_service" }, 's', "service", "The name of the service."); - IOX_CLI_OPTIONAL( - uint64_t, event_id, { 0 }, 'e', "event_id", "The event id that shall be used to trigger the service."); + IOX_CLI_OPTIONAL(iox::string<64>, service, { "fuu" }, 's', "service", "The name of the service."); + IOX_CLI_OPTIONAL(uint64_t, event_id, 0, 'e', "event_id", "The event id that shall be used to trigger the service."); }; // NOLINTEND - auto main(int argc, char** argv) -> int { using namespace iox2; auto args = Args::parse(argc, argv, "Notifier of the event multiplexing example."); diff --git a/examples/cxx/event_multiplexing/src/wait.cpp b/examples/cxx/event_multiplexing/src/wait.cpp index 80349c674..07d378c30 100644 --- a/examples/cxx/event_multiplexing/src/wait.cpp +++ b/examples/cxx/event_multiplexing/src/wait.cpp @@ -10,32 +10,74 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT +#include +#include + +#include "iox/cli_definition.hpp" #include "iox/duration.hpp" +#include "iox/string.hpp" +#include "iox/vector.hpp" #include "iox2/node.hpp" #include "iox2/service_name.hpp" #include "iox2/service_type.hpp" - -#include +#include "iox2/waitset.hpp" constexpr iox::units::Duration CYCLE_TIME = iox::units::Duration::fromSeconds(1); -auto main() -> int { +// NOLINTBEGIN +struct Args { + IOX_CLI_DEFINITION(Args); + IOX_CLI_OPTIONAL(iox::string<64>, service1, { "fuu" }, 's', "service1", "The name of service 1."); + IOX_CLI_OPTIONAL(iox::string<64>, service2, { "bar" }, 't', "service2", "The name of service 2."); + IOX_CLI_OPTIONAL(uint64_t, event_id, 0, 'e', "event_id", "The event id that shall be used to trigger the service."); +}; +// NOLINTEND + + +auto main(int argc, char** argv) -> int { using namespace iox2; + auto args = Args::parse(argc, argv, "Notifier of the event multiplexing example."); + + auto event_id = EventId(args.event_id()); + auto service_name_1 = ServiceName::create(args.service1().c_str()).expect("valid service name"); + auto service_name_2 = ServiceName::create(args.service2().c_str()).expect("valid service name"); + auto node = NodeBuilder().create().expect("successful node creation"); - auto service = node.service_builder(ServiceName::create("MyEventName").expect("valid service name")) - .event() - .open_or_create() - .expect("successful service creation/opening"); + auto service_1 = + node.service_builder(service_name_1).event().open_or_create().expect("successful service creation/opening"); + auto service_2 = + node.service_builder(service_name_2).event().open_or_create().expect("successful service creation/opening"); + + auto waitset = WaitSetBuilder().create().expect(""); + auto listener_1 = service_1.listener_builder().create().expect("successful listener creation"); + auto listener_2 = service_2.listener_builder().create().expect("successful listener creation"); + + iox::vector, 2> guards; + + guards.emplace_back(waitset.attach_notification(listener_1).expect("")); + guards.emplace_back(waitset.attach_notification(listener_2).expect("")); + + std::map, std::pair>> listeners; + + listeners.emplace(std::make_pair(WaitSetAttachmentId::from_guard(guards[0]), + std::make_pair(service_name_1, std::move(listener_1)))); + listeners.emplace(std::make_pair(WaitSetAttachmentId::from_guard(guards[1]), + std::make_pair(service_name_2, std::move(listener_2)))); + + auto on_event = [&](WaitSetAttachmentId attachment_id) { + if (auto l = listeners.find(attachment_id); l != listeners.end()) { + std::cout << "Received trigger from \"" << l->second.first.to_string().c_str() << "\"" << std::endl; - auto listener = service.listener_builder().create().expect("successful listener creation"); + auto& listener = l->second.second; + listener.try_wait_one().expect("").and_then([](auto event_id) { std::cout << " " << event_id; }); + std::cout << std::endl; + } + }; - while (node.wait(iox::units::Duration::zero()).has_value()) { - listener.timed_wait_one(CYCLE_TIME).and_then([](auto maybe_event_id) { - maybe_event_id.and_then( - [](auto event_id) { std::cout << "event was triggered with id: " << event_id << std::endl; }); - }); - } + std::cout << "Waiting on the following services: " << service_name_1.to_string().c_str() << ", " + << service_name_2.to_string().c_str() << std::endl; + waitset.wait_and_process(on_event).expect(""); std::cout << "exit" << std::endl; From 0d145b0e3fc9cd0a8b591d1fa56ab47762a50001 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Wed, 9 Oct 2024 11:06:00 +0200 Subject: [PATCH 3/8] [#390] Cleanup and finalize C++ waitset example --- examples/cxx/event_multiplexing/README.md | 18 +++++++------- .../cxx/event_multiplexing/src/notifier.cpp | 4 ++-- examples/cxx/event_multiplexing/src/wait.cpp | 24 +++++++++++++------ examples/rust/event_multiplexing/README.md | 4 ++-- 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/examples/cxx/event_multiplexing/README.md b/examples/cxx/event_multiplexing/README.md index f1abdf2ae..c34c0906f 100644 --- a/examples/cxx/event_multiplexing/README.md +++ b/examples/cxx/event_multiplexing/README.md @@ -10,8 +10,8 @@ called the `WaitSet`. It allows waiting, with a single call, on multiple `Listener` ports as well as external file descriptor-based events such as `sockets`. -In this setup, the `wait` process monitors an arbitrary number of -services, which the user can specify via the command line option `-s`. +In this setup, the `wait` process monitors two services, which the +user can specify via the command line option `-s` and `-t`. The `notifier` can define the service to which it will send event notifications using the `-s` option and specify the event ID with the `-e` option. @@ -23,19 +23,19 @@ notified with event ID `456`. ### Terminal 1 ```sh -cargo run --example event_multiplexing_wait -- -s "fuu" -s "bar" +./target/ffi/build/examples/cxx/event_multiplexing/example_cxx_event_multiplexing_wait -s fuu -t bar ``` ### Terminal 2 ```sh -cargo run --example event_multiplexing_notifier -- -s "fuu" -e 123 +./target/ffi/build/examples/cxx/event_multiplexing/example_cxx_event_multiplexing_notifier -s "fuu" -e 123 ``` ### Terminal 3 ```sh -cargo run --example event_multiplexing_notifier -- -s "bar" -e 456 +./target/ffi/build/examples/cxx/event_multiplexing/example_cxx_event_multiplexing_notifier -s "bar" -e 456 ``` Feel free to instantiate multiple notifiers for the same service with the same @@ -45,12 +45,12 @@ or different event id's. Or to for different services. The `WaitSet` utilizes `epoll`, `select`, or other event-multiplexing mechanisms. Before the `WaitSet` can monitor a specific event, it must first be -attached using `WaitSet::attach()`, which returns a RAII `Guard`. This `Guard` +attached using `WaitSet::attach_notification()`, which returns a RAII `Guard`. This `Guard` automatically detaches the attachment when it goes out of scope. -The `WaitSet::**_wait()` calls require a closure that is invoked for each +The `WaitSet::wait_and_process()` call requires a closure that is invoked for each triggered attachment and provides the `AttachmentId`. The user can either use -`AttachmentId::originates_from($ATTACHED_OBJECT$)` to identify the object +`AttachmentId::has_event_from($ATTACHED_OBJECT$)` to identify the object associated with the `AttachmentId`, or set up a -`HashMap::>` to quickly access the +`std::map::>` to quickly access the corresponding object. diff --git a/examples/cxx/event_multiplexing/src/notifier.cpp b/examples/cxx/event_multiplexing/src/notifier.cpp index 37de959f4..a82579928 100644 --- a/examples/cxx/event_multiplexing/src/notifier.cpp +++ b/examples/cxx/event_multiplexing/src/notifier.cpp @@ -19,8 +19,6 @@ #include "iox2/service_name.hpp" #include "iox2/service_type.hpp" -constexpr iox::units::Duration CYCLE_TIME = iox::units::Duration::fromSeconds(1); - // NOLINTBEGIN struct Args { IOX_CLI_DEFINITION(Args); @@ -29,6 +27,8 @@ struct Args { }; // NOLINTEND +constexpr iox::units::Duration CYCLE_TIME = iox::units::Duration::fromSeconds(1); + auto main(int argc, char** argv) -> int { using namespace iox2; auto args = Args::parse(argc, argv, "Notifier of the event multiplexing example."); diff --git a/examples/cxx/event_multiplexing/src/wait.cpp b/examples/cxx/event_multiplexing/src/wait.cpp index 07d378c30..34b22c283 100644 --- a/examples/cxx/event_multiplexing/src/wait.cpp +++ b/examples/cxx/event_multiplexing/src/wait.cpp @@ -33,6 +33,10 @@ struct Args { }; // NOLINTEND +struct ServiceNameListenerPair { + iox2::ServiceName service_name; + iox2::Listener listener; +}; auto main(int argc, char** argv) -> int { using namespace iox2; @@ -42,34 +46,36 @@ auto main(int argc, char** argv) -> int { auto service_name_1 = ServiceName::create(args.service1().c_str()).expect("valid service name"); auto service_name_2 = ServiceName::create(args.service2().c_str()).expect("valid service name"); + // create node and services auto node = NodeBuilder().create().expect("successful node creation"); auto service_1 = node.service_builder(service_name_1).event().open_or_create().expect("successful service creation/opening"); auto service_2 = node.service_builder(service_name_2).event().open_or_create().expect("successful service creation/opening"); - - auto waitset = WaitSetBuilder().create().expect(""); auto listener_1 = service_1.listener_builder().create().expect("successful listener creation"); auto listener_2 = service_2.listener_builder().create().expect("successful listener creation"); + // create the waitset and attach the listeners to it + auto waitset = WaitSetBuilder().create().expect(""); iox::vector, 2> guards; guards.emplace_back(waitset.attach_notification(listener_1).expect("")); guards.emplace_back(waitset.attach_notification(listener_2).expect("")); - std::map, std::pair>> listeners; + std::map, ServiceNameListenerPair> listeners; listeners.emplace(std::make_pair(WaitSetAttachmentId::from_guard(guards[0]), - std::make_pair(service_name_1, std::move(listener_1)))); + ServiceNameListenerPair { service_name_1, std::move(listener_1) })); listeners.emplace(std::make_pair(WaitSetAttachmentId::from_guard(guards[1]), - std::make_pair(service_name_2, std::move(listener_2)))); + ServiceNameListenerPair { service_name_2, std::move(listener_2) })); + // the callback that is called when a listener has received an event auto on_event = [&](WaitSetAttachmentId attachment_id) { if (auto l = listeners.find(attachment_id); l != listeners.end()) { - std::cout << "Received trigger from \"" << l->second.first.to_string().c_str() << "\"" << std::endl; + std::cout << "Received trigger from \"" << l->second.service_name.to_string().c_str() << "\"" << std::endl; - auto& listener = l->second.second; + auto& listener = l->second.listener; listener.try_wait_one().expect("").and_then([](auto event_id) { std::cout << " " << event_id; }); std::cout << std::endl; } @@ -77,6 +83,10 @@ auto main(int argc, char** argv) -> int { std::cout << "Waiting on the following services: " << service_name_1.to_string().c_str() << ", " << service_name_2.to_string().c_str() << std::endl; + + // loops until the user has pressed CTRL+c, the application has received a SIGTERM or SIGINT + // signal or the user has called explicitly `waitset.stop()` in the `on_event` callback. We + // didn't add this to the example so feel free to play around with it. waitset.wait_and_process(on_event).expect(""); std::cout << "exit" << std::endl; diff --git a/examples/rust/event_multiplexing/README.md b/examples/rust/event_multiplexing/README.md index 0ee6d4f09..b08a31c2e 100644 --- a/examples/rust/event_multiplexing/README.md +++ b/examples/rust/event_multiplexing/README.md @@ -45,9 +45,9 @@ mechanisms. Before the `WaitSet` can monitor a specific event, it must first be attached using `WaitSet::attach()`, which returns a RAII `Guard`. This `Guard` automatically detaches the attachment when it goes out of scope. -The `WaitSet::**_wait()` calls require a closure that is invoked for each +The `WaitSet::wait_and_process()` call requires a closure that is invoked for each triggered attachment and provides the `AttachmentId`. The user can either use -`AttachmentId::originates_from($ATTACHED_OBJECT$)` to identify the object +`AttachmentId::has_event_from($ATTACHED_OBJECT$)` to identify the object associated with the `AttachmentId`, or set up a `HashMap::>` to quickly access the corresponding object. From 63c3ae56d63c81039f96341bc432277beb3d39ae Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Wed, 9 Oct 2024 14:11:44 +0200 Subject: [PATCH 4/8] [#390] Add C example --- examples/README.md | 2 +- examples/c/CMakeLists.txt | 1 + examples/c/event_multiplexing/BUILD.bazel | 33 ++++ examples/c/event_multiplexing/CMakeLists.txt | 22 +++ examples/c/event_multiplexing/README.md | 46 ++++++ examples/c/event_multiplexing/src/notifier.c | 91 +++++++++++ examples/c/event_multiplexing/src/wait.c | 154 +++++++++++++++++++ 7 files changed, 348 insertions(+), 1 deletion(-) create mode 100644 examples/c/event_multiplexing/BUILD.bazel create mode 100644 examples/c/event_multiplexing/CMakeLists.txt create mode 100644 examples/c/event_multiplexing/README.md create mode 100644 examples/c/event_multiplexing/src/notifier.c create mode 100644 examples/c/event_multiplexing/src/wait.c diff --git a/examples/README.md b/examples/README.md index 87831fca8..820ad165b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -77,7 +77,7 @@ These types are demonstrated in the complex data types example. | docker | [all](rust/docker) | Communicate between different docker containers and the host. | | domains | [C](c/domains) [C++](cxx/domains) [Rust](rust/domains) | Establish separate domains that operate independently from one another. | | event | [C](c/event) [C++](cxx/event) [Rust](rust/event) | Push notifications - send event signals to wakeup processes that are waiting for them. | -| event multiplexing | [C++](rust/event_multiplexing) [Rust](rust/event_multiplexing) | Wait on multiple listeners or sockets with a single call. The WaitSet demultiplexes incoming events and notifies the user. | +| event multiplexing | [C](c/event_multiplexing) [C++](cxx/event_multiplexing) [Rust](rust/event_multiplexing) | Wait on multiple listeners or sockets with a single call. The WaitSet demultiplexes incoming events and notifies the user. | | publish subscribe | [C](c/publish_subscribe) [C++](cxx/publish_subscribe) [Rust](rust/publish_subscribe) | Communication between multiple processes with a [publish subscribe messaging pattern](https://en.wikipedia.org/wiki/Publish–subscribe_pattern). | | publish subscribe dynamic data | [Rust](rust/publish_subscribe_dynamic_data) | Communication between multiple processes with a [publish subscribe messaging pattern](https://en.wikipedia.org/wiki/Publish–subscribe_pattern) and payload data that has a dynamic size. | | publish subscribe with user header | [C](c/publish_subscribe_with_user_header) [C++](cxx/publish_subscribe_with_user_header) [Rust](rust/publish_subscribe_with_user_header) | Add a user header to the payload (samples) to transfer additional information. | diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index 60e9b4a21..4a7449864 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -16,5 +16,6 @@ project(examples_c LANGUAGES C) add_subdirectory(discovery) add_subdirectory(domains) add_subdirectory(event) +add_subdirectory(event_multiplexing) add_subdirectory(publish_subscribe) add_subdirectory(publish_subscribe_with_user_header) diff --git a/examples/c/event_multiplexing/BUILD.bazel b/examples/c/event_multiplexing/BUILD.bazel new file mode 100644 index 000000000..83a9bc7b0 --- /dev/null +++ b/examples/c/event_multiplexing/BUILD.bazel @@ -0,0 +1,33 @@ +# Copyright (c) 2024 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 Software License 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +# which is available at https://opensource.org/licenses/MIT. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT + +load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") + +cc_binary( + name = "example_c_event_multiplexing_wait", + srcs = [ + "src/wait.c", + ], + deps = [ + "//:iceoryx2-c", + ], +) + +cc_binary( + name = "example_c_event_multiplexing_notifier", + srcs = [ + "src/notifier.c", + ], + deps = [ + "//:iceoryx2-c", + ], +) diff --git a/examples/c/event_multiplexing/CMakeLists.txt b/examples/c/event_multiplexing/CMakeLists.txt new file mode 100644 index 000000000..e6c02e8e2 --- /dev/null +++ b/examples/c/event_multiplexing/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (c) 2024 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 Software License 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +# which is available at https://opensource.org/licenses/MIT. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT + +cmake_minimum_required(VERSION 3.22) +project(example_c_event_multiplexing LANGUAGES C) + +find_package(iceoryx2-c 0.4.1 REQUIRED) + +add_executable(example_c_event_multiplexing_wait src/wait.c) +target_link_libraries(example_c_event_multiplexing_wait iceoryx2-c::static-lib) + +add_executable(example_c_event_multiplexing_notifier src/notifier.c) +target_link_libraries(example_c_event_multiplexing_notifier iceoryx2-c::static-lib) diff --git a/examples/c/event_multiplexing/README.md b/examples/c/event_multiplexing/README.md new file mode 100644 index 000000000..c1067383c --- /dev/null +++ b/examples/c/event_multiplexing/README.md @@ -0,0 +1,46 @@ +# Event + +Before proceeding, all dependencies need to be installed. You can find +instructions in the [C Examples Readme](../README.md). + +## Running The Example + +This example offers a practical demonstration of inter-process event signaling +in iceoryx2. It showcases how one process can signal an event to another +process, allowing for efficient communication. + +In this scenario, the 'listener' process waits for incoming events. When an +event arrives, it promptly awakens and reports the [`EventId`] of the received +event. On the other end, the 'notifier' process periodically sends notifications +with an incrementing `EventId` every second. + +First you have to build the C examples: + +```sh +cmake -S . -B target/ffi/build -DBUILD_EXAMPLES=ON +cmake --build target/ffi/build +``` + +To see this in action, open two separate terminals and run the following +commands: + +### Terminal 1 + +```sh +./target/ffi/build/examples/c/event/example_c_event_listener +``` + +### Terminal 2 + +```sh +./target/ffi/build/examples/c/event/example_c_event_notifier +``` + +Feel free to run multiple listeners or notifiers concurrently to observe how +iceoryx2 efficiently handles event signaling across processes. + +You may hit the maximum supported number of ports when too many listener or +notifier processes run. Take a look at the [iceoryx2 config](../../../config) to +set the limits globally or at the +[API of the Service builder](https://docs.rs/iceoryx2/latest/iceoryx2/service/index.html) +to set them for a single service. diff --git a/examples/c/event_multiplexing/src/notifier.c b/examples/c/event_multiplexing/src/notifier.c new file mode 100644 index 000000000..66035f9d8 --- /dev/null +++ b/examples/c/event_multiplexing/src/notifier.c @@ -0,0 +1,91 @@ +// Copyright (c) 2024 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 Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#include "iox2/iceoryx2.h" + +#include +#include +#include + +#ifdef _WIN64 +#include +#define sleep Sleep +#else +#include +#endif + +int main(int argc, char** argv) { + if (argc != 3) { + printf("Usage: %s EVENT_ID SERVICE_NAME\n", argv[0]); + return -1; + } + + size_t event_id_value = strtoull(argv[1], NULL, 10); + + // create new node + iox2_node_builder_h node_builder_handle = iox2_node_builder_new(NULL); + iox2_node_h node_handle = NULL; + if (iox2_node_builder_create(node_builder_handle, NULL, iox2_service_type_e_IPC, &node_handle) != IOX2_OK) { + printf("Could not create node!\n"); + goto end; + } + + // create service name + const char* service_name_value = argv[2]; + iox2_service_name_h service_name = NULL; + if (iox2_service_name_new(NULL, service_name_value, strlen(service_name_value), &service_name) != IOX2_OK) { + printf("Unable to create service name!\n"); + goto drop_node; + } + + // create service + iox2_service_name_ptr service_name_ptr = iox2_cast_service_name_ptr(service_name); + iox2_service_builder_h service_builder = iox2_node_service_builder(&node_handle, NULL, service_name_ptr); + iox2_service_builder_event_h service_builder_event = iox2_service_builder_event(service_builder); + iox2_port_factory_event_h service = NULL; + if (iox2_service_builder_event_open_or_create(service_builder_event, NULL, &service) != IOX2_OK) { + printf("Unable to create service!\n"); + goto drop_node; + } + + // create notifier + iox2_port_factory_notifier_builder_h notifier_builder = iox2_port_factory_event_notifier_builder(&service, NULL); + iox2_notifier_h notifier = NULL; + if (iox2_port_factory_notifier_builder_create(notifier_builder, NULL, ¬ifier) != IOX2_OK) { + printf("Unable to create notifier!\n"); + goto drop_service; + } + + while (iox2_node_wait(&node_handle, 0, 0) == IOX2_OK) { + iox2_event_id_t event_id = { .value = event_id_value }; // NOLINT + if (iox2_notifier_notify_with_custom_event_id(¬ifier, &event_id, NULL) != IOX2_OK) { + printf("Failed to notify listener!\n"); + goto drop_notifier; + } + + printf("[service: \"%s\"] Trigger event with id %lu ...\n", argv[2], (long unsigned) event_id.value); + + sleep(1); + } + +drop_notifier: + iox2_notifier_drop(notifier); + +drop_service: + iox2_port_factory_event_drop(service); + +drop_node: + iox2_node_drop(node_handle); + +end: + return 0; +} diff --git a/examples/c/event_multiplexing/src/wait.c b/examples/c/event_multiplexing/src/wait.c new file mode 100644 index 000000000..fd5d65112 --- /dev/null +++ b/examples/c/event_multiplexing/src/wait.c @@ -0,0 +1,154 @@ +// Copyright (c) 2024 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 Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#include "iox2/iceoryx2.h" + +#include +#include +#include + +struct CallbackContext { + iox2_waitset_guard_h_ref guard_1; + iox2_waitset_guard_h_ref guard_2; + iox2_listener_h_ref listener_1; + iox2_listener_h_ref listener_2; +}; + +int main(int argc, char** argv) { + if (argc != 3) { + printf("Usage: %s SERVICE_NAME_1 SERVICE_NAME_2\n", argv[0]); + return -1; + } + + // create new node + iox2_node_builder_h node_builder_handle = iox2_node_builder_new(NULL); + iox2_node_h node_handle = NULL; + if (iox2_node_builder_create(node_builder_handle, NULL, iox2_service_type_e_IPC, &node_handle) != IOX2_OK) { + printf("Could not create node!\n"); + goto end; + } + + // create service names + iox2_service_name_h service_name_1 = NULL; + if (iox2_service_name_new(NULL, argv[1], strlen(argv[1]), &service_name_1) != IOX2_OK) { + printf("Unable to create service name!\n"); + goto drop_node; + } + + iox2_service_name_h service_name_2 = NULL; + if (iox2_service_name_new(NULL, argv[2], strlen(argv[2]), &service_name_2) != IOX2_OK) { + printf("Unable to create service name!\n"); + goto drop_node; + } + + // create services + iox2_service_name_ptr service_name_ptr_1 = iox2_cast_service_name_ptr(service_name_1); + iox2_service_builder_h service_builder_1 = iox2_node_service_builder(&node_handle, NULL, service_name_ptr_1); + iox2_service_builder_event_h service_builder_event_1 = iox2_service_builder_event(service_builder_1); + iox2_port_factory_event_h service_1 = NULL; + if (iox2_service_builder_event_open_or_create(service_builder_event_1, NULL, &service_1) != IOX2_OK) { + printf("Unable to create service!\n"); + goto drop_node; + } + + iox2_service_name_ptr service_name_ptr_2 = iox2_cast_service_name_ptr(service_name_2); + iox2_service_builder_h service_builder_2 = iox2_node_service_builder(&node_handle, NULL, service_name_ptr_2); + iox2_service_builder_event_h service_builder_event_2 = iox2_service_builder_event(service_builder_2); + iox2_port_factory_event_h service_2 = NULL; + if (iox2_service_builder_event_open_or_create(service_builder_event_2, NULL, &service_2) != IOX2_OK) { + printf("Unable to create service!\n"); + goto drop_service_1; + } + + // create listeners + iox2_port_factory_listener_builder_h listener_builder_1 = + iox2_port_factory_event_listener_builder(&service_1, NULL); + iox2_listener_h listener_1 = NULL; + if (iox2_port_factory_listener_builder_create(listener_builder_1, NULL, &listener_1) != IOX2_OK) { + printf("Unable to create listener!\n"); + goto drop_service_2; + } + + iox2_port_factory_listener_builder_h listener_builder_2 = + iox2_port_factory_event_listener_builder(&service_2, NULL); + iox2_listener_h listener_2 = NULL; + if (iox2_port_factory_listener_builder_create(listener_builder_2, NULL, &listener_2) != IOX2_OK) { + printf("Unable to create listener!\n"); + goto drop_listener_1; + } + + // create waitset + iox2_waitset_builder_h waitset_builder; + iox2_waitset_builder_new(NULL, &waitset_builder); + iox2_waitset_h waitset; + if (iox2_waitset_builder_create(waitset_builder, iox2_service_type_e_IPC, NULL, &waitset) != IOX2_OK) { + printf("Unable to create waitset\n"); + goto drop_waitset_builder; + } + + // attach listeners to waitset + iox2_waitset_guard_h guard_1; + if (iox2_waitset_attach_notification(&waitset, iox2_listener_get_file_descriptor(&listener_1), NULL, &guard_1) + != IOX2_OK) { + printf("Unable to attach listener 1\n"); + goto drop_waitset; + } + + iox2_waitset_guard_h guard_2; + if (iox2_waitset_attach_notification(&waitset, iox2_listener_get_file_descriptor(&listener_2), NULL, &guard_2) + != IOX2_OK) { + printf("Unable to attach listener 2\n"); + goto drop_guard_1; + } + + struct CallbackContext context; + context.guard_1 = &guard_1; + context.guard_2 = &guard_2; + context.listener_1 = &listener_1; + context.listener_2 = &listener_2; + + // iox2_waitset_run_result_e result; + // iox2_waitset_wait_and_process( + // &waitset, iox2_waitset_run_callback callback, iox2_callback_context callback_ctx, &result); + + iox2_event_id_t event_id; + +drop_guard_2: + iox2_waitset_guard_drop(guard_2); + +drop_guard_1: + iox2_waitset_guard_drop(guard_1); + +drop_waitset: + iox2_waitset_drop(waitset); + +drop_waitset_builder: + iox2_waitset_builder_drop(waitset_builder); + +drop_listener_2: + iox2_listener_drop(listener_2); + +drop_listener_1: + iox2_listener_drop(listener_1); + +drop_service_2: + iox2_port_factory_event_drop(service_2); + +drop_service_1: + iox2_port_factory_event_drop(service_1); + +drop_node: + iox2_node_drop(node_handle); + +end: + return 0; +} From 66f656fe94cec59e42094c62a71698a58643fbe7 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Wed, 9 Oct 2024 15:06:57 +0200 Subject: [PATCH 5/8] [#390] Finalize C example --- examples/c/event_multiplexing/README.md | 57 ++++++++------ examples/c/event_multiplexing/src/notifier.c | 8 +- examples/c/event_multiplexing/src/wait.c | 74 ++++++++++++++++--- examples/cxx/event_multiplexing/README.md | 10 +-- .../cxx/event_multiplexing/src/notifier.cpp | 1 - examples/cxx/event_multiplexing/src/wait.cpp | 17 +++-- 6 files changed, 119 insertions(+), 48 deletions(-) diff --git a/examples/c/event_multiplexing/README.md b/examples/c/event_multiplexing/README.md index c1067383c..eafec840e 100644 --- a/examples/c/event_multiplexing/README.md +++ b/examples/c/event_multiplexing/README.md @@ -5,42 +5,51 @@ instructions in the [C Examples Readme](../README.md). ## Running The Example -This example offers a practical demonstration of inter-process event signaling -in iceoryx2. It showcases how one process can signal an event to another -process, allowing for efficient communication. +This example demonstrates iceoryx2's event multiplexing mechanism, +called the `WaitSet`. It allows waiting, with a single call, on +multiple `Listener` ports as well as external file descriptor-based +events such as `sockets`. -In this scenario, the 'listener' process waits for incoming events. When an -event arrives, it promptly awakens and reports the [`EventId`] of the received -event. On the other end, the 'notifier' process periodically sends notifications -with an incrementing `EventId` every second. +In this setup, the `wait` process monitors two services, which the +user can specify via the command line option `-s` and `-t`. +The `notifier` can define the service to which it will send event +notifications using the `-s` option and specify the event ID with +the `-e` option. -First you have to build the C examples: +In the example below, we are waiting for events on the services `fuu` and +`bar`. Service `fuu` is notified with event ID `123`, and service `bar` is +notified with event ID `456`. + +### Terminal 1 ```sh -cmake -S . -B target/ffi/build -DBUILD_EXAMPLES=ON -cmake --build target/ffi/build +./target/ffi/build/examples/c/event_multiplexing/example_c_event_multiplexing_wait fuu bar ``` -To see this in action, open two separate terminals and run the following -commands: - -### Terminal 1 +### Terminal 2 ```sh -./target/ffi/build/examples/c/event/example_c_event_listener +./target/ffi/build/examples/c/event_multiplexing/example_c_event_multiplexing_notifier 123 fuu ``` -### Terminal 2 +### Terminal 3 ```sh -./target/ffi/build/examples/c/event/example_c_event_notifier +./target/ffi/build/examples/c/event_multiplexing/example_c_event_multiplexing_notifier 456 bar ``` -Feel free to run multiple listeners or notifiers concurrently to observe how -iceoryx2 efficiently handles event signaling across processes. +Feel free to instantiate multiple notifiers for the same service with the same +or different event id's. Or to for different services. + +## Technical Details + +The `iox2_waitset_t` utilizes `epoll`, `select`, or other event-multiplexing +mechanisms. Before the `iox2_waitset_t` can monitor a specific event, it must +first be attached using `iox2_waitset_attach_notification()`, which returns a +RAII `iox2_waitset_guard_t`. This `Guard` automatically detaches the attachment +when it is cleaned up with `iox2_waitset_guard_drop()`. -You may hit the maximum supported number of ports when too many listener or -notifier processes run. Take a look at the [iceoryx2 config](../../../config) to -set the limits globally or at the -[API of the Service builder](https://docs.rs/iceoryx2/latest/iceoryx2/service/index.html) -to set them for a single service. +The `iox2_waitset_wait_and_process()` call requires a callback that is invoked +for each triggered attachment and provides the `iox2_waitset_attachment_id_h`. +The user can either use `iox2_waitset_attachment_id_has_event_from()` to +identify the object associated with the `iox2_waitset_attachment_id_h`. diff --git a/examples/c/event_multiplexing/src/notifier.c b/examples/c/event_multiplexing/src/notifier.c index 66035f9d8..67ecff8ee 100644 --- a/examples/c/event_multiplexing/src/notifier.c +++ b/examples/c/event_multiplexing/src/notifier.c @@ -16,6 +16,8 @@ #include #include +const int BASE_10 = 10; + #ifdef _WIN64 #include #define sleep Sleep @@ -29,7 +31,7 @@ int main(int argc, char** argv) { return -1; } - size_t event_id_value = strtoull(argv[1], NULL, 10); + size_t event_id_value = strtoull(argv[1], NULL, BASE_10); // create new node iox2_node_builder_h node_builder_handle = iox2_node_builder_new(NULL); @@ -65,6 +67,7 @@ int main(int argc, char** argv) { goto drop_service; } + // notifier with a period of 1 second while (iox2_node_wait(&node_handle, 0, 0) == IOX2_OK) { iox2_event_id_t event_id = { .value = event_id_value }; // NOLINT if (iox2_notifier_notify_with_custom_event_id(¬ifier, &event_id, NULL) != IOX2_OK) { @@ -83,6 +86,9 @@ int main(int argc, char** argv) { drop_service: iox2_port_factory_event_drop(service); +drop_service_name: + iox2_service_name_drop(service_name); + drop_node: iox2_node_drop(node_handle); diff --git a/examples/c/event_multiplexing/src/wait.c b/examples/c/event_multiplexing/src/wait.c index fd5d65112..b619f778e 100644 --- a/examples/c/event_multiplexing/src/wait.c +++ b/examples/c/event_multiplexing/src/wait.c @@ -21,8 +21,49 @@ struct CallbackContext { iox2_waitset_guard_h_ref guard_2; iox2_listener_h_ref listener_1; iox2_listener_h_ref listener_2; + const char* service_name_1; + const char* service_name_2; }; + +// the function that is called when a listener has received an event +void on_event(iox2_waitset_attachment_id_h attachment_id, void* context) { + struct CallbackContext* ctx = (struct CallbackContext*) context; + + iox2_event_id_t event_id; + bool has_received_event = false; + // check if the event originated from guard_1 of listener_1 + if (iox2_waitset_attachment_id_has_event_from(&attachment_id, ctx->guard_1)) { + printf("Received trigger from \"%s\" ::", ctx->service_name_1); + do { + if (iox2_listener_try_wait_one(ctx->listener_1, &event_id, &has_received_event) != IOX2_OK) { + printf("failed to receive event on listener: %s\n", ctx->service_name_1); + } + + if (has_received_event) { + printf(" %lu", (long unsigned) event_id.value); + } + } while (has_received_event); + printf("\n"); + // check if the event originated from guard_2 of listener_2 + } else if (iox2_waitset_attachment_id_has_event_from(&attachment_id, ctx->guard_2)) { + printf("Received trigger from \"%s\" ::", ctx->service_name_2); + do { + if (iox2_listener_try_wait_one(ctx->listener_2, &event_id, &has_received_event) != IOX2_OK) { + printf("failed to receive event on listener: %s\n", ctx->service_name_2); + } + + if (has_received_event) { + printf(" %lu", (long unsigned) event_id.value); + } + } while (has_received_event); + printf("\n"); + } + + iox2_waitset_attachment_id_drop(attachment_id); +} + +//NOLINTBEGIN(readability-function-size) int main(int argc, char** argv) { if (argc != 3) { printf("Usage: %s SERVICE_NAME_1 SERVICE_NAME_2\n", argv[0]); @@ -47,7 +88,7 @@ int main(int argc, char** argv) { iox2_service_name_h service_name_2 = NULL; if (iox2_service_name_new(NULL, argv[2], strlen(argv[2]), &service_name_2) != IOX2_OK) { printf("Unable to create service name!\n"); - goto drop_node; + goto drop_service_name_1; } // create services @@ -57,7 +98,7 @@ int main(int argc, char** argv) { iox2_port_factory_event_h service_1 = NULL; if (iox2_service_builder_event_open_or_create(service_builder_event_1, NULL, &service_1) != IOX2_OK) { printf("Unable to create service!\n"); - goto drop_node; + goto drop_service_name_2; } iox2_service_name_ptr service_name_ptr_2 = iox2_cast_service_name_ptr(service_name_2); @@ -87,23 +128,23 @@ int main(int argc, char** argv) { } // create waitset - iox2_waitset_builder_h waitset_builder; + iox2_waitset_builder_h waitset_builder = NULL; iox2_waitset_builder_new(NULL, &waitset_builder); - iox2_waitset_h waitset; + iox2_waitset_h waitset = NULL; if (iox2_waitset_builder_create(waitset_builder, iox2_service_type_e_IPC, NULL, &waitset) != IOX2_OK) { printf("Unable to create waitset\n"); goto drop_waitset_builder; } // attach listeners to waitset - iox2_waitset_guard_h guard_1; + iox2_waitset_guard_h guard_1 = NULL; if (iox2_waitset_attach_notification(&waitset, iox2_listener_get_file_descriptor(&listener_1), NULL, &guard_1) != IOX2_OK) { printf("Unable to attach listener 1\n"); goto drop_waitset; } - iox2_waitset_guard_h guard_2; + iox2_waitset_guard_h guard_2 = NULL; if (iox2_waitset_attach_notification(&waitset, iox2_listener_get_file_descriptor(&listener_2), NULL, &guard_2) != IOX2_OK) { printf("Unable to attach listener 2\n"); @@ -115,10 +156,16 @@ int main(int argc, char** argv) { context.guard_2 = &guard_2; context.listener_1 = &listener_1; context.listener_2 = &listener_2; - - // iox2_waitset_run_result_e result; - // iox2_waitset_wait_and_process( - // &waitset, iox2_waitset_run_callback callback, iox2_callback_context callback_ctx, &result); + context.service_name_1 = argv[1]; + context.service_name_2 = argv[2]; + + iox2_waitset_run_result_e result = iox2_waitset_run_result_e_STOP_REQUEST; + // loops until the user has pressed CTRL+c, the application has received a SIGTERM or SIGINT + // signal or the user has called explicitly `iox2_waitset_stop` in the `on_event` function. We + // didn't add this to the example so feel free to play around with it. + if (iox2_waitset_wait_and_process(&waitset, on_event, (void*) &context, &result) != IOX2_OK) { + printf("Failure in WaitSet::wait_and_process loop \n"); + } iox2_event_id_t event_id; @@ -146,9 +193,16 @@ int main(int argc, char** argv) { drop_service_1: iox2_port_factory_event_drop(service_1); +drop_service_name_2: + iox2_service_name_drop(service_name_2); + +drop_service_name_1: + iox2_service_name_drop(service_name_1); + drop_node: iox2_node_drop(node_handle); end: return 0; } +//NOLINTEND(readability-function-size) diff --git a/examples/cxx/event_multiplexing/README.md b/examples/cxx/event_multiplexing/README.md index c34c0906f..7b10f93e0 100644 --- a/examples/cxx/event_multiplexing/README.md +++ b/examples/cxx/event_multiplexing/README.md @@ -45,12 +45,12 @@ or different event id's. Or to for different services. The `WaitSet` utilizes `epoll`, `select`, or other event-multiplexing mechanisms. Before the `WaitSet` can monitor a specific event, it must first be -attached using `WaitSet::attach_notification()`, which returns a RAII `Guard`. This `Guard` -automatically detaches the attachment when it goes out of scope. +attached using `WaitSet::attach_notification()`, which returns a RAII `Guard`. +This `Guard` automatically detaches the attachment when it goes out of scope. -The `WaitSet::wait_and_process()` call requires a closure that is invoked for each -triggered attachment and provides the `AttachmentId`. The user can either use -`AttachmentId::has_event_from($ATTACHED_OBJECT$)` to identify the object +The `WaitSet::wait_and_process()` call requires a closure that is invoked for +each triggered attachment and provides the `AttachmentId`. The user can either +use `AttachmentId::has_event_from($ATTACHED_OBJECT$)` to identify the object associated with the `AttachmentId`, or set up a `std::map::>` to quickly access the corresponding object. diff --git a/examples/cxx/event_multiplexing/src/notifier.cpp b/examples/cxx/event_multiplexing/src/notifier.cpp index a82579928..efbe274f8 100644 --- a/examples/cxx/event_multiplexing/src/notifier.cpp +++ b/examples/cxx/event_multiplexing/src/notifier.cpp @@ -40,7 +40,6 @@ auto main(int argc, char** argv) -> int { auto service = node.service_builder(service_name).event().open_or_create().expect("successful service creation/opening"); - auto max_event_id = service.static_config().event_id_max_value(); auto notifier = service.notifier_builder().create().expect("successful notifier creation"); diff --git a/examples/cxx/event_multiplexing/src/wait.cpp b/examples/cxx/event_multiplexing/src/wait.cpp index 34b22c283..22678980e 100644 --- a/examples/cxx/event_multiplexing/src/wait.cpp +++ b/examples/cxx/event_multiplexing/src/wait.cpp @@ -58,24 +58,27 @@ auto main(int argc, char** argv) -> int { // create the waitset and attach the listeners to it auto waitset = WaitSetBuilder().create().expect(""); + // NOLINTNEXTLINE(misc-const-correctness) false positive iox::vector, 2> guards; guards.emplace_back(waitset.attach_notification(listener_1).expect("")); guards.emplace_back(waitset.attach_notification(listener_2).expect("")); + // NOLINTNEXTLINE(misc-const-correctness) false positive std::map, ServiceNameListenerPair> listeners; - listeners.emplace(std::make_pair(WaitSetAttachmentId::from_guard(guards[0]), - ServiceNameListenerPair { service_name_1, std::move(listener_1) })); - listeners.emplace(std::make_pair(WaitSetAttachmentId::from_guard(guards[1]), - ServiceNameListenerPair { service_name_2, std::move(listener_2) })); + listeners.emplace(WaitSetAttachmentId::from_guard(guards[0]), + ServiceNameListenerPair { service_name_1, std::move(listener_1) }); + listeners.emplace(WaitSetAttachmentId::from_guard(guards[1]), + ServiceNameListenerPair { service_name_2, std::move(listener_2) }); // the callback that is called when a listener has received an event auto on_event = [&](WaitSetAttachmentId attachment_id) { - if (auto l = listeners.find(attachment_id); l != listeners.end()) { - std::cout << "Received trigger from \"" << l->second.service_name.to_string().c_str() << "\"" << std::endl; + if (auto entry = listeners.find(attachment_id); entry != listeners.end()) { + std::cout << "Received trigger from \"" << entry->second.service_name.to_string().c_str() << "\"" + << std::endl; - auto& listener = l->second.listener; + auto& listener = entry->second.listener; listener.try_wait_one().expect("").and_then([](auto event_id) { std::cout << " " << event_id; }); std::cout << std::endl; } From 13d0438c7eebbce9e000926c1f279f6d9e7025d6 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Wed, 9 Oct 2024 16:53:22 +0200 Subject: [PATCH 6/8] [#390] Equal settings for toml file and default config --- iceoryx2/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iceoryx2/src/config.rs b/iceoryx2/src/config.rs index 138d7155f..6ad0ee1a6 100644 --- a/iceoryx2/src/config.rs +++ b/iceoryx2/src/config.rs @@ -332,7 +332,7 @@ impl Default for Config { max_listeners: 16, max_notifiers: 16, max_nodes: 36, - event_id_max_value: 32, + event_id_max_value: 4294967296, }, }, } From be13c3ee86056887846c928f3d4e5e38e52ffa6a Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Wed, 9 Oct 2024 18:36:27 +0200 Subject: [PATCH 7/8] [#390] Adjust event id max so that it also works on 32 bit --- config/iceoryx2.toml | 2 +- iceoryx2/src/config.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/iceoryx2.toml b/config/iceoryx2.toml index 7c66d1335..00ca9fb52 100644 --- a/config/iceoryx2.toml +++ b/config/iceoryx2.toml @@ -37,4 +37,4 @@ subscriber-expired-connection-buffer = 128 max-listeners = 16 max-notifiers = 16 max-nodes = 36 -event-id-max-value = 4294967296 +event-id-max-value = 4294967295 diff --git a/iceoryx2/src/config.rs b/iceoryx2/src/config.rs index 6ad0ee1a6..58199e483 100644 --- a/iceoryx2/src/config.rs +++ b/iceoryx2/src/config.rs @@ -332,7 +332,7 @@ impl Default for Config { max_listeners: 16, max_notifiers: 16, max_nodes: 36, - event_id_max_value: 4294967296, + event_id_max_value: 4294967295, }, }, } From 1436d6aede638023044c6f7814a43475d4c325e4 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Wed, 9 Oct 2024 18:37:47 +0200 Subject: [PATCH 8/8] [#456] Update changelog --- doc/release-notes/iceoryx2-unreleased.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/release-notes/iceoryx2-unreleased.md b/doc/release-notes/iceoryx2-unreleased.md index 554cb883b..d44a253ca 100644 --- a/doc/release-notes/iceoryx2-unreleased.md +++ b/doc/release-notes/iceoryx2-unreleased.md @@ -34,6 +34,7 @@ --> * Rename `NodeEvent` into `WaitEvent` [#390](https://github.com/eclipse-iceoryx/iceoryx2/issues/390) +* Remove ACL dependency [#457](https://github.com/eclipse-iceoryx/iceoryx2/issues/457) ### Workflow