Skip to content

Commit

Permalink
[#390] Finalize C example
Browse files Browse the repository at this point in the history
  • Loading branch information
elfenpiff committed Oct 9, 2024
1 parent 63c3ae5 commit a41c05b
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 29 deletions.
57 changes: 33 additions & 24 deletions examples/c/event_multiplexing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
4 changes: 4 additions & 0 deletions examples/c/event_multiplexing/src/notifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -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(&notifier, &event_id, NULL) != IOX2_OK) {
Expand All @@ -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);

Expand Down
57 changes: 52 additions & 5 deletions examples/c/event_multiplexing/src/wait.c
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand All @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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);

Expand Down

0 comments on commit a41c05b

Please sign in to comment.