diff --git a/config/iceoryx2.toml b/config/iceoryx2.toml index 883e81909..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 = 32 +event-id-max-value = 4294967295 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 diff --git a/examples/README.md b/examples/README.md index ca8f65c39..820ad165b 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](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. | +| 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/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..eafec840e --- /dev/null +++ b/examples/c/event_multiplexing/README.md @@ -0,0 +1,55 @@ +# 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 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. + +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 +./target/ffi/build/examples/c/event_multiplexing/example_c_event_multiplexing_wait fuu bar +``` + +### Terminal 2 + +```sh +./target/ffi/build/examples/c/event_multiplexing/example_c_event_multiplexing_notifier 123 fuu +``` + +### Terminal 3 + +```sh +./target/ffi/build/examples/c/event_multiplexing/example_c_event_multiplexing_notifier 456 bar +``` + +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()`. + +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 new file mode 100644 index 000000000..67ecff8ee --- /dev/null +++ b/examples/c/event_multiplexing/src/notifier.c @@ -0,0 +1,97 @@ +// 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 + +const int BASE_10 = 10; + +#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, BASE_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; + } + + // 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) { + 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_service_name: + iox2_service_name_drop(service_name); + +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..b619f778e --- /dev/null +++ b/examples/c/event_multiplexing/src/wait.c @@ -0,0 +1,208 @@ +// 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; + 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]); + 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_service_name_1; + } + + // 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_service_name_2; + } + + 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 = NULL; + iox2_waitset_builder_new(NULL, &waitset_builder); + 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 = 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 = 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"); + 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; + 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; + +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_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/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..7b10f93e0 --- /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 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. + +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 +./target/ffi/build/examples/cxx/event_multiplexing/example_cxx_event_multiplexing_wait -s fuu -t bar +``` + +### Terminal 2 + +```sh +./target/ffi/build/examples/cxx/event_multiplexing/example_cxx_event_multiplexing_notifier -s "fuu" -e 123 +``` + +### Terminal 3 + +```sh +./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 +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_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 +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 new file mode 100644 index 000000000..efbe274f8 --- /dev/null +++ b/examples/cxx/event_multiplexing/src/notifier.cpp @@ -0,0 +1,56 @@ +// 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" + +// NOLINTBEGIN +struct Args { + IOX_CLI_DEFINITION(Args); + 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 + +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."); + + 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 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..22678980e --- /dev/null +++ b/examples/cxx/event_multiplexing/src/wait.cpp @@ -0,0 +1,98 @@ +// 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 + +#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 "iox2/waitset.hpp" + +constexpr iox::units::Duration CYCLE_TIME = iox::units::Duration::fromSeconds(1); + +// 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 + +struct ServiceNameListenerPair { + iox2::ServiceName service_name; + iox2::Listener listener; +}; + +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"); + + // 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 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(""); + // 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(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 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 = entry->second.listener; + listener.try_wait_one().expect("").and_then([](auto event_id) { std::cout << " " << event_id; }); + std::cout << 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; + + // 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; + + return 0; +} 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. diff --git a/iceoryx2/src/config.rs b/iceoryx2/src/config.rs index 138d7155f..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: 32, + event_id_max_value: 4294967295, }, }, }