From a41c05b06ba74e9e376983ddaf6810868757c412 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Wed, 9 Oct 2024 15:06:57 +0200 Subject: [PATCH] [#390] Finalize C example --- examples/c/event_multiplexing/README.md | 57 +++++++++++--------- examples/c/event_multiplexing/src/notifier.c | 4 ++ examples/c/event_multiplexing/src/wait.c | 57 ++++++++++++++++++-- 3 files changed, 89 insertions(+), 29 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..f6757b3a0 100644 --- a/examples/c/event_multiplexing/src/notifier.c +++ b/examples/c/event_multiplexing/src/notifier.c @@ -65,6 +65,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 +84,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..e9193b95b 100644 --- a/examples/c/event_multiplexing/src/wait.c +++ b/examples/c/event_multiplexing/src/wait.c @@ -21,8 +21,46 @@ 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; }; +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); +} + int main(int argc, char** argv) { if (argc != 3) { printf("Usage: %s SERVICE_NAME_1 SERVICE_NAME_2\n", argv[0]); @@ -47,7 +85,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 +95,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); @@ -115,10 +153,13 @@ int main(int argc, char** argv) { 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_wait_and_process( - // &waitset, iox2_waitset_run_callback callback, iox2_callback_context callback_ctx, &result); + iox2_waitset_run_result_e result; + 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,6 +187,12 @@ 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);