diff --git a/.clang-tidy b/.clang-tidy index 37cab666d..da2ab051e 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -52,7 +52,7 @@ CheckOptions: - { key: readability-identifier-naming.MemberCase, value: lower_case } - { key: readability-identifier-naming.ParameterCase, value: lower_case } - { key: readability-identifier-naming.VariableCase, value: lower_case } - - { key: readability-identifier-naming.PublicMemberPrefix, value: } + - { key: readability-identifier-naming.PublicMemberPrefix, value: "" } - { key: readability-identifier-naming.PrivateMemberPrefix, value: m_ } - { key: readability-identifier-naming.ProtectedMemberPrefix, value: m_ } - { key: readability-identifier-naming.MacroDefinitionPrefix, value: IOX2_ } diff --git a/examples/README.md b/examples/README.md index 612af6194..045e5d5ba 100644 --- a/examples/README.md +++ b/examples/README.md @@ -48,8 +48,8 @@ they interact and exchange data. | 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 | [Rust](rust/discovery) | List all available services in a system. | | docker | [all](rust/docker) | Communicate between different docker containers and the host. | -| event | [C++](cxx/event) [Rust](rust/event) | Push notifications - send event signals to wakeup processes that are waiting for them.| -| publish subscribe | [Rust](rust/publish_subscribe) | Communication between multiple processes with a [publish subscribe messaging pattern](https://en.wikipedia.org/wiki/Publish–subscribe_pattern). | +| event | [C](c/event) [C++](cxx/event) [Rust](rust/event) | Push notifications - send event signals to wakeup processes that are waiting for them.| +| 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 | [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 73a6bf378..6b8e3acd9 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -14,4 +14,5 @@ cmake_minimum_required(VERSION 3.22) project(examples_c LANGUAGES C) add_subdirectory(discovery) +add_subdirectory(event) add_subdirectory(publish_subscribe) diff --git a/examples/c/event/CMakeLists.txt b/examples/c/event/CMakeLists.txt new file mode 100644 index 000000000..106303f3d --- /dev/null +++ b/examples/c/event/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 LANGUAGES C) + +find_package(iceoryx2-c 0.3.0 REQUIRED) + +add_executable(example_c_event_listener src/listener.c) +target_link_libraries(example_c_event_listener iceoryx2-c::static-lib) + +add_executable(example_c_event_notifier src/notifier.c) +target_link_libraries(example_c_event_notifier iceoryx2-c::static-lib) diff --git a/examples/c/event/README.md b/examples/c/event/README.md new file mode 100644 index 000000000..846ae0092 --- /dev/null +++ b/examples/c/event/README.md @@ -0,0 +1,43 @@ +# Event + +## 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/src/listener.c b/examples/c/event/src/listener.c new file mode 100644 index 000000000..88e830ac6 --- /dev/null +++ b/examples/c/event/src/listener.c @@ -0,0 +1,81 @@ +// 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 + +int main(void) { + // 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 = "MyEventName"; + 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_node_ref_h node_ref_handle = iox2_cast_node_ref_h(node_handle); + iox2_service_builder_h service_builder = iox2_node_service_builder(node_ref_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 listener + iox2_port_factory_event_ref_h ref_service = iox2_cast_port_factory_event_ref_h(service); + iox2_port_factory_listener_builder_h listener_builder = iox2_port_factory_event_listener_builder(ref_service, NULL); + iox2_listener_h listener = NULL; + if (iox2_port_factory_listener_builder_create(listener_builder, NULL, &listener) != IOX2_OK) { + printf("Unable to create listener!\n"); + goto drop_service; + } + iox2_listener_ref_h listener_ref = iox2_cast_listener_ref_h(listener); + iox2_event_id_t event_id; + + while (iox2_node_wait(node_ref_handle, 0, 0) == iox2_node_event_e_TICK) { + bool has_received_one = false; + if (iox2_listener_timed_wait_one(listener_ref, &event_id, &has_received_one, 1, 0) != IOX2_OK) { + printf("Unable to wait for notification!\n"); + goto drop_listener; + } + + if (has_received_one) { + printf("event was triggered with id: %lu\n", event_id.value); + } + } + +drop_listener: + iox2_listener_drop(listener); + +drop_service: + iox2_port_factory_event_drop(service); + +drop_node: + iox2_node_drop(node_handle); + +end: + return 0; +} diff --git a/examples/c/event/src/notifier.c b/examples/c/event/src/notifier.c new file mode 100644 index 000000000..73ea6733f --- /dev/null +++ b/examples/c/event/src/notifier.c @@ -0,0 +1,90 @@ +// 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(void) { + // 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 = "MyEventName"; + 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_node_ref_h node_ref_handle = iox2_cast_node_ref_h(node_handle); + iox2_service_builder_h service_builder = iox2_node_service_builder(node_ref_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_event_ref_h ref_service = iox2_cast_port_factory_event_ref_h(service); + iox2_port_factory_notifier_builder_h notifier_builder = iox2_port_factory_event_notifier_builder(ref_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; + } + iox2_notifier_ref_h notifier_ref = iox2_cast_notifier_ref_h(notifier); + iox2_event_id_t event_id; + + uint64_t counter = 0; + while (iox2_node_wait(node_ref_handle, 0, 0) == iox2_node_event_e_TICK) { + counter += 1; + iox2_event_id_t event_id = { .value = counter % 12 }; // NOLINT + if (iox2_notifier_notify_with_custom_event_id(notifier_ref, &event_id, NULL) != IOX2_OK) { + printf("Failed to notify listener!\n"); + goto drop_notifier; + } + + printf("Trigger event with id %lu ...\n", (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/publish_subscribe/README.md b/examples/c/publish_subscribe/README.md new file mode 100644 index 000000000..020a7a3de --- /dev/null +++ b/examples/c/publish_subscribe/README.md @@ -0,0 +1,42 @@ +# Publish-Subscribe + +## Running The Example + +This example illustrates a robust publisher-subscriber communication +pattern between two separate processes. The publisher sends two +messages every second, each containing [`TransmissionData`]. On the +receiving end, the subscriber checks for new data every second. + +The subscriber is printing the sample on the console whenever new data arrives. + +First you have to build the C examples: + +```sh +cmake -S . -B target/ffi/build -DBUILD_EXAMPLES=ON +cmake --build target/ffi/build +``` + +To observe this dynamic communication in action, open two separate terminals +and execute the following commands: + +**Terminal 1** + +```sh +./target/ffi/build/examples/c/publish_subscribe/example_c_publish_subscribe_subscriber +``` + +**Terminal 2** + +```sh +./target/ffi/build/examples/c/publish_subscribe/example_c_publish_subscribe_publisher +``` + +Feel free to run multiple instances of publisher or subscriber processes +simultaneously to explore how iceoryx2 handles publisher-subscriber communication +efficiently. + +You may hit the maximum supported number of ports when too many publisher or +subscriber 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/publish_subscribe/src/publisher.c b/examples/c/publish_subscribe/src/publisher.c index 5ce78926b..b7ff7aeb3 100644 --- a/examples/c/publish_subscribe/src/publisher.c +++ b/examples/c/publish_subscribe/src/publisher.c @@ -11,23 +11,109 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT #include "iox2/iceoryx2.h" +#include "transmission_data.h" +#ifdef _WIN64 +#define alignof __alignof +#else +#include +#endif #include #include +#include int main(void) { + // create new node iox2_node_builder_h node_builder_handle = iox2_node_builder_new(NULL); iox2_node_h node_handle = NULL; - int ret_val = iox2_node_builder_create(node_builder_handle, NULL, iox2_service_type_e_IPC, &node_handle); - if (ret_val != IOX2_OK) { - printf("Could not create node! Error code: %i", ret_val); - return -1; + 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; } - const uint32_t NUMBER_OF_SECONDS_TO_RUN = 10; - ret_val = run_publisher(NUMBER_OF_SECONDS_TO_RUN); + // create service name + const char* service_name_value = "My/Funk/ServiceName"; + 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_node_ref_h node_ref_handle = iox2_cast_node_ref_h(node_handle); + iox2_service_builder_h service_builder = iox2_node_service_builder(node_ref_handle, NULL, service_name_ptr); + iox2_service_builder_pub_sub_h service_builder_pub_sub = iox2_service_builder_pub_sub(service_builder); + iox2_service_builder_pub_sub_ref_h service_builder_pub_sub_ref = + iox2_cast_service_builder_pub_sub_ref_h(service_builder_pub_sub); + + // set pub sub payload type + const char* payload_type_name = "16TransmissionData"; + if (iox2_service_builder_pub_sub_set_payload_type_details(service_builder_pub_sub_ref, + iox2_type_variant_e_FIXED_SIZE, + payload_type_name, + strlen(payload_type_name), + sizeof(struct TransmissionData), + alignof(struct TransmissionData)) + != IOX2_OK) { + printf("Unable to set type details\n"); + goto drop_node; + } + iox2_port_factory_pub_sub_h service = NULL; + if (iox2_service_builder_pub_sub_open_or_create(service_builder_pub_sub, NULL, &service) != IOX2_OK) { + printf("Unable to create service!\n"); + goto drop_node; + } + + // create publisher + iox2_port_factory_pub_sub_ref_h ref_service = iox2_cast_port_factory_pub_sub_ref_h(service); + iox2_port_factory_publisher_builder_h publisher_builder = + iox2_port_factory_pub_sub_publisher_builder(ref_service, NULL); + iox2_publisher_h publisher = NULL; + if (iox2_port_factory_publisher_builder_create(publisher_builder, NULL, &publisher) != IOX2_OK) { + printf("Unable to create publisher!\n"); + goto drop_service; + } + iox2_publisher_ref_h publisher_ref = iox2_cast_publisher_ref_h(publisher); + + int32_t counter = 0; + while (iox2_node_wait(node_ref_handle, 1, 0) == iox2_node_event_e_TICK) { + counter += 1; + + // loan sample + iox2_sample_mut_h sample = NULL; + if (iox2_publisher_loan(publisher_ref, NULL, &sample) != IOX2_OK) { + printf("Failed to loan sample\n"); + goto drop_publisher; + } + iox2_sample_mut_ref_h sample_ref = iox2_cast_sample_mut_ref_h(sample); + + // write payload + struct TransmissionData* payload = NULL; + iox2_sample_mut_payload_mut(sample_ref, (void**) &payload, NULL); + payload->x = counter; + payload->y = counter * 3; + payload->funky = counter * 812.12; // NOLINT + + // send sample + if (iox2_sample_mut_send(sample, NULL) != IOX2_OK) { + printf("Failed to send sample\n"); + goto drop_publisher; + } + + printf("Send sample %d ...\n", counter); + } + + +drop_publisher: + iox2_publisher_drop(publisher); + +drop_service: + iox2_port_factory_pub_sub_drop(service); +drop_node: iox2_node_drop(node_handle); - return ret_val; +end: + return 0; } diff --git a/examples/c/publish_subscribe/src/subscriber.c b/examples/c/publish_subscribe/src/subscriber.c index 63cedf890..7a2234e93 100644 --- a/examples/c/publish_subscribe/src/subscriber.c +++ b/examples/c/publish_subscribe/src/subscriber.c @@ -11,10 +11,103 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT #include "iox2/iceoryx2.h" +#include "transmission_data.h" +#ifdef _WIN64 +#define alignof __alignof +#else +#include +#endif #include +#include +#include int main(void) { - const uint32_t NUMBER_OF_SECONDS_TO_RUN = 10; - return run_subscriber(NUMBER_OF_SECONDS_TO_RUN); + // 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 = "My/Funk/ServiceName"; + 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_node_ref_h node_ref_handle = iox2_cast_node_ref_h(node_handle); + iox2_service_builder_h service_builder = iox2_node_service_builder(node_ref_handle, NULL, service_name_ptr); + iox2_service_builder_pub_sub_h service_builder_pub_sub = iox2_service_builder_pub_sub(service_builder); + iox2_service_builder_pub_sub_ref_h service_builder_pub_sub_ref = + iox2_cast_service_builder_pub_sub_ref_h(service_builder_pub_sub); + + // set pub sub payload type + const char* payload_type_name = "16TransmissionData"; + if (iox2_service_builder_pub_sub_set_payload_type_details(service_builder_pub_sub_ref, + iox2_type_variant_e_FIXED_SIZE, + payload_type_name, + strlen(payload_type_name), + sizeof(struct TransmissionData), + alignof(struct TransmissionData)) + != IOX2_OK) { + printf("Unable to set type details\n"); + goto drop_node; + } + iox2_port_factory_pub_sub_h service = NULL; + if (iox2_service_builder_pub_sub_open_or_create(service_builder_pub_sub, NULL, &service) != IOX2_OK) { + printf("Unable to create service!\n"); + goto drop_node; + } + + // create subscriber + iox2_port_factory_pub_sub_ref_h ref_service = iox2_cast_port_factory_pub_sub_ref_h(service); + iox2_port_factory_subscriber_builder_h subscriber_builder = + iox2_port_factory_pub_sub_subscriber_builder(ref_service, NULL); + iox2_subscriber_h subscriber = NULL; + if (iox2_port_factory_subscriber_builder_create(subscriber_builder, NULL, &subscriber) != IOX2_OK) { + printf("Unable to create subscriber!\n"); + goto drop_service; + } + iox2_subscriber_ref_h subscriber_ref = iox2_cast_subscriber_ref_h(subscriber); + + uint64_t counter = 0; + while (iox2_node_wait(node_ref_handle, 1, 0) == iox2_node_event_e_TICK) { + counter += 1; + + // receive sample + iox2_sample_h sample = NULL; + if (iox2_subscriber_receive(subscriber_ref, NULL, &sample) != IOX2_OK) { + printf("Failed to receive sample\n"); + goto drop_subscriber; + } + + if (sample != NULL) { + iox2_sample_ref_h sample_ref = iox2_cast_sample_ref_h(sample); + struct TransmissionData* payload = NULL; + iox2_sample_payload(sample_ref, (const void**) &payload, NULL); + + printf( + "received: TransmissionData { .x: %d, .y: %d, .funky: %lf}\n", payload->x, payload->y, payload->funky); + iox2_sample_drop(sample); + } + } + + +drop_subscriber: + iox2_subscriber_drop(subscriber); + +drop_service: + iox2_port_factory_pub_sub_drop(service); + +drop_node: + iox2_node_drop(node_handle); + +end: + return 0; } diff --git a/examples/c/publish_subscribe/src/transmission_data.h b/examples/c/publish_subscribe/src/transmission_data.h new file mode 100644 index 000000000..b9130321a --- /dev/null +++ b/examples/c/publish_subscribe/src/transmission_data.h @@ -0,0 +1,24 @@ +// 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 + +#ifndef IOX2_EXAMPLES_TRANSMISSION_DATA_H +#define IOX2_EXAMPLES_TRANSMISSION_DATA_H + +#include + +struct TransmissionData { + int32_t x; + int32_t y; + double funky; +}; + +#endif diff --git a/examples/cxx/README.md b/examples/cxx/README.md index 5467d0066..06290153b 100644 --- a/examples/cxx/README.md +++ b/examples/cxx/README.md @@ -8,26 +8,3 @@ In the repository root folder, execute this steps. cmake -S . -B target/ffi/build -DBUILD_EXAMPLES=ON cmake --build target/ffi/build ``` - -## Run Examples - -### Publish-Subscribe - -Run in two separate terminals. Note, currently the examples run for 10 seconds. - - -```bash -target/ffi/build/examples/cxx/publish_subscribe/example_cxx_publisher -``` - - -```bash -target/ffi/build/examples/cxx/publish_subscribe/example_cxx_subscriber -``` - -### Discovery - - -```bash -target/ffi/build/examples/cxx/discovery/example_cxx_discovery -``` diff --git a/examples/cxx/publish_subscribe/README.md b/examples/cxx/publish_subscribe/README.md new file mode 100644 index 000000000..9de5dd4e1 --- /dev/null +++ b/examples/cxx/publish_subscribe/README.md @@ -0,0 +1,42 @@ +# Publish-Subscribe + +## Running The Example + +This example illustrates a robust publisher-subscriber communication +pattern between two separate processes. The publisher sends two +messages every second, each containing [`TransmissionData`]. On the +receiving end, the subscriber checks for new data every second. + +The subscriber is printing the sample on the console whenever new data arrives. + +First you have to build the C++ examples: + +```sh +cmake -S . -B target/ffi/build -DBUILD_EXAMPLES=ON +cmake --build target/ffi/build +``` + +To observe this dynamic communication in action, open two separate terminals +and execute the following commands: + +**Terminal 1** + +```sh +./target/ffi/build/examples/cxx/publish_subscribe/example_cxx_publish_subscribe_subscriber +``` + +**Terminal 2** + +```sh +./target/ffi/build/examples/cxx/publish_subscribe/example_cxx_publish_subscribe_publisher +``` + +Feel free to run multiple instances of publisher or subscriber processes +simultaneously to explore how iceoryx2 handles publisher-subscriber communication +efficiently. + +You may hit the maximum supported number of ports when too many publisher or +subscriber 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/cxx/publish_subscribe/src/publisher.cpp b/examples/cxx/publish_subscribe/src/publisher.cpp index d5d41cce7..8a0993091 100644 --- a/examples/cxx/publish_subscribe/src/publisher.cpp +++ b/examples/cxx/publish_subscribe/src/publisher.cpp @@ -36,6 +36,7 @@ auto main() -> int { auto counter = 0; while (node.wait(CYCLE_TIME) == NodeEvent::Tick) { counter += 1; + auto sample = publisher.loan_uninit().expect("acquire sample"); sample.write_payload(TransmissionData { counter, counter * 3, counter * 812.12 }); // NOLINT diff --git a/examples/cxx/publish_subscribe/src/transmission_data.hpp b/examples/cxx/publish_subscribe/src/transmission_data.hpp index 79cd37998..48ac150fa 100644 --- a/examples/cxx/publish_subscribe/src/transmission_data.hpp +++ b/examples/cxx/publish_subscribe/src/transmission_data.hpp @@ -23,7 +23,7 @@ struct TransmissionData { }; inline auto operator<<(std::ostream& stream, const TransmissionData& value) -> std::ostream& { - std::cout << "TransmissionData { x: " << value.x << ", y: " << value.y << ", funky: " << value.funky << "}"; + std::cout << "TransmissionData { x: " << value.x << ", y: " << value.y << ", funky: " << value.funky << " }"; return stream; } diff --git a/iceoryx2-ffi/cxx/CMakeLists.txt b/iceoryx2-ffi/cxx/CMakeLists.txt index 802a04e18..7a8a7046d 100644 --- a/iceoryx2-ffi/cxx/CMakeLists.txt +++ b/iceoryx2-ffi/cxx/CMakeLists.txt @@ -39,6 +39,7 @@ add_library(iceoryx2-cxx-object-lib OBJECT src/config.cpp src/event_id.cpp src/listener.cpp + src/message_type_details.cpp src/node.cpp src/node_details.cpp src/node_name.cpp @@ -51,6 +52,7 @@ add_library(iceoryx2-cxx-object-lib OBJECT src/service_builder_event.cpp src/service_name.cpp src/static_config_event.cpp + src/static_config_publish_subscribe.cpp ) set_target_properties(iceoryx2-cxx-object-lib diff --git a/iceoryx2-ffi/cxx/include/iox/slice.hpp b/iceoryx2-ffi/cxx/include/iox/slice.hpp index 3ab02a695..c0fcae70a 100644 --- a/iceoryx2-ffi/cxx/include/iox/slice.hpp +++ b/iceoryx2-ffi/cxx/include/iox/slice.hpp @@ -23,6 +23,7 @@ class Slice { public: using Iterator = T*; using ConstIterator = const T*; + using ValueType = T; auto size() const -> uint64_t { IOX_TODO(); @@ -46,6 +47,16 @@ class Slice { IOX_TODO(); } }; + +template +struct IsSlice { + static constexpr bool VALUE = false; +}; + +template +struct IsSlice> { + static constexpr bool VALUE = true; +}; } // namespace iox #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp b/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp index 3d9d708c7..261605486 100644 --- a/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/enum_translation.hpp @@ -22,11 +22,14 @@ #include "iox2/node_event.hpp" #include "iox2/node_failure_enums.hpp" #include "iox2/notifier_error.hpp" +#include "iox2/publisher_error.hpp" #include "iox2/semantic_string.hpp" #include "iox2/service_builder_event_error.hpp" #include "iox2/service_builder_publish_subscribe_error.hpp" #include "iox2/service_error_enums.hpp" #include "iox2/service_type.hpp" +#include "iox2/subscriber_error.hpp" +#include "iox2/type_variant.hpp" namespace iox { template <> @@ -461,6 +464,98 @@ constexpr auto from(const int value) noexcept -> i IOX_UNREACHABLE(); } +template <> +constexpr auto from(const int value) noexcept -> iox2::PublisherCreateError { + const auto error = static_cast(value); + switch (error) { + case iox2_publisher_create_error_e_EXCEEDS_MAX_SUPPORTED_PUBLISHERS: + return iox2::PublisherCreateError::ExceedsMaxSupportedPublishers; + case iox2_publisher_create_error_e_UNABLE_TO_CREATE_DATA_SEGMENT: + return iox2::PublisherCreateError::UnableToCreateDataSegment; + } + + IOX_UNREACHABLE(); +} + +template <> +constexpr auto from(const int value) noexcept -> iox2::SubscriberCreateError { + const auto error = static_cast(value); + switch (error) { + case iox2_subscriber_create_error_e_BUFFER_SIZE_EXCEEDS_MAX_SUPPORTED_BUFFER_SIZE_OF_SERVICE: + return iox2::SubscriberCreateError::BufferSizeExceedsMaxSupportedBufferSizeOfService; + case iox2_subscriber_create_error_e_EXCEEDS_MAX_SUPPORTED_SUBSCRIBERS: + return iox2::SubscriberCreateError::ExceedsMaxSupportedSubscribers; + } + + IOX_UNREACHABLE(); +} + +template <> +constexpr auto from(const int value) noexcept -> iox2::PublisherSendError { + const auto error = static_cast(value); + switch (error) { + case iox2_publisher_send_error_e_CONNECTION_BROKEN_SINCE_PUBLISHER_NO_LONGER_EXISTS: + return iox2::PublisherSendError::ConnectionBrokenSincePublisherNoLongerExists; + case iox2_publisher_send_error_e_CONNECTION_CORRUPTED: + return iox2::PublisherSendError::ConnectionCorrupted; + case iox2_publisher_send_error_e_LOAN_ERROR_OUT_OF_MEMORY: + return iox2::PublisherSendError::LoanErrorOutOfMemory; + case iox2_publisher_send_error_e_LOAN_ERROR_EXCEEDS_MAX_LOANED_SAMPLES: + return iox2::PublisherSendError::LoanErrorExceedsMaxLoanedSamples; + case iox2_publisher_send_error_e_LOAN_ERROR_EXCEEDS_MAX_LOAN_SIZE: + return iox2::PublisherSendError::LoanErrorExceedsMaxLoanSize; + case iox2_publisher_send_error_e_LOAN_ERROR_INTERNAL_FAILURE: + return iox2::PublisherSendError::LoanErrorInternalFailure; + case iox2_publisher_send_error_e_CONNECTION_ERROR: + return iox2::PublisherSendError::ConnectionError; + } + + IOX_UNREACHABLE(); +} + +template <> +constexpr auto from(const int value) noexcept -> iox2::SubscriberReceiveError { + const auto error = static_cast(value); + switch (error) { + case iox2_subscriber_receive_error_e_CONNECTION_FAILURE: + return iox2::SubscriberReceiveError::ConnectionFailure; + case iox2_subscriber_receive_error_e_EXCEEDS_MAX_BORROWED_SAMPLES: + return iox2::SubscriberReceiveError::ExceedsMaxBorrowedSamples; + } + + IOX_UNREACHABLE(); +} + +template <> +constexpr auto from(const int value) noexcept -> iox2::PublisherLoanError { + const auto error = static_cast(value); + switch (error) { + case iox2_publisher_loan_error_e_EXCEEDS_MAX_LOANED_SAMPLES: + return iox2::PublisherLoanError::ExceedsMaxLoanedSamples; + case iox2_publisher_loan_error_e_OUT_OF_MEMORY: + return iox2::PublisherLoanError::OutOfMemory; + case iox2_publisher_loan_error_e_EXCEEDS_MAX_LOAN_SIZE: + return iox2::PublisherLoanError::ExceedsMaxLoanSize; + case iox2_publisher_loan_error_e_INTERNAL_FAILURE: + return iox2::PublisherLoanError::InternalFailure; + } + + IOX_UNREACHABLE(); +} + +template <> +constexpr auto from(const int value) noexcept -> iox2::TypeVariant { + const auto variant = static_cast(value); + switch (variant) { + case iox2_type_variant_e_DYNAMIC: + return iox2::TypeVariant::Dynamic; + case iox2_type_variant_e_FIXED_SIZE: + return iox2::TypeVariant::FixedSize; + } + + IOX_UNREACHABLE(); +} + } // namespace iox diff --git a/iceoryx2-ffi/cxx/include/iox2/message_type_details.hpp b/iceoryx2-ffi/cxx/include/iox2/message_type_details.hpp index 80dff636a..41d5db6d8 100644 --- a/iceoryx2-ffi/cxx/include/iox2/message_type_details.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/message_type_details.hpp @@ -13,44 +13,54 @@ #ifndef IOX2_MESSAGE_TYPE_DETAILS_HPP #define IOX2_MESSAGE_TYPE_DETAILS_HPP -#include "iox/string.hpp" -#include "iox2/iceoryx2_settings.hpp" +#include "iox2/internal/iceoryx2.hpp" +#include "iox2/type_variant.hpp" -#include namespace iox2 { -/// Defines if the type is a slice with a runtime-size -/// ([`TypeVariant::Dynamic`]) or if its a type that satisfies [`Sized`] -/// ([`TypeVariant::FixedSize`]). -enum class TypeVariant : uint8_t { - /// A fixed size type like [`u64`] - FixedSize, - /// A dynamic sized type like a slice - Dynamic, -}; - /// Contains all type details required to connect to a /// [`crate::service::Service`] -struct TypeDetail { +class TypeDetail { + public: /// The [`TypeVariant`] of the type - TypeVariant variant; - /// Contains the output of [`core::any::type_name()`]. - iox::string type_name; + auto variant() const -> TypeVariant; + + /// Contains the output of [`typeid().name`]. + auto type_name() const -> const char*; + /// The size of the underlying type. - uint64_t size; + auto size() const -> size_t; + /// The alignment of the underlying type. - uint64_t alignment; + auto alignment() const -> size_t; + + private: + friend class MessageTypeDetails; + explicit TypeDetail(iox2_type_detail_t value); + + iox2_type_detail_t m_value; }; -struct MessageTypeDetails { +/// Contains all type information to the header and payload type. +class MessageTypeDetails { + public: /// The [`TypeDetail`] of the header of a message, the first iceoryx2 /// internal part. - TypeDetail header; + auto header() const -> TypeDetail; + /// The [`TypeDetail`] of the user_header or the custom header, is located /// directly after the header. - TypeDetail user_header; + auto user_header() const -> TypeDetail; + /// The [`TypeDetail`] of the payload of the message, the last part. - TypeDetail payload; + auto payload() const -> TypeDetail; + + private: + friend class StaticConfigPublishSubscribe; + + explicit MessageTypeDetails(iox2_message_type_details_t value); + + iox2_message_type_details_t m_value; }; } // namespace iox2 diff --git a/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp b/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp index a6ce390af..53384c679 100644 --- a/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/port_factory_publish_subscribe.hpp @@ -54,7 +54,7 @@ class PortFactoryPublishSubscribe { /// Returns the StaticConfig of the [`Service`]. /// Contains all settings that never change during the lifetime of the service. - auto static_config() const -> const StaticConfigPublishSubscribe&; + auto static_config() const -> StaticConfigPublishSubscribe; /// Returns the DynamicConfig of the [`Service`]. /// Contains all dynamic settings, like the current participants etc.. @@ -136,9 +136,12 @@ inline auto PortFactoryPublishSubscribe::attributes() co } template -inline auto -PortFactoryPublishSubscribe::static_config() const -> const StaticConfigPublishSubscribe& { - IOX_TODO(); +inline auto PortFactoryPublishSubscribe::static_config() const -> StaticConfigPublishSubscribe { + auto* ref_handle = iox2_cast_port_factory_pub_sub_ref_h(m_handle); + iox2_static_config_publish_subscribe_t static_config {}; + iox2_port_factory_pub_sub_static_config(ref_handle, &static_config); + + return StaticConfigPublishSubscribe(static_config); } template @@ -156,13 +159,17 @@ inline auto PortFactoryPublishSubscribe::nodes( template inline auto PortFactoryPublishSubscribe::subscriber_builder() const -> PortFactorySubscriber { - IOX_TODO(); + auto* ref_handle = iox2_cast_port_factory_pub_sub_ref_h(m_handle); + return PortFactorySubscriber( + iox2_port_factory_pub_sub_subscriber_builder(ref_handle, nullptr)); } template inline auto PortFactoryPublishSubscribe::publisher_builder() const -> PortFactoryPublisher { - IOX_TODO(); + auto* ref_handle = iox2_cast_port_factory_pub_sub_ref_h(m_handle); + return PortFactoryPublisher( + iox2_port_factory_pub_sub_publisher_builder(ref_handle, nullptr)); } diff --git a/iceoryx2-ffi/cxx/include/iox2/port_factory_publisher.hpp b/iceoryx2-ffi/cxx/include/iox2/port_factory_publisher.hpp index 25b20c29d..4716adfa8 100644 --- a/iceoryx2-ffi/cxx/include/iox2/port_factory_publisher.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/port_factory_publisher.hpp @@ -16,28 +16,85 @@ #include "iox/assertions_addendum.hpp" #include "iox/builder_addendum.hpp" #include "iox/expected.hpp" -#include "publisher.hpp" -#include "service_type.hpp" +#include "iox2/internal/iceoryx2.hpp" +#include "iox2/publisher.hpp" +#include "iox2/service_type.hpp" #include namespace iox2 { +/// Defines the strategy the [`Publisher`] shall pursue in +/// [`send_sample(`] or +/// [`Publisher::send_copy()`] when the buffer of a +/// [`Subscriber`] is full and the service does not overflow. enum class UnableToDeliverStrategy : uint8_t { + /// Blocks until the [`Subscriber`] has consumed the + /// [`Sample`] from the buffer and there is space again Block, + /// Do not deliver the [`Sample`]. DiscardSample }; +/// Factory to create a new [`Publisher`] port/endpoint for +/// [`MessagingPattern::PublishSubscribe`] based communication. template class PortFactoryPublisher { + /// Sets the [`UnableToDeliverStrategy`]. IOX_BUILDER_OPTIONAL(UnableToDeliverStrategy, unable_to_deliver_strategy); + + /// Defines how many [`SampleMut`] the [`Publisher`] can loan with + /// [`Publisher::loan()`] or [`Publisher::loan_uninit()`] in parallel. IOX_BUILDER_OPTIONAL(uint64_t, max_loaned_samples); + + /// Sets the maximum slice length that a user can allocate with + /// [`Publisher::loan_slice()`] or [`Publisher::loan_slice_uninit()`]. IOX_BUILDER_OPTIONAL(uint64_t, max_slice_len); public: - auto create() && -> iox::expected, PublisherCreateError> { - IOX_TODO(); - } + PortFactoryPublisher(const PortFactoryPublisher&) = delete; + PortFactoryPublisher(PortFactoryPublisher&&) = default; + auto operator=(const PortFactoryPublisher&) -> PortFactoryPublisher& = delete; + auto operator=(PortFactoryPublisher&&) -> PortFactoryPublisher& = default; + ~PortFactoryPublisher() = default; + + /// Creates a new [`Publisher`] or returns a [`PublisherCreateError`] on failure. + auto create() && -> iox::expected, PublisherCreateError>; + + private: + template + friend class PortFactoryPublishSubscribe; + + explicit PortFactoryPublisher(iox2_port_factory_publisher_builder_h handle); + + iox2_port_factory_publisher_builder_h m_handle; }; + +template +inline PortFactoryPublisher::PortFactoryPublisher(iox2_port_factory_publisher_builder_h handle) + : m_handle { handle } { +} + +template +inline auto +PortFactoryPublisher::create() && -> iox::expected, + PublisherCreateError> { + auto* ref_handle = iox2_cast_port_factory_publisher_builder_ref_h(m_handle); + + m_unable_to_deliver_strategy.and_then([](auto) { IOX_TODO(); }); + m_max_slice_len.and_then([](auto) { IOX_TODO(); }); + m_max_loaned_samples.and_then( + [&](auto value) { iox2_port_factory_publisher_builder_set_max_loaned_samples(ref_handle, value); }); + + iox2_publisher_h pub_handle {}; + + auto result = iox2_port_factory_publisher_builder_create(m_handle, nullptr, &pub_handle); + + if (result == IOX2_OK) { + return iox::ok(Publisher(pub_handle)); + } + + return iox::err(iox::into(result)); +} } // namespace iox2 #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/port_factory_subscriber.hpp b/iceoryx2-ffi/cxx/include/iox2/port_factory_subscriber.hpp index 443dc64d9..26d2d074f 100644 --- a/iceoryx2-ffi/cxx/include/iox2/port_factory_subscriber.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/port_factory_subscriber.hpp @@ -16,22 +16,63 @@ #include "iox/assertions_addendum.hpp" #include "iox/builder_addendum.hpp" #include "iox/expected.hpp" -#include "service_type.hpp" -#include "subscriber.hpp" +#include "iox2/internal/iceoryx2.hpp" +#include "iox2/service_type.hpp" +#include "iox2/subscriber.hpp" #include namespace iox2 { +/// Factory to create a new [`Subscriber`] port/endpoint for +/// [`MessagingPattern::PublishSubscribe`] based communication. template class PortFactorySubscriber { - IOX_BUILDER_OPTIONAL(uint64_t, history_size); + /// Defines the required buffer size of the [`Subscriber`]. Smallest possible value is `1`. + IOX_BUILDER_OPTIONAL(uint64_t, buffer_size); public: - auto create() && -> iox::expected, SubscriberCreateError> { - IOX_TODO(); - } + PortFactorySubscriber(const PortFactorySubscriber&) = delete; + PortFactorySubscriber(PortFactorySubscriber&&) = default; + auto operator=(const PortFactorySubscriber&) -> PortFactorySubscriber& = delete; + auto operator=(PortFactorySubscriber&&) -> PortFactorySubscriber& = default; + ~PortFactorySubscriber() = default; + + /// Creates a new [`Subscriber`] or returns a [`SubscriberCreateError`] on failure. + auto create() && -> iox::expected, SubscriberCreateError>; + + private: + template + friend class PortFactoryPublishSubscribe; + + explicit PortFactorySubscriber(iox2_port_factory_subscriber_builder_h handle); + + iox2_port_factory_subscriber_builder_h m_handle; }; + +template +inline PortFactorySubscriber::PortFactorySubscriber( + iox2_port_factory_subscriber_builder_h handle) + : m_handle { handle } { +} + +template +inline auto +PortFactorySubscriber::create() && -> iox::expected, + SubscriberCreateError> { + auto* ref_handle = iox2_cast_port_factory_subscriber_builder_ref_h(m_handle); + m_buffer_size.and_then( + [&](auto value) { iox2_port_factory_subscriber_builder_set_buffer_size(ref_handle, value); }); + + iox2_subscriber_h sub_handle {}; + auto result = iox2_port_factory_subscriber_builder_create(m_handle, nullptr, &sub_handle); + + if (result == IOX2_OK) { + return iox::ok(Subscriber(sub_handle)); + } + + return iox::err(iox::into(result)); +} } // namespace iox2 #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp index 1de9e65d9..35de7e6bd 100644 --- a/iceoryx2-ffi/cxx/include/iox2/publisher.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/publisher.hpp @@ -13,92 +13,183 @@ #ifndef IOX2_PUBLISHER_HPP #define IOX2_PUBLISHER_HPP -#include "connection_failure.hpp" #include "iox/assertions_addendum.hpp" #include "iox/expected.hpp" -#include "sample_mut.hpp" -#include "service_type.hpp" -#include "unique_port_id.hpp" +#include "iox2/connection_failure.hpp" +#include "iox2/iceoryx2.h" +#include "iox2/internal/iceoryx2.hpp" +#include "iox2/publisher_error.hpp" +#include "iox2/sample_mut.hpp" +#include "iox2/service_type.hpp" +#include "iox2/unique_port_id.hpp" #include +#include namespace iox2 { -enum class PublisherCreateError : uint8_t { - /// The maximum amount of [`Publisher`]s that can connect to a - /// [`Service`](crate::service::Service) is - /// defined in [`crate::config::Config`]. When this is exceeded no more - /// [`Publisher`]s - /// can be created for a specific [`Service`](crate::service::Service). - ExceedsMaxSupportedPublishers, - /// The datasegment in which the payload of the [`Publisher`] is stored, - /// could not be created. - UnableToCreateDataSegment, -}; - -/// Defines a failure that can occur in [`Publisher::loan()`] and -/// [`Publisher::loan_uninit()`] or is part of [`PublisherSendError`] emitted in -/// [`Publisher::send_copy()`]. -enum class PublisherLoanError : uint8_t { - /// The [`Publisher`]s data segment does not have any more memory left - OutOfMemory, - /// The maximum amount of [`SampleMut`]s a user can borrow with - /// [`Publisher::loan()`] or - /// [`Publisher::loan_uninit()`] is - /// defined in [`crate::config::Config`]. When this is exceeded those calls - /// will fail. - ExceedsMaxLoanedSamples, - /// The provided slice size exceeds the configured max slice size of the - /// [`Publisher`]. - /// To send a [`SampleMut`] with this size a new [`Publisher`] has to be - /// created with - /// a - /// [`crate::service::port_factory::publisher::PortFactoryPublisher::max_slice_len()`] - /// greater or equal to the required len. - ExceedsMaxLoanSize, - /// Errors that indicate either an implementation issue or a wrongly - /// configured system. - InternalFailure, -}; - +/// Sending endpoint of a publish-subscriber based communication. template class Publisher { public: - Publisher() = default; - Publisher(Publisher&&) = default; - auto operator=(Publisher&&) -> Publisher& = default; - ~Publisher() = default; + Publisher(Publisher&& rhs) noexcept; + auto operator=(Publisher&& rhs) noexcept -> Publisher&; + ~Publisher(); Publisher(const Publisher&) = delete; auto operator=(const Publisher&) -> Publisher& = delete; - auto id() const -> UniquePublisherId { - IOX_TODO(); - } - auto send_copy(const Payload& payload) const -> iox::expected { - IOX_TODO(); - } + /// Returns the [`UniquePublisherId`] of the [`Publisher`] + auto id() const -> UniquePublisherId; - auto loan_uninit() -> iox::expected, PublisherLoanError> { - IOX_TODO(); - } + /// Copies the input `value` into a [`SampleMut`] and delivers it. + /// On success it returns the number of [`Subscriber`]s that received + /// the data, otherwise a [`PublisherSendError`] describing the failure. + auto send_copy(const Payload& payload) const -> iox::expected; + + /// Loans/allocates a [`SampleMut`] from the underlying data segment of the [`Publisher`]. + /// The user has to initialize the payload before it can be sent. + /// + /// On failure it returns [`PublisherLoanError`] describing the failure. + auto loan_uninit() -> iox::expected, PublisherLoanError>; + + /// Loans/allocates a [`SampleMut`] from the underlying data segment of the [`Publisher`] + /// and initialize it with the default value. This can be a performance hit and [`Publisher::loan_uninit`] + /// can be used to loan an uninitalized [`SampleMut`]. + /// + /// On failure it returns [`PublisherLoanError`] describing the failure. + auto loan() -> iox::expected, PublisherLoanError>; + + /// Loans/allocates a [`SampleMut`] from the underlying data segment of the [`Publisher`] + /// and initializes all slice elements with the default value. This can be a performance hit + /// and [`Publisher::loan_slice_uninit()`] can be used to loan a slice of uninitialized + /// [`Payload`]. + /// + /// On failure it returns [`PublisherLoanError`] describing the failure. + template ::VALUE, void>> + auto loan_slice(uint64_t number_of_elements) -> iox::expected, PublisherLoanError>; + + /// Loans/allocates a [`SampleMut`] from the underlying data segment of the [`Publisher`]. + /// The user has to initialize the payload before it can be sent. + /// + /// On failure it returns [`PublisherLoanError`] describing the failure. + template ::VALUE, void>> + auto + loan_slice_uninit(uint64_t number_of_elements) -> iox::expected, PublisherLoanError>; + + /// Explicitly updates all connections to the [`Subscriber`]s. This is + /// required to be called whenever a new [`Subscriber`] is connected to + /// the service. It is called implicitly whenever [`SampleMut::send()`] or + /// [`Publisher::send_copy()`] is called. + /// When a [`Subscriber`] is connected that requires a history this + /// call will deliver it. + auto update_connections() -> iox::expected; - auto loan() -> iox::expected, PublisherLoanError> { - IOX_TODO(); + private: + template + friend class PortFactoryPublisher; + + explicit Publisher(iox2_publisher_h handle); + void drop(); + + iox2_publisher_h m_handle { nullptr }; +}; + +template +inline Publisher::Publisher(iox2_publisher_h handle) + : m_handle { handle } { +} + +template +inline void Publisher::drop() { + if (m_handle != nullptr) { + iox2_publisher_drop(m_handle); + m_handle = nullptr; } +} + +template +inline Publisher::Publisher(Publisher&& rhs) noexcept { + *this = std::move(rhs); +} - auto loan_slice(const uint64_t number_of_elements) - -> iox::expected, PublisherLoanError> { - IOX_TODO(); +template +inline auto Publisher::operator=(Publisher&& rhs) noexcept -> Publisher& { + if (this != &rhs) { + drop(); + m_handle = std::move(rhs.m_handle); + rhs.m_handle = nullptr; } - auto loan_slice_uninit(const uint64_t number_of_elements) - -> iox::expected, PublisherLoanError> { - IOX_TODO(); + + return *this; +} + +template +inline Publisher::~Publisher() { + drop(); +} + +template +inline auto Publisher::id() const -> UniquePublisherId { + IOX_TODO(); +} + +template +inline auto Publisher::send_copy(const Payload& payload) const + -> iox::expected { + static_assert(std::is_trivially_copyable::value); + + auto* ref_handle = iox2_cast_publisher_ref_h(m_handle); + + size_t number_of_recipients = 0; + auto result = iox2_publisher_send_copy( + ref_handle, static_cast(&payload), sizeof(Payload), &number_of_recipients); + + if (result == IOX2_OK) { + return iox::ok(number_of_recipients); } - auto update_connections() -> iox::expected { - IOX_TODO(); + return iox::err(iox::into(result)); +} + +template +inline auto Publisher::loan_uninit() + -> iox::expected, PublisherLoanError> { + auto* ref_handle = iox2_cast_publisher_ref_h(m_handle); + iox2_sample_mut_h sample_handle {}; + + auto result = iox2_publisher_loan(ref_handle, nullptr, &sample_handle); + + if (result == IOX2_OK) { + return iox::ok(SampleMut(sample_handle)); } -}; + + return iox::err(iox::into(result)); +} + +template +inline auto +Publisher::loan() -> iox::expected, PublisherLoanError> { + return loan_uninit().and_then([](auto& sample) { new (&sample.payload_mut()) Payload(); }); +} + +template +template +inline auto Publisher::loan_slice(const uint64_t number_of_elements) + -> iox::expected, PublisherLoanError> { + IOX_TODO(); +} + +template +template +inline auto Publisher::loan_slice_uninit(const uint64_t number_of_elements) + -> iox::expected, PublisherLoanError> { + IOX_TODO(); +} + +template +inline auto Publisher::update_connections() -> iox::expected { + IOX_TODO(); +} } // namespace iox2 #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/publisher_error.hpp b/iceoryx2-ffi/cxx/include/iox2/publisher_error.hpp new file mode 100644 index 000000000..27118a78d --- /dev/null +++ b/iceoryx2-ffi/cxx/include/iox2/publisher_error.hpp @@ -0,0 +1,87 @@ +// 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 + +#ifndef IOX2_PUBLISHER_ERROR_HPP +#define IOX2_PUBLISHER_ERROR_HPP + +#include + +namespace iox2 { + +/// Defines a failure that can occur when a [`Publisher`] is created with +/// [`PortFactoryPublisher`]. +enum class PublisherCreateError : uint8_t { + /// The maximum amount of [`Publisher`]s that can connect to a + /// [`Service`] is + /// defined in [`Config`]. When this is exceeded no more + /// [`Publisher`]s + /// can be created for a specific [`Service`]. + ExceedsMaxSupportedPublishers, + /// The datasegment in which the payload of the [`Publisher`] is stored, + /// could not be created. + UnableToCreateDataSegment, +}; + +/// Defines a failure that can occur in [`Publisher::loan()`] and +/// [`Publisher::loan_uninit()`] or is part of [`PublisherSendError`] emitted in +/// [`Publisher::send_copy()`]. +enum class PublisherLoanError : uint8_t { + /// The [`Publisher`]s data segment does not have any more memory left + OutOfMemory, + /// The maximum amount of [`SampleMut`]s a user can borrow with + /// [`Publisher::loan()`] or + /// [`Publisher::loan_uninit()`] is + /// defined in [`Config`]. When this is exceeded those calls + /// will fail. + ExceedsMaxLoanedSamples, + /// The provided slice size exceeds the configured max slice size of the + /// [`Publisher`]. + /// To send a [`SampleMut`] with this size a new [`Publisher`] has to be + /// created with + /// a + /// [`PortFactoryPublisher::max_slice_len()`] + /// greater or equal to the required len. + ExceedsMaxLoanSize, + /// Errors that indicate either an implementation issue or a wrongly + /// configured system. + InternalFailure, +}; + +/// Failure that can be emitted when a [`SampleMut`] is sent via [`SampleMut::send()`]. +enum class PublisherSendError : uint8_t { + /// [`SampleMut::send()`] was called but the corresponding [`Publisher`] went already out of + /// scope. + ConnectionBrokenSincePublisherNoLongerExists, + /// A connection between a [`Subscriber`] and a + /// [`Publisher`] is corrupted. + ConnectionCorrupted, + /// A failure occurred while acquiring memory for the payload + /// The [`Publisher`]s data segment does not have any more memory left + LoanErrorOutOfMemory, + /// The maximum amount of [`SampleMut`]s a user can borrow with [`Publisher::loan()`] or + /// [`Publisher::loan_uninit()`] is + /// defined in [`crate::config::Config`]. When this is exceeded those calls will fail. + LoanErrorExceedsMaxLoanedSamples, + /// The provided slice size exceeds the configured max slice size of the [`Publisher`]. + /// To send a [`SampleMut`] with this size a new [`Publisher`] has to be created with + /// a [`crate::service::port_factory::publisher::PortFactoryPublisher::max_slice_len()`] + /// greater or equal to the required len. + LoanErrorExceedsMaxLoanSize, + /// Errors that indicate either an implementation issue or a wrongly configured system. + LoanErrorInternalFailure, + /// A failure occurred while establishing a connection to a + /// [`Subscriber`] + ConnectionError, +}; +} // namespace iox2 + +#endif diff --git a/iceoryx2-ffi/cxx/include/iox2/sample.hpp b/iceoryx2-ffi/cxx/include/iox2/sample.hpp index 994d6b1be..013a3e685 100644 --- a/iceoryx2-ffi/cxx/include/iox2/sample.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/sample.hpp @@ -13,59 +13,135 @@ #ifndef IOX2_SAMPLE_HPP #define IOX2_SAMPLE_HPP -#include "header_publish_subscribe.hpp" #include "iox/assertions_addendum.hpp" -#include "service_type.hpp" -#include "unique_port_id.hpp" +#include "iox2/header_publish_subscribe.hpp" +#include "iox2/internal/iceoryx2.hpp" +#include "iox2/service_type.hpp" +#include "iox2/unique_port_id.hpp" namespace iox2 { - +/// It stores the payload and is acquired by the [`Subscriber`] whenever +/// it receives new data from a [`Publisher`] via +/// [`Subscriber::receive()`]. +/// # Notes +/// +/// Does not implement [`Send`] since it releases unsent samples vie the [`Subscriber`] and the +/// [`Subscriber`] is not thread-safe! +/// +/// # Important +/// +/// DO NOT MOVE THE SAMPLE INTO ANOTHER THREAD! template class Sample { public: - Sample() = default; - Sample(Sample&&) = default; - auto operator=(Sample&&) -> Sample& = default; - ~Sample() = default; + Sample(Sample&& rhs) noexcept; + auto operator=(Sample&& rhs) noexcept -> Sample&; + ~Sample(); Sample(const Sample&) = delete; auto operator=(const Sample&) -> Sample& = delete; - auto payload() const -> const Payload& { - IOX_TODO(); - } - auto user_header() const -> const UserHeader& { - IOX_TODO(); - } - auto header() const -> const HeaderPublishSubscribe& { - IOX_TODO(); - } - auto origin() const -> UniquePublisherId { - IOX_TODO(); - } -}; + /// Returns a reference to the payload of the [`Sample`] + auto operator*() const -> const Payload&; -template -class Sample { - public: - Sample() = default; - Sample(Sample&&) = default; - auto operator=(Sample&&) -> Sample& = default; - ~Sample() = default; + /// Returns a pointer to the payload of the [`Sample`] + auto operator->() const -> const Payload*; - Sample(const Sample&) = delete; - auto operator=(const Sample&) -> Sample& = delete; + /// Returns a reference to the payload of the [`Sample`] + auto payload() const -> const Payload&; - auto payload() const -> const Payload& { - IOX_TODO(); - } - auto header() const -> const HeaderPublishSubscribe& { - IOX_TODO(); + /// Returns a reference to the user_header of the [`Sample`] + template , T>> + auto user_header() const -> const T&; + + /// Returns a reference to the [`Header`] of the [`Sample`]. + auto header() const -> const HeaderPublishSubscribe&; + + /// Returns the [`UniquePublisherId`] of the [`Publisher`](crate::port::publisher::Publisher) + auto origin() const -> UniquePublisherId; + + private: + template + friend class Subscriber; + + explicit Sample(iox2_sample_h handle); + void drop(); + + iox2_sample_h m_handle { nullptr }; +}; + +template +inline Sample::Sample(iox2_sample_h handle) + : m_handle { handle } { +} + +template +inline void Sample::drop() { + if (m_handle != nullptr) { + iox2_sample_drop(m_handle); + m_handle = nullptr; } - auto origin() const -> UniquePublisherId { - IOX_TODO(); +} + +template +inline Sample::Sample(Sample&& rhs) noexcept { + *this = std::move(rhs); +} + +template +inline auto Sample::operator=(Sample&& rhs) noexcept -> Sample& { + if (this != &rhs) { + drop(); + m_handle = std::move(rhs.m_handle); + rhs.m_handle = nullptr; } -}; + + return *this; +} + +template +inline Sample::~Sample() { + drop(); +} + +template +inline auto Sample::operator*() const -> const Payload& { + return payload(); +} + +template +inline auto Sample::operator->() const -> const Payload* { + return &payload(); +} + +template +inline auto Sample::payload() const -> const Payload& { + auto* ref_handle = iox2_cast_sample_ref_h(m_handle); + const void* payload_ptr = nullptr; + size_t payload_len = 0; + + iox2_sample_payload(ref_handle, &payload_ptr, &payload_len); + IOX_ASSERT(sizeof(Payload) <= payload_len, ""); + + return *static_cast(payload_ptr); +} + +template +template +inline auto Sample::user_header() const -> const T& { + IOX_TODO(); +} + +template +inline auto Sample::header() const -> const HeaderPublishSubscribe& { + IOX_TODO(); +} + +template +inline auto Sample::origin() const -> UniquePublisherId { + IOX_TODO(); +} + } // namespace iox2 diff --git a/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp b/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp index 573471779..3aeb29327 100644 --- a/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/sample_mut.hpp @@ -13,120 +13,220 @@ #ifndef IOX2_SAMPLE_MUT_HPP #define IOX2_SAMPLE_MUT_HPP -#include "header_publish_subscribe.hpp" +#include "iox/assertions.hpp" #include "iox/assertions_addendum.hpp" #include "iox/expected.hpp" #include "iox/function.hpp" #include "iox/slice.hpp" -#include "service_type.hpp" +#include "iox2/header_publish_subscribe.hpp" +#include "iox2/iceoryx2.h" +#include "iox2/internal/iceoryx2.hpp" +#include "iox2/publisher_error.hpp" +#include "iox2/service_type.hpp" #include namespace iox2 { -/// Failure that can be emitted when a [`SampleMut`] is sent via -/// [`SampleMut::send()`]. -enum PublisherSendError : uint8_t { - /// [`SampleMut::send()`] was called but the corresponding [`Publisher`] - /// went already out of - /// scope. - ConnectionBrokenSincePublisherNoLongerExists, - /// A connection between a - /// [`Subscriber`](crate::port::subscriber::Subscriber) and a - /// [`Publisher`] is corrupted. - ConnectionCorrupted, - /// A failure occurred while acquiring memory for the payload - LoanError, - /// A failure occurred while establishing a connection to a - /// [`Subscriber`](crate::port::subscriber::Subscriber) - ConnectionError, -}; +/// Acquired by a [`Publisher`] via +/// * [`Publisher::loan()`], +/// * [`Publisher::loan_uninit()`] +/// * [`Publisher::loan_slice()`] +/// * [`Publisher::loan_slice_uninit()`] +/// +/// It stores the payload that will be sent +/// to all connected [`Subscriber`]s. If the [`SampleMut`] is not sent +/// it will release the loaned memory when going out of scope. +/// +/// # Notes +/// +/// Does not implement [`Send`] since it releases unsent samples in the [`Publisher`] and the +/// [`Publisher`] is not thread-safe! +/// +/// # Important +/// +/// DO NOT MOVE THE SAMPLE INTO ANOTHER THREAD! template class SampleMut { public: - SampleMut() = default; - SampleMut(SampleMut&&) = default; - auto operator=(SampleMut&&) -> SampleMut& = default; - ~SampleMut() = default; + SampleMut(SampleMut&& rhs) noexcept; + auto operator=(SampleMut&& rhs) noexcept -> SampleMut&; + ~SampleMut() noexcept; SampleMut(const SampleMut&) = delete; auto operator=(const SampleMut&) -> SampleMut& = delete; - auto header() const -> const HeaderPublishSubscribe& { - IOX_TODO(); - } - auto user_header() const -> const UserHeader& { - IOX_TODO(); - } - auto user_header_mut() -> UserHeader& { - IOX_TODO(); - } - auto payload() const -> const Payload& { - IOX_TODO(); - } - auto payload_mut() -> Payload& { - IOX_TODO(); - } - void write_payload(const Payload& payload) { - IOX_TODO(); - } -}; + /// Returns a const reference to the payload of the [`Sample`] + auto operator*() const -> const Payload&; -template -class SampleMut { - public: - SampleMut() = default; - SampleMut(SampleMut&&) = default; - auto operator=(SampleMut&&) -> SampleMut& = default; - ~SampleMut() = default; + /// Returns a reference to the payload of the [`Sample`] + auto operator*() -> Payload&; - SampleMut(const SampleMut&) = delete; - auto operator=(const SampleMut&) -> SampleMut& = delete; + /// Returns a const pointer to the payload of the [`Sample`] + auto operator->() const -> const Payload*; - auto header() const -> const HeaderPublishSubscribe& { - IOX_TODO(); - } - auto payload() const -> const Payload& { - IOX_TODO(); - } - auto payload_mut() -> Payload& { - IOX_TODO(); - } - void write_payload(const Payload& payload) { - IOX_TODO(); - } -}; + /// Returns a pointer to the payload of the [`Sample`] + auto operator->() -> Payload*; -template -class SampleMut, void> { - public: - SampleMut() = default; - SampleMut(SampleMut&&) = default; - auto operator=(SampleMut&&) -> SampleMut& = default; - ~SampleMut() = default; + /// Returns a reference to the [`Header`] of the [`Sample`]. + auto header() const -> const HeaderPublishSubscribe&; - SampleMut(const SampleMut&) = delete; - auto operator=(const SampleMut&) -> SampleMut& = delete; + /// Returns a reference to the user_header of the [`Sample`] + template , T>> + auto user_header() const -> const T&; - auto header() const -> const HeaderPublishSubscribe& { - IOX_TODO(); - } - auto payload() const -> const Payload& { - IOX_TODO(); - } - auto payload_mut() -> Payload& { - IOX_TODO(); + /// Returns a mutable reference to the user_header of the [`Sample`]. + template , T>> + auto user_header_mut() -> T&; + + /// Returns a reference to the const payload of the sample. + auto payload() const -> const Payload&; + + /// Returns a reference to the payload of the sample. + auto payload_mut() -> Payload&; + + /// Writes the payload to the sample + template ::VALUE, T>> + void write_payload(T&& value); + + /// Writes the payload to the sample + template ::VALUE, T>> + void write_from_fn(const iox::function& initializer); + + private: + template + friend class Publisher; + + template + friend auto send_sample(SampleMut&& sample) -> iox::expected; + + explicit SampleMut(iox2_sample_mut_h handle); + void drop(); + + iox2_sample_mut_h m_handle { nullptr }; +}; + +template +inline SampleMut::SampleMut(iox2_sample_mut_h handle) + : m_handle { handle } { +} + +template +inline void SampleMut::drop() { + if (m_handle != nullptr) { + iox2_sample_mut_drop(m_handle); + m_handle = nullptr; } - void write_from_fn(const iox::function& initializer) { - IOX_TODO(); +} + +template +inline SampleMut::SampleMut(SampleMut&& rhs) noexcept { + *this = std::move(rhs); +} + +template +inline auto SampleMut::operator=(SampleMut&& rhs) noexcept -> SampleMut& { + if (this != &rhs) { + drop(); + m_handle = std::move(rhs.m_handle); + rhs.m_handle = nullptr; } -}; + + return *this; +} + +template +inline SampleMut::~SampleMut() noexcept { + drop(); +} + +template +inline auto SampleMut::operator*() const -> const Payload& { + return payload(); +} + +template +inline auto SampleMut::operator*() -> Payload& { + return payload_mut(); +} + +template +inline auto SampleMut::operator->() const -> const Payload* { + return &payload(); +} template -auto send_sample(SampleMut&& sample) -> iox::expected { +inline auto SampleMut::operator->() -> Payload* { + return &payload_mut(); +} + +template +inline auto SampleMut::header() const -> const HeaderPublishSubscribe& { + IOX_TODO(); +} + +template +template +inline auto SampleMut::user_header() const -> const T& { IOX_TODO(); } +template +template +inline auto SampleMut::user_header_mut() -> T& { + IOX_TODO(); +} + +template +inline auto SampleMut::payload() const -> const Payload& { + auto* ref_handle = iox2_cast_sample_mut_ref_h(m_handle); + const void* ptr = nullptr; + size_t payload_len = 0; + + iox2_sample_mut_payload(ref_handle, &ptr, &payload_len); + IOX_ASSERT(sizeof(Payload) <= payload_len, ""); + + return *static_cast(ptr); +} + +template +inline auto SampleMut::payload_mut() -> Payload& { + auto* ref_handle = iox2_cast_sample_mut_ref_h(m_handle); + void* ptr = nullptr; + size_t payload_len = 0; + + iox2_sample_mut_payload_mut(ref_handle, &ptr, &payload_len); + IOX_ASSERT(sizeof(Payload) <= payload_len, ""); + + return *static_cast(ptr); +} + +template +template +inline void SampleMut::write_payload(T&& value) { + new (&payload_mut()) Payload(std::forward(value)); +} + +template +template +inline void +SampleMut::write_from_fn(const iox::function& initializer) { + IOX_TODO(); +} + +template +inline auto send_sample(SampleMut&& sample) -> iox::expected { + size_t number_of_recipients = 0; + auto result = iox2_sample_mut_send(sample.m_handle, &number_of_recipients); + sample.m_handle = nullptr; + + if (result == IOX2_OK) { + return iox::ok(number_of_recipients); + } + + return iox::err(iox::into(result)); +} + } // namespace iox2 #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/static_config_publish_subscribe.hpp b/iceoryx2-ffi/cxx/include/iox2/static_config_publish_subscribe.hpp index 078e4461d..888336f14 100644 --- a/iceoryx2-ffi/cxx/include/iox2/static_config_publish_subscribe.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/static_config_publish_subscribe.hpp @@ -13,38 +13,51 @@ #ifndef IOX2_STATIC_CONFIG_PUBLISH_SUBSCRIBE_HPP #define IOX2_STATIC_CONFIG_PUBLISH_SUBSCRIBE_HPP -#include "iox/assertions_addendum.hpp" -#include "message_type_details.hpp" +#include "iox2/internal/iceoryx2.hpp" +#include "iox2/message_type_details.hpp" #include namespace iox2 { +/// The static configuration of an [`MessagingPattern::PublishSubscribe`] +/// based service. Contains all parameters that do not change during the lifetime of a +/// [`Service`]. class StaticConfigPublishSubscribe { public: - auto max_nodes() const -> uint64_t { - IOX_TODO(); - } - auto max_publishers() const -> uint64_t { - IOX_TODO(); - } - auto max_subscribers() const -> uint64_t { - IOX_TODO(); - } - auto history_size() const -> uint64_t { - IOX_TODO(); - } - auto subscriber_max_buffer_size() const -> uint64_t { - IOX_TODO(); - } - auto subscriber_max_borrowed_samples() const -> uint64_t { - IOX_TODO(); - } - auto has_safe_overflow() const -> bool { - IOX_TODO(); - } - auto message_type_details() const -> const MessageTypeDetails& { - IOX_TODO(); - } + /// Returns the maximum supported amount of [`Node`]s that can open the + /// [`Service`] in parallel. + auto max_nodes() const -> uint64_t; + + /// Returns the maximum supported amount of [`Publisher`] ports + auto max_publishers() const -> uint64_t; + + /// Returns the maximum supported amount of [`Subscriber`] ports + auto max_subscribers() const -> uint64_t; + + /// Returns the maximum history size that can be requested on connect. + auto history_size() const -> uint64_t; + + /// Returns the maximum supported buffer size for [`Subscriber`] port + auto subscriber_max_buffer_size() const -> uint64_t; + + /// Returns how many [`Sample`]s a [`Subscriber`] port can borrow in parallel at most. + auto subscriber_max_borrowed_samples() const -> uint64_t; + + /// Returns true if the [`Service`] safely overflows, otherwise false. Safe + /// overflow means that the [`Publisher`] will recycle the oldest + /// [`Sample`] from the [`Subscriber`] when its buffer is full. + auto has_safe_overflow() const -> bool; + + /// Returns the type details of the [`Service`]. + auto message_type_details() const -> MessageTypeDetails; + + private: + template + friend class PortFactoryPublishSubscribe; + + explicit StaticConfigPublishSubscribe(iox2_static_config_publish_subscribe_t value); + + iox2_static_config_publish_subscribe_t m_value; }; } // namespace iox2 diff --git a/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp b/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp index 26d9a3615..19aac5416 100644 --- a/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp +++ b/iceoryx2-ffi/cxx/include/iox2/subscriber.hpp @@ -13,56 +13,122 @@ #ifndef IOX2_SUBSCRIBER_HPP #define IOX2_SUBSCRIBER_HPP -#include "connection_failure.hpp" #include "iox/assertions_addendum.hpp" #include "iox/expected.hpp" #include "iox/optional.hpp" -#include "sample.hpp" -#include "service_type.hpp" -#include "unique_port_id.hpp" - -#include +#include "iox2/connection_failure.hpp" +#include "iox2/internal/iceoryx2.hpp" +#include "iox2/sample.hpp" +#include "iox2/service_type.hpp" +#include "iox2/subscriber_error.hpp" +#include "iox2/unique_port_id.hpp" namespace iox2 { -enum class SubscriberReceiveError : uint8_t { -}; - -enum class SubscriberCreateError : uint8_t { - /// The maximum amount of [`Subscriber`]s that can connect to a - /// [`Service`](crate::service::Service) is - /// defined in [`crate::config::Config`]. When this is exceeded no more - /// [`Subscriber`]s - /// can be created for a specific [`Service`](crate::service::Service). - ExceedsMaxSupportedSubscribers, - /// When the [`Subscriber`] requires a larger buffer size than the - /// [`Service`](crate::service::Service) offers the creation will fail. - BufferSizeExceedsMaxSupportedBufferSizeOfService, -}; - +/// The receiving endpoint of a publish-subscribe communication. template class Subscriber { public: - Subscriber() = default; - Subscriber(Subscriber&&) = default; - auto operator=(Subscriber&&) -> Subscriber& = default; - ~Subscriber() = default; + Subscriber(Subscriber&& rhs) noexcept; + auto operator=(Subscriber&& rhs) noexcept -> Subscriber&; + ~Subscriber(); Subscriber(const Subscriber&) = delete; auto operator=(const Subscriber&) -> Subscriber& = delete; - auto id() const -> UniqueSubscriberId { - IOX_TODO(); - } - auto buffer_size() const -> uint64_t { - IOX_TODO(); + /// Returns the [`UniqueSubscriberId`] of the [`Subscriber`] + auto id() const -> UniqueSubscriberId; + + /// Returns the internal buffer size of the [`Subscriber`]. + auto buffer_size() const -> uint64_t; + + /// Receives a [`Sample`] from [`Publisher`]. If no sample could be + /// received [`None`] is returned. If a failure occurs [`SubscriberReceiveError`] is returned. + auto receive() const -> iox::expected>, SubscriberReceiveError>; + + /// Explicitly updates all connections to the [`Subscriber`]s. This is + /// required to be called whenever a new [`Subscriber`] connected to + /// the service. It is done implicitly whenever [`SampleMut::send()`] or + /// [`Publisher::send_copy()`] is called. + /// When a [`Subscriber`] is connected that requires a history this + /// call will deliver it. + auto update_connections() const -> iox::expected; + + private: + template + friend class PortFactorySubscriber; + + explicit Subscriber(iox2_subscriber_h handle); + void drop(); + + iox2_subscriber_h m_handle { nullptr }; +}; +template +inline Subscriber::Subscriber(iox2_subscriber_h handle) + : m_handle { handle } { +} + +template +inline Subscriber::Subscriber(Subscriber&& rhs) noexcept { + *this = std::move(rhs); +} + +template +inline auto Subscriber::operator=(Subscriber&& rhs) noexcept -> Subscriber& { + if (this != &rhs) { + drop(); + m_handle = std::move(rhs.m_handle); + rhs.m_handle = nullptr; } - auto receive() const -> iox::expected>, SubscriberReceiveError> { - IOX_TODO(); + + return *this; +} + +template +inline Subscriber::~Subscriber() { + drop(); +} + +template +inline void Subscriber::drop() { + if (m_handle != nullptr) { + iox2_subscriber_drop(m_handle); + m_handle = nullptr; } - auto update_connections() const -> iox::expected { - IOX_TODO(); +} + +template +inline auto Subscriber::id() const -> UniqueSubscriberId { + IOX_TODO(); +} + +template +inline auto Subscriber::buffer_size() const -> uint64_t { + IOX_TODO(); +} + +template +inline auto Subscriber::receive() const + -> iox::expected>, SubscriberReceiveError> { + auto* ref_handle = iox2_cast_subscriber_ref_h(m_handle); + iox2_sample_h sample_handle {}; + auto result = iox2_subscriber_receive(ref_handle, nullptr, &sample_handle); + + if (result == IOX2_OK) { + if (sample_handle != nullptr) { + return iox::ok( + iox::optional>(Sample(sample_handle))); + } + return iox::ok(iox::optional>(iox::nullopt)); } -}; + + return iox::err(iox::into(result)); +} + +template +inline auto Subscriber::update_connections() const -> iox::expected { + IOX_TODO(); +} + } // namespace iox2 #endif diff --git a/iceoryx2-ffi/cxx/include/iox2/subscriber_error.hpp b/iceoryx2-ffi/cxx/include/iox2/subscriber_error.hpp new file mode 100644 index 000000000..b4b4f8ce8 --- /dev/null +++ b/iceoryx2-ffi/cxx/include/iox2/subscriber_error.hpp @@ -0,0 +1,46 @@ +// 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 + +#ifndef IOX2_SUBSCRIBER_ERROR_HPP +#define IOX2_SUBSCRIBER_ERROR_HPP + +#include + +namespace iox2 { +/// Defines the failure that can occur when receiving data with [`Subscriber::receive()`]. +enum class SubscriberReceiveError : uint8_t { + /// The maximum amount of [`Sample`]s a user can borrow with [`Subscriber::receive()`] is + /// defined in [`Config`]. When this is exceeded [`Subscriber::receive()`] + /// fails. + ExceedsMaxBorrowedSamples, + + /// Occurs when a [`Subscriber`] is unable to connect to a corresponding + /// [`Publisher`]. + ConnectionFailure, +}; + +/// Describes the failures when a new [`Subscriber`] is created via the +/// [`PortFactorySubscriber`]. +enum class SubscriberCreateError : uint8_t { + /// The maximum amount of [`Subscriber`]s that can connect to a + /// [`Service`] is defined in [`Config`]. When this is exceeded no more + /// [`Subscriber`]s + /// can be created for a specific [`Service`]. + ExceedsMaxSupportedSubscribers, + + /// When the [`Subscriber`] requires a larger buffer size than the + /// [`Service`] offers the creation will fail. + BufferSizeExceedsMaxSupportedBufferSizeOfService, +}; +} // namespace iox2 + +#endif diff --git a/iceoryx2-ffi/cxx/include/iox2/type_variant.hpp b/iceoryx2-ffi/cxx/include/iox2/type_variant.hpp new file mode 100644 index 000000000..331a2ea5e --- /dev/null +++ b/iceoryx2-ffi/cxx/include/iox2/type_variant.hpp @@ -0,0 +1,30 @@ +// 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 + +#ifndef IOX2_TYPE_VARIANT_HPP +#define IOX2_TYPE_VARIANT_HPP + +#include + +namespace iox2 { +/// Defines if the type is a slice with a runtime-size +/// ([`TypeVariant::Dynamic`]) or if its a type that satisfies [`Sized`] +/// ([`TypeVariant::FixedSize`]). +enum class TypeVariant : uint8_t { + /// A fixed size type like [`u64`] + FixedSize, + /// A dynamic sized type like a slice + Dynamic, +}; +} // namespace iox2 + +#endif diff --git a/iceoryx2-ffi/cxx/src/message_type_details.cpp b/iceoryx2-ffi/cxx/src/message_type_details.cpp new file mode 100644 index 000000000..933efa08a --- /dev/null +++ b/iceoryx2-ffi/cxx/src/message_type_details.cpp @@ -0,0 +1,52 @@ +// 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/message_type_details.hpp" +#include "iox/into.hpp" + +namespace iox2 { +TypeDetail::TypeDetail(iox2_type_detail_t value) + : m_value { value } { +} + +auto TypeDetail::variant() const -> TypeVariant { + return iox::into(static_cast(m_value.variant)); +} + +auto TypeDetail::type_name() const -> const char* { + return &m_value.type_name[0]; +} + +auto TypeDetail::size() const -> size_t { + return m_value.size; +} + +auto TypeDetail::alignment() const -> size_t { + return m_value.alignment; +} + +MessageTypeDetails::MessageTypeDetails(iox2_message_type_details_t value) + : m_value { value } { +} + +auto MessageTypeDetails::header() const -> TypeDetail { + return TypeDetail(m_value.header); +} + +auto MessageTypeDetails::user_header() const -> TypeDetail { + return TypeDetail(m_value.user_header); +} + +auto MessageTypeDetails::payload() const -> TypeDetail { + return TypeDetail(m_value.payload); +} +} // namespace iox2 diff --git a/iceoryx2-ffi/cxx/src/static_config_publish_subscribe.cpp b/iceoryx2-ffi/cxx/src/static_config_publish_subscribe.cpp new file mode 100644 index 000000000..a4dd9ff0e --- /dev/null +++ b/iceoryx2-ffi/cxx/src/static_config_publish_subscribe.cpp @@ -0,0 +1,53 @@ +// 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/static_config_publish_subscribe.hpp" + +namespace iox2 { +StaticConfigPublishSubscribe::StaticConfigPublishSubscribe(iox2_static_config_publish_subscribe_t value) + : m_value { value } { +} + +auto StaticConfigPublishSubscribe::max_nodes() const -> uint64_t { + return m_value.max_nodes; +} + +auto StaticConfigPublishSubscribe::max_publishers() const -> uint64_t { + return m_value.max_publishers; +} + +auto StaticConfigPublishSubscribe::max_subscribers() const -> uint64_t { + return m_value.max_subscribers; +} + +auto StaticConfigPublishSubscribe::history_size() const -> uint64_t { + return m_value.history_size; +} + +auto StaticConfigPublishSubscribe::subscriber_max_buffer_size() const -> uint64_t { + return m_value.subscriber_max_buffer_size; +} + +auto StaticConfigPublishSubscribe::subscriber_max_borrowed_samples() const -> uint64_t { + return m_value.subscriber_max_borrowed_samples; +} + +auto StaticConfigPublishSubscribe::has_safe_overflow() const -> bool { + return m_value.enable_safe_overflow; +} + +auto StaticConfigPublishSubscribe::message_type_details() const -> MessageTypeDetails { + return MessageTypeDetails(m_value.message_type_details); +} + + +} // namespace iox2 diff --git a/iceoryx2-ffi/cxx/tests/src/service_event_tests.cpp b/iceoryx2-ffi/cxx/tests/src/service_event_tests.cpp index 783412385..757a313f2 100644 --- a/iceoryx2-ffi/cxx/tests/src/service_event_tests.cpp +++ b/iceoryx2-ffi/cxx/tests/src/service_event_tests.cpp @@ -36,7 +36,8 @@ struct ServiceEventTest : public ::testing::Test { static std::atomic event_id_counter; static constexpr ServiceType TYPE = T::TYPE; - const char* service_name_value; + //NOLINTBEGIN(misc-non-private-member-variables-in-classes), required for tests + const char* service_name_value { nullptr }; ServiceName service_name; Node node; PortFactoryEvent service; @@ -44,6 +45,7 @@ struct ServiceEventTest : public ::testing::Test { Listener listener; EventId event_id_1; EventId event_id_2; + //NOLINTEND(misc-non-private-member-variables-in-classes) }; template @@ -89,28 +91,36 @@ TYPED_TEST(ServiceEventTest, creating_existing_service_fails) { TYPED_TEST(ServiceEventTest, service_settings_are_applied) { constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + constexpr uint64_t NUMBER_OF_NOTIFIERS = 5; + constexpr uint64_t NUMBER_OF_LISTENERS = 7; const auto* name_value = "First time we met, I saw the ocean, it was wet!"; const auto service_name = ServiceName::create(name_value).expect(""); auto node = NodeBuilder().create().expect(""); - auto sut = node.service_builder(service_name).event().max_notifiers(5).max_listeners(7).create().expect(""); + auto sut = node.service_builder(service_name) + .event() + .max_notifiers(NUMBER_OF_NOTIFIERS) + .max_listeners(NUMBER_OF_LISTENERS) + .create() + .expect(""); auto static_config = sut.static_config(); - ASSERT_THAT(static_config.max_notifiers(), Eq(5)); - ASSERT_THAT(static_config.max_listeners(), Eq(7)); + ASSERT_THAT(static_config.max_notifiers(), Eq(NUMBER_OF_NOTIFIERS)); + ASSERT_THAT(static_config.max_listeners(), Eq(NUMBER_OF_LISTENERS)); } TYPED_TEST(ServiceEventTest, open_fails_with_incompatible_max_notifiers_requirements) { constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + constexpr uint64_t NUMBER_OF_NOTIFIERS = 5; const auto* name_value = "First time we met, I saw the ocean, it was wet!"; const auto service_name = ServiceName::create(name_value).expect(""); auto node = NodeBuilder().create().expect(""); - auto sut = node.service_builder(service_name).event().max_notifiers(5).create().expect(""); - auto sut_fail = node.service_builder(service_name).event().max_notifiers(6).open(); + auto sut = node.service_builder(service_name).event().max_notifiers(NUMBER_OF_NOTIFIERS).create().expect(""); + auto sut_fail = node.service_builder(service_name).event().max_notifiers(NUMBER_OF_NOTIFIERS + 1).open(); ASSERT_TRUE(sut_fail.has_error()); ASSERT_THAT(sut_fail.error(), Eq(EventOpenError::DoesNotSupportRequestedAmountOfNotifiers)); @@ -118,13 +128,14 @@ TYPED_TEST(ServiceEventTest, open_fails_with_incompatible_max_notifiers_requirem TYPED_TEST(ServiceEventTest, open_fails_with_incompatible_max_listeners_requirements) { constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + constexpr uint64_t NUMBER_OF_LISTENERS = 7; const auto* name_value = "First time we met, I saw the ocean, it was wet!"; const auto service_name = ServiceName::create(name_value).expect(""); auto node = NodeBuilder().create().expect(""); - auto sut = node.service_builder(service_name).event().max_listeners(5).create().expect(""); - auto sut_fail = node.service_builder(service_name).event().max_listeners(6).open(); + auto sut = node.service_builder(service_name).event().max_listeners(NUMBER_OF_LISTENERS).create().expect(""); + auto sut_fail = node.service_builder(service_name).event().max_listeners(NUMBER_OF_LISTENERS + 1).open(); ASSERT_TRUE(sut_fail.has_error()); ASSERT_THAT(sut_fail.error(), Eq(EventOpenError::DoesNotSupportRequestedAmountOfListeners)); diff --git a/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp b/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp index 3abd36779..4f55e5abc 100644 --- a/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp +++ b/iceoryx2-ffi/cxx/tests/src/service_publish_subscribe_tests.cpp @@ -159,4 +159,139 @@ TYPED_TEST(ServicePublishSubscribeTest, open_or_create_existing_service_with_wro ASSERT_TRUE(sut.has_error()); ASSERT_THAT(sut.error(), Eq(PublishSubscribeOpenOrCreateError::OpenIncompatibleTypes)); } + +TYPED_TEST(ServicePublishSubscribeTest, send_copy_receive_works) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + + const auto* name_value = "I am floating through the galaxy of my brain. Oh the colors!"; + const auto service_name = ServiceName::create(name_value).expect(""); + + auto node = NodeBuilder().create().expect(""); + auto service = node.service_builder(service_name).template publish_subscribe().create().expect(""); + + auto sut_publisher = service.publisher_builder().create().expect(""); + auto sut_subscriber = service.subscriber_builder().create().expect(""); + + const uint64_t payload = 123; + sut_publisher.send_copy(payload).expect(""); + auto sample = sut_subscriber.receive().expect(""); + + ASSERT_TRUE(sample.has_value()); + ASSERT_THAT(**sample, Eq(payload)); +} + +TYPED_TEST(ServicePublishSubscribeTest, loan_uninit_send_receive_works) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + + const auto* name_value = "I am floating through the galaxy of my brain. Oh the colors!"; + const auto service_name = ServiceName::create(name_value).expect(""); + + auto node = NodeBuilder().create().expect(""); + auto service = node.service_builder(service_name).template publish_subscribe().create().expect(""); + + auto sut_publisher = service.publisher_builder().create().expect(""); + auto sut_subscriber = service.subscriber_builder().create().expect(""); + + auto sample = sut_publisher.loan_uninit().expect(""); + const uint64_t payload = 78123791; + sample.write_payload(payload); + send_sample(std::move(sample)).expect(""); + auto recv_sample = sut_subscriber.receive().expect(""); + + ASSERT_TRUE(recv_sample.has_value()); + ASSERT_THAT(**recv_sample, Eq(payload)); +} + +TYPED_TEST(ServicePublishSubscribeTest, loan_send_receive_works) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + + const auto* name_value = "I am floating through the galaxy of my brain. Oh the colors!"; + const auto service_name = ServiceName::create(name_value).expect(""); + + auto node = NodeBuilder().create().expect(""); + auto service = node.service_builder(service_name).template publish_subscribe().create().expect(""); + + auto sut_publisher = service.publisher_builder().create().expect(""); + auto sut_subscriber = service.subscriber_builder().create().expect(""); + + auto sample = sut_publisher.loan().expect(""); + const uint64_t payload = 781891729871; + *sample = payload; + send_sample(std::move(sample)).expect(""); + auto recv_sample = sut_subscriber.receive().expect(""); + + ASSERT_TRUE(recv_sample.has_value()); + ASSERT_THAT(**recv_sample, Eq(payload)); +} + +TYPED_TEST(ServicePublishSubscribeTest, setting_service_properties_works) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + constexpr uint64_t NUMBER_OF_PUBLISHERS = 11; + constexpr uint64_t NUMBER_OF_SUBSCRIBERS = 12; + + const auto* name_value = "I am floating through the galaxy of my brain. Oh the colors!"; + const auto service_name = ServiceName::create(name_value).expect(""); + + auto node = NodeBuilder().create().expect(""); + auto service = node.service_builder(service_name) + .template publish_subscribe() + .max_publishers(NUMBER_OF_PUBLISHERS) + .max_subscribers(NUMBER_OF_SUBSCRIBERS) + .create() + .expect(""); + + auto static_config = service.static_config(); + + ASSERT_THAT(static_config.max_publishers(), Eq(NUMBER_OF_PUBLISHERS)); + ASSERT_THAT(static_config.max_subscribers(), Eq(NUMBER_OF_SUBSCRIBERS)); + ASSERT_THAT(static_config.message_type_details().payload().size(), Eq(sizeof(uint64_t))); + ASSERT_THAT(static_config.message_type_details().payload().alignment(), Eq(alignof(uint64_t))); + ASSERT_THAT(static_config.message_type_details().payload().type_name(), StrEq(typeid(uint64_t).name())); +} + +TYPED_TEST(ServicePublishSubscribeTest, open_fails_with_incompatible_publisher_requirement) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + constexpr uint64_t NUMBER_OF_PUBLISHERS = 11; + + const auto* name_value = "I am floating through the galaxy of my brain. Oh the colors!"; + const auto service_name = ServiceName::create(name_value).expect(""); + + auto node = NodeBuilder().create().expect(""); + auto service = node.service_builder(service_name) + .template publish_subscribe() + .max_publishers(NUMBER_OF_PUBLISHERS) + .create() + .expect(""); + + auto service_fail = node.service_builder(service_name) + .template publish_subscribe() + .max_publishers(NUMBER_OF_PUBLISHERS + 1) + .open(); + + ASSERT_TRUE(service_fail.has_error()); + ASSERT_THAT(service_fail.error(), Eq(PublishSubscribeOpenError::DoesNotSupportRequestedAmountOfPublishers)); +} + +TYPED_TEST(ServicePublishSubscribeTest, open_fails_with_incompatible_subscriber_requirement) { + constexpr ServiceType SERVICE_TYPE = TestFixture::TYPE; + constexpr uint64_t NUMBER_OF_SUBSCRIBERS = 12; + + const auto* name_value = "I am floating through the galaxy of my brain. Oh the colors!"; + const auto service_name = ServiceName::create(name_value).expect(""); + + auto node = NodeBuilder().create().expect(""); + auto service = node.service_builder(service_name) + .template publish_subscribe() + .max_subscribers(NUMBER_OF_SUBSCRIBERS) + .create() + .expect(""); + + auto service_fail = node.service_builder(service_name) + .template publish_subscribe() + .max_subscribers(NUMBER_OF_SUBSCRIBERS + 1) + .open(); + + ASSERT_TRUE(service_fail.has_error()); + ASSERT_THAT(service_fail.error(), Eq(PublishSubscribeOpenError::DoesNotSupportRequestedAmountOfSubscribers)); +} } // namespace diff --git a/iceoryx2-ffi/ffi/src/api/message_type_details.rs b/iceoryx2-ffi/ffi/src/api/message_type_details.rs new file mode 100644 index 000000000..37f14d8fc --- /dev/null +++ b/iceoryx2-ffi/ffi/src/api/message_type_details.rs @@ -0,0 +1,61 @@ +// 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 + +use core::ffi::c_char; + +use iceoryx2::service::static_config::message_type_details::*; + +use crate::iox2_type_variant_e; + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct iox2_type_detail_t { + pub variant: iox2_type_variant_e, + pub type_name: [c_char; 256], + pub size: usize, + pub alignment: usize, +} + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct iox2_message_type_details_t { + pub header: iox2_type_detail_t, + pub user_header: iox2_type_detail_t, + pub payload: iox2_type_detail_t, +} + +impl From<&TypeDetail> for iox2_type_detail_t { + fn from(value: &TypeDetail) -> Self { + Self { + variant: (&value.variant).into(), + type_name: core::array::from_fn(|n| { + if n < value.type_name.as_bytes().len() { + value.type_name.as_bytes()[n] as _ + } else { + 0 + } + }), + size: value.size, + alignment: value.alignment, + } + } +} + +impl From<&MessageTypeDetails> for iox2_message_type_details_t { + fn from(m: &MessageTypeDetails) -> Self { + Self { + header: (&m.header).into(), + user_header: (&m.user_header).into(), + payload: (&m.payload).into(), + } + } +} diff --git a/iceoryx2-ffi/ffi/src/api/mod.rs b/iceoryx2-ffi/ffi/src/api/mod.rs index b458338ca..27efa9992 100644 --- a/iceoryx2-ffi/ffi/src/api/mod.rs +++ b/iceoryx2-ffi/ffi/src/api/mod.rs @@ -21,6 +21,7 @@ use core::ffi::{c_int, c_void}; mod config; mod event_id; mod listener; +mod message_type_details; mod node; mod node_builder; mod node_name; @@ -29,19 +30,25 @@ mod port_factory_event; mod port_factory_listener_builder; mod port_factory_notifier_builder; mod port_factory_pub_sub; +mod port_factory_publisher_builder; +mod port_factory_subscriber_builder; mod publisher; mod quirks_correction; +mod sample; +mod sample_mut; mod service; mod service_builder; mod service_builder_event; mod service_builder_pub_sub; mod service_name; mod static_config_event; +mod static_config_publish_subscribe; mod subscriber; pub use config::*; pub use event_id::*; pub use listener::*; +pub use message_type_details::*; pub use node::*; pub use node_builder::*; pub use node_name::*; @@ -50,14 +57,19 @@ pub use port_factory_event::*; pub use port_factory_listener_builder::*; pub use port_factory_notifier_builder::*; pub use port_factory_pub_sub::*; +pub use port_factory_publisher_builder::*; +pub use port_factory_subscriber_builder::*; pub use publisher::*; pub use quirks_correction::*; +pub use sample::*; +pub use sample_mut::*; pub use service::*; pub use service_builder::*; pub use service_builder_event::*; pub use service_builder_pub_sub::*; pub use service_name::*; pub use static_config_event::*; +pub use static_config_publish_subscribe::*; pub use subscriber::*; /// This constant signals an successful function call diff --git a/iceoryx2-ffi/ffi/src/api/port_factory_listener_builder.rs b/iceoryx2-ffi/ffi/src/api/port_factory_listener_builder.rs index d82f8b01f..53eeb130b 100644 --- a/iceoryx2-ffi/ffi/src/api/port_factory_listener_builder.rs +++ b/iceoryx2-ffi/ffi/src/api/port_factory_listener_builder.rs @@ -159,12 +159,19 @@ pub unsafe extern "C" fn iox2_port_factory_listener_builder_create( debug_assert!(!listener_struct_ptr.is_null()); let listener_builder_struct = unsafe { &mut *port_factory_handle.as_type() }; - let service_type = listener_builder_struct.service_type; + let listener_builder = listener_builder_struct + .value + .as_option_mut() + .take() + .unwrap_or_else(|| { + panic!("Trying to use an invalid 'iox2_port_factory_listener_builder_h'!") + }); + (listener_builder_struct.deleter)(listener_builder_struct); + match service_type { iox2_service_type_e::IPC => { - let listener_builder = - ManuallyDrop::take(&mut listener_builder_struct.value.as_mut().ipc); + let listener_builder = ManuallyDrop::into_inner(listener_builder.ipc); match listener_builder.create() { Ok(listener) => { @@ -180,8 +187,7 @@ pub unsafe extern "C" fn iox2_port_factory_listener_builder_create( } } iox2_service_type_e::LOCAL => { - let listener_builder = - ManuallyDrop::take(&mut listener_builder_struct.value.as_mut().local); + let listener_builder = ManuallyDrop::into_inner(listener_builder.local); match listener_builder.create() { Ok(listener) => { diff --git a/iceoryx2-ffi/ffi/src/api/port_factory_notifier_builder.rs b/iceoryx2-ffi/ffi/src/api/port_factory_notifier_builder.rs index d9390e589..3cf64246b 100644 --- a/iceoryx2-ffi/ffi/src/api/port_factory_notifier_builder.rs +++ b/iceoryx2-ffi/ffi/src/api/port_factory_notifier_builder.rs @@ -215,12 +215,19 @@ pub unsafe extern "C" fn iox2_port_factory_notifier_builder_create( debug_assert!(!notifier_struct_ptr.is_null()); let notifier_builder_struct = unsafe { &mut *port_factory_handle.as_type() }; - let service_type = notifier_builder_struct.service_type; + let notifier_builder = notifier_builder_struct + .value + .as_option_mut() + .take() + .unwrap_or_else(|| { + panic!("Trying to use an invalid 'iox2_port_factory_notifier_builder_h'!") + }); + (notifier_builder_struct.deleter)(notifier_builder_struct); + match service_type { iox2_service_type_e::IPC => { - let notifier_builder = - ManuallyDrop::take(&mut notifier_builder_struct.value.as_mut().ipc); + let notifier_builder = ManuallyDrop::into_inner(notifier_builder.ipc); match notifier_builder.create() { Ok(notifier) => { @@ -236,8 +243,7 @@ pub unsafe extern "C" fn iox2_port_factory_notifier_builder_create( } } iox2_service_type_e::LOCAL => { - let notifier_builder = - ManuallyDrop::take(&mut notifier_builder_struct.value.as_mut().local); + let notifier_builder = ManuallyDrop::into_inner(notifier_builder.local); match notifier_builder.create() { Ok(notifier) => { diff --git a/iceoryx2-ffi/ffi/src/api/port_factory_pub_sub.rs b/iceoryx2-ffi/ffi/src/api/port_factory_pub_sub.rs index 4688162cf..99139dc83 100644 --- a/iceoryx2-ffi/ffi/src/api/port_factory_pub_sub.rs +++ b/iceoryx2-ffi/ffi/src/api/port_factory_pub_sub.rs @@ -12,7 +12,13 @@ #![allow(non_camel_case_types)] -use crate::api::{iox2_service_type_e, HandleToType, NoUserHeaderFfi, PayloadFfi}; +use crate::api::{ + iox2_port_factory_publisher_builder_h, iox2_port_factory_publisher_builder_t, + iox2_port_factory_subscriber_builder_h, iox2_port_factory_subscriber_builder_t, + iox2_service_type_e, HandleToType, NoUserHeaderFfi, PayloadFfi, + PortFactoryPublisherBuilderUnion, PortFactorySubscriberBuilderUnion, +}; +use crate::iox2_static_config_publish_subscribe_t; use iceoryx2::prelude::*; use iceoryx2::service::port_factory::publish_subscribe::PortFactory; @@ -122,6 +128,140 @@ pub unsafe extern "C" fn iox2_cast_port_factory_pub_sub_ref_h( (*port_factory_handle.as_type()).as_ref_handle() as *mut _ as _ } +/// Instantiates a [`iox2_port_factory_publisher_builder_h`] to build a publisher. +/// +/// # Arguments +/// +/// * `port_factory_handle` - Must be a valid [`iox2_port_factory_pub_sub_ref_h`] obtained +/// by e.g. [`iox2_service_builder_pub_sub_open_or_create`](crate::iox2_service_builder_pub_sub_open_or_create) +/// and casted by [`iox2_cast_port_factory_pub_sub_ref_h`] +/// * `publisher_builder_struct_ptr` - Must be either a NULL pointer or a pointer to a valid [`iox2_port_factory_publisher_builder_t`]. +/// If it is a NULL pointer, the storage will be allocated on the heap. +/// +/// Returns the `iox2_port_factory_publisher_builder_h` handle for the publisher builder. +/// +/// # Safety +/// +/// * The `port_factory_handle` is still valid after the return of this function and can be use in another function call. +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_pub_sub_publisher_builder( + port_factory_handle: iox2_port_factory_pub_sub_ref_h, + publisher_builder_struct_ptr: *mut iox2_port_factory_publisher_builder_t, +) -> iox2_port_factory_publisher_builder_h { + debug_assert!(!port_factory_handle.is_null()); + + let mut publisher_builder_struct_ptr = publisher_builder_struct_ptr; + fn no_op(_: *mut iox2_port_factory_publisher_builder_t) {} + let mut deleter: fn(*mut iox2_port_factory_publisher_builder_t) = no_op; + if publisher_builder_struct_ptr.is_null() { + publisher_builder_struct_ptr = iox2_port_factory_publisher_builder_t::alloc(); + deleter = iox2_port_factory_publisher_builder_t::dealloc; + } + debug_assert!(!publisher_builder_struct_ptr.is_null()); + + let port_factory = &mut *port_factory_handle.as_type(); + match port_factory.service_type { + iox2_service_type_e::IPC => { + let publisher_builder = port_factory.value.as_ref().ipc.publisher_builder(); + (*publisher_builder_struct_ptr).init( + port_factory.service_type, + PortFactoryPublisherBuilderUnion::new_ipc(publisher_builder), + deleter, + ); + } + iox2_service_type_e::LOCAL => { + let publisher_builder = port_factory.value.as_ref().local.publisher_builder(); + (*publisher_builder_struct_ptr).init( + port_factory.service_type, + PortFactoryPublisherBuilderUnion::new_local(publisher_builder), + deleter, + ); + } + }; + + (*publisher_builder_struct_ptr).as_handle() +} + +/// Instantiates a [`iox2_port_factory_subscriber_builder_h`] to build a subscriber. +/// +/// # Arguments +/// +/// * `port_factory_handle` - Must be a valid [`iox2_port_factory_pub_sub_ref_h`] obtained +/// by e.g. [`iox2_service_builder_pub_sub_open_or_create`](crate::iox2_service_builder_pub_sub_open_or_create) +/// and casted by [`iox2_cast_port_factory_pub_sub_ref_h`] +/// * `subscriber_builder_struct_ptr` - Must be either a NULL pointer or a pointer to a valid [`iox2_port_factory_subscriber_builder_t`]. +/// If it is a NULL pointer, the storage will be allocated on the heap. +/// +/// Returns the [`iox2_port_factory_subscriber_builder_h`] handle for the subscriber builder. +/// +/// # Safety +/// +/// * The `port_factory_handle` is still valid after the return of this function and can be use in another function call. +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_pub_sub_subscriber_builder( + port_factory_handle: iox2_port_factory_pub_sub_ref_h, + subscriber_builder_struct_ptr: *mut iox2_port_factory_subscriber_builder_t, +) -> iox2_port_factory_subscriber_builder_h { + debug_assert!(!port_factory_handle.is_null()); + + let mut subscriber_builder_struct_ptr = subscriber_builder_struct_ptr; + fn no_op(_: *mut iox2_port_factory_subscriber_builder_t) {} + let mut deleter: fn(*mut iox2_port_factory_subscriber_builder_t) = no_op; + if subscriber_builder_struct_ptr.is_null() { + subscriber_builder_struct_ptr = iox2_port_factory_subscriber_builder_t::alloc(); + deleter = iox2_port_factory_subscriber_builder_t::dealloc; + } + debug_assert!(!subscriber_builder_struct_ptr.is_null()); + + let port_factory = &mut *port_factory_handle.as_type(); + match port_factory.service_type { + iox2_service_type_e::IPC => { + let subscriber_builder = port_factory.value.as_ref().ipc.subscriber_builder(); + (*subscriber_builder_struct_ptr).init( + port_factory.service_type, + PortFactorySubscriberBuilderUnion::new_ipc(subscriber_builder), + deleter, + ); + } + iox2_service_type_e::LOCAL => { + let subscriber_builder = port_factory.value.as_ref().local.subscriber_builder(); + (*subscriber_builder_struct_ptr).init( + port_factory.service_type, + PortFactorySubscriberBuilderUnion::new_local(subscriber_builder), + deleter, + ); + } + }; + + (*subscriber_builder_struct_ptr).as_handle() +} + +/// Set the values int the provided [`iox2_static_config_publish_subscribe_t`] pointer. +/// +/// # Safety +/// +/// * The `_handle` must be valid and obtained by [`iox2_service_builder_pub_sub_open`](crate::iox2_service_builder_pub_sub_open) or +/// [`iox2_service_builder_pub_sub_open_or_create`](crate::iox2_service_builder_pub_sub_open_or_create)! +/// * The `static_config` must be a valid pointer and non-null. +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_pub_sub_static_config( + port_factory_handle: iox2_port_factory_pub_sub_ref_h, + static_config: *mut iox2_static_config_publish_subscribe_t, +) { + debug_assert!(!port_factory_handle.is_null()); + debug_assert!(!static_config.is_null()); + + let port_factory = &mut *port_factory_handle.as_type(); + + use iceoryx2::prelude::PortFactory; + let config = match port_factory.service_type { + iox2_service_type_e::IPC => port_factory.value.as_ref().ipc.static_config(), + iox2_service_type_e::LOCAL => port_factory.value.as_ref().local.static_config(), + }; + + *static_config = config.into(); +} + /// This function needs to be called to destroy the port factory! /// /// # Arguments diff --git a/iceoryx2-ffi/ffi/src/api/port_factory_publisher_builder.rs b/iceoryx2-ffi/ffi/src/api/port_factory_publisher_builder.rs new file mode 100644 index 000000000..1c24adb45 --- /dev/null +++ b/iceoryx2-ffi/ffi/src/api/port_factory_publisher_builder.rs @@ -0,0 +1,288 @@ +// 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 + +#![allow(non_camel_case_types)] + +use crate::api::{ + c_size_t, iox2_publisher_h, iox2_publisher_t, iox2_service_type_e, HandleToType, IntoCInt, + NoUserHeaderFfi, PayloadFfi, PublisherUnion, IOX2_OK, +}; + +use iceoryx2::port::publisher::PublisherCreateError; +use iceoryx2::prelude::*; +use iceoryx2::service::port_factory::publisher::PortFactoryPublisher; +use iceoryx2_bb_elementary::static_assert::*; +use iceoryx2_ffi_macros::iceoryx2_ffi; + +use core::ffi::c_int; +use core::mem::ManuallyDrop; + +// BEGIN types definition + +#[repr(C)] +#[derive(Copy, Clone)] +pub enum iox2_publisher_create_error_e { + EXCEEDS_MAX_SUPPORTED_PUBLISHERS = IOX2_OK as isize + 1, + UNABLE_TO_CREATE_DATA_SEGMENT, +} + +impl IntoCInt for PublisherCreateError { + fn into_c_int(self) -> c_int { + (match self { + PublisherCreateError::ExceedsMaxSupportedPublishers => { + iox2_publisher_create_error_e::EXCEEDS_MAX_SUPPORTED_PUBLISHERS + } + PublisherCreateError::UnableToCreateDataSegment => { + iox2_publisher_create_error_e::UNABLE_TO_CREATE_DATA_SEGMENT + } + }) as c_int + } +} + +pub(super) union PortFactoryPublisherBuilderUnion { + ipc: ManuallyDrop< + PortFactoryPublisher<'static, zero_copy::Service, PayloadFfi, NoUserHeaderFfi>, + >, + local: ManuallyDrop< + PortFactoryPublisher<'static, process_local::Service, PayloadFfi, NoUserHeaderFfi>, + >, +} + +impl PortFactoryPublisherBuilderUnion { + pub(super) fn new_ipc( + port_factory: PortFactoryPublisher< + 'static, + zero_copy::Service, + PayloadFfi, + NoUserHeaderFfi, + >, + ) -> Self { + Self { + ipc: ManuallyDrop::new(port_factory), + } + } + pub(super) fn new_local( + port_factory: PortFactoryPublisher< + 'static, + process_local::Service, + PayloadFfi, + NoUserHeaderFfi, + >, + ) -> Self { + Self { + local: ManuallyDrop::new(port_factory), + } + } +} + +#[repr(C)] +#[repr(align(16))] // alignment of Option +pub struct iox2_port_factory_publisher_builder_storage_t { + internal: [u8; 128], // magic number obtained with size_of::>() +} + +#[repr(C)] +#[iceoryx2_ffi(PortFactoryPublisherBuilderUnion)] +pub struct iox2_port_factory_publisher_builder_t { + service_type: iox2_service_type_e, + value: iox2_port_factory_publisher_builder_storage_t, + deleter: fn(*mut iox2_port_factory_publisher_builder_t), +} + +impl iox2_port_factory_publisher_builder_t { + pub(super) fn init( + &mut self, + service_type: iox2_service_type_e, + value: PortFactoryPublisherBuilderUnion, + deleter: fn(*mut iox2_port_factory_publisher_builder_t), + ) { + self.service_type = service_type; + self.value.init(value); + self.deleter = deleter; + } +} + +pub struct iox2_port_factory_publisher_builder_h_t; +/// The owning handle for `iox2_port_factory_publisher_builder_t`. Passing the handle to an function transfers the ownership. +pub type iox2_port_factory_publisher_builder_h = *mut iox2_port_factory_publisher_builder_h_t; + +pub struct iox2_port_factory_publisher_builder_ref_h_t; +/// The non-owning handle for `iox2_port_factory_publisher_builder_t`. Passing the handle to an function does not transfers the ownership. +pub type iox2_port_factory_publisher_builder_ref_h = + *mut iox2_port_factory_publisher_builder_ref_h_t; + +impl HandleToType for iox2_port_factory_publisher_builder_h { + type Target = *mut iox2_port_factory_publisher_builder_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +impl HandleToType for iox2_port_factory_publisher_builder_ref_h { + type Target = *mut iox2_port_factory_publisher_builder_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +// END type definition + +// BEGIN C API + +/// This function casts an owning [`iox2_port_factory_publisher_builder_h`] into a non-owning [`iox2_port_factory_publisher_builder_ref_h`] +/// +/// # Arguments +/// +/// * `port_factory_handle` obtained by [`iox2_port_factory_pub_sub_publisher_builder`](crate::iox2_port_factory_pub_sub_publisher_builder) +/// +/// Returns a [`iox2_port_factory_publisher_builder_ref_h`] +/// +/// # Safety +/// +/// * The `port_factory_handle` must be a valid handle. +/// * The `port_factory_handle` is still valid after the call to this function. +#[no_mangle] +pub unsafe extern "C" fn iox2_cast_port_factory_publisher_builder_ref_h( + port_factory_handle: iox2_port_factory_publisher_builder_h, +) -> iox2_port_factory_publisher_builder_ref_h { + debug_assert!(!port_factory_handle.is_null()); + + (*port_factory_handle.as_type()).as_ref_handle() as *mut _ as _ +} + +/// Sets the max loaned samples for the publisher +/// +/// # Arguments +/// +/// * `port_factory_handle` - Must be a valid [`iox2_port_factory_publisher_builder_ref_h`] +/// obtained by [`iox2_port_factory_pub_sub_publisher_builder`](crate::iox2_port_factory_pub_sub_publisher_builder) and +/// casted by [`iox2_cast_port_factory_publisher_builder_ref_h`]. +/// * `value` - The value to set max loaned samples to +/// +/// # Safety +/// +/// * `port_factory_handle` must be valid handles +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_publisher_builder_set_max_loaned_samples( + port_factory_handle: iox2_port_factory_publisher_builder_ref_h, + value: c_size_t, +) { + debug_assert!(!port_factory_handle.is_null()); + + let port_factory_struct = unsafe { &mut *port_factory_handle.as_type() }; + match port_factory_struct.service_type { + iox2_service_type_e::IPC => { + let port_factory = ManuallyDrop::take(&mut port_factory_struct.value.as_mut().ipc); + + port_factory_struct.set(PortFactoryPublisherBuilderUnion::new_ipc( + port_factory.max_loaned_samples(value), + )); + } + iox2_service_type_e::LOCAL => { + let port_factory = ManuallyDrop::take(&mut port_factory_struct.value.as_mut().local); + + port_factory_struct.set(PortFactoryPublisherBuilderUnion::new_local( + port_factory.max_loaned_samples(value), + )); + } + } +} + +// TODO [#210] add all the other setter methods + +/// Creates a publisher and consumes the builder +/// +/// # Arguments +/// +/// * `port_factory_handle` - Must be a valid [`iox2_port_factory_publisher_builder_h`] obtained by [`iox2_port_factory_pub_sub_publisher_builder`](crate::iox2_port_factory_pub_sub_publisher_builder). +/// * `publisher_struct_ptr` - Must be either a NULL pointer or a pointer to a valid [`iox2_publisher_t`]. If it is a NULL pointer, the storage will be allocated on the heap. +/// * `publisher_handle_ptr` - An uninitialized or dangling [`iox2_publisher_h`] handle which will be initialized by this function call. +/// +/// Returns IOX2_OK on success, an [`iox2_publisher_create_error_e`] otherwise. +/// +/// # Safety +/// +/// * The `port_factory_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call! +/// * The corresponding [`iox2_port_factory_publisher_builder_t`] +/// can be re-used with a call to [`iox2_port_factory_pub_sub_publisher_builder`](crate::iox2_port_factory_pub_sub_publisher_builder)! +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_publisher_builder_create( + port_factory_handle: iox2_port_factory_publisher_builder_h, + publisher_struct_ptr: *mut iox2_publisher_t, + publisher_handle_ptr: *mut iox2_publisher_h, +) -> c_int { + debug_assert!(!port_factory_handle.is_null()); + debug_assert!(!publisher_handle_ptr.is_null()); + + let mut publisher_struct_ptr = publisher_struct_ptr; + fn no_op(_: *mut iox2_publisher_t) {} + let mut deleter: fn(*mut iox2_publisher_t) = no_op; + if publisher_struct_ptr.is_null() { + publisher_struct_ptr = iox2_publisher_t::alloc(); + deleter = iox2_publisher_t::dealloc; + } + debug_assert!(!publisher_struct_ptr.is_null()); + + let publisher_builder_struct = unsafe { &mut *port_factory_handle.as_type() }; + let service_type = publisher_builder_struct.service_type; + let publisher_builder = publisher_builder_struct + .value + .as_option_mut() + .take() + .unwrap_or_else(|| { + panic!("Trying to use an invalid 'iox2_port_factory_publisher_builder_h'!") + }); + (publisher_builder_struct.deleter)(publisher_builder_struct); + + match service_type { + iox2_service_type_e::IPC => { + let publisher_builder = ManuallyDrop::into_inner(publisher_builder.ipc); + + match publisher_builder.create() { + Ok(publisher) => { + (*publisher_struct_ptr).init( + service_type, + PublisherUnion::new_ipc(publisher), + deleter, + ); + } + Err(error) => { + return error.into_c_int(); + } + } + } + iox2_service_type_e::LOCAL => { + let publisher_builder = ManuallyDrop::into_inner(publisher_builder.local); + + match publisher_builder.create() { + Ok(publisher) => { + (*publisher_struct_ptr).init( + service_type, + PublisherUnion::new_local(publisher), + deleter, + ); + } + Err(error) => { + return error.into_c_int(); + } + } + } + } + + *publisher_handle_ptr = (*publisher_struct_ptr).as_handle(); + + IOX2_OK +} + +// END C API diff --git a/iceoryx2-ffi/ffi/src/api/port_factory_subscriber_builder.rs b/iceoryx2-ffi/ffi/src/api/port_factory_subscriber_builder.rs new file mode 100644 index 000000000..ee097fd6a --- /dev/null +++ b/iceoryx2-ffi/ffi/src/api/port_factory_subscriber_builder.rs @@ -0,0 +1,288 @@ +// 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 + +#![allow(non_camel_case_types)] + +use crate::api::{ + c_size_t, iox2_service_type_e, iox2_subscriber_h, iox2_subscriber_t, HandleToType, IntoCInt, + NoUserHeaderFfi, PayloadFfi, SubscriberUnion, IOX2_OK, +}; + +use iceoryx2::port::subscriber::SubscriberCreateError; +use iceoryx2::prelude::*; +use iceoryx2::service::port_factory::subscriber::PortFactorySubscriber; +use iceoryx2_bb_elementary::static_assert::*; +use iceoryx2_ffi_macros::iceoryx2_ffi; + +use core::ffi::c_int; +use core::mem::ManuallyDrop; + +// BEGIN types definition + +#[repr(C)] +#[derive(Copy, Clone)] +pub enum iox2_subscriber_create_error_e { + EXCEEDS_MAX_SUPPORTED_SUBSCRIBERS = IOX2_OK as isize + 1, + BUFFER_SIZE_EXCEEDS_MAX_SUPPORTED_BUFFER_SIZE_OF_SERVICE, +} + +impl IntoCInt for SubscriberCreateError { + fn into_c_int(self) -> c_int { + (match self { + SubscriberCreateError::ExceedsMaxSupportedSubscribers => { + iox2_subscriber_create_error_e::EXCEEDS_MAX_SUPPORTED_SUBSCRIBERS + } + SubscriberCreateError::BufferSizeExceedsMaxSupportedBufferSizeOfService => { + iox2_subscriber_create_error_e::BUFFER_SIZE_EXCEEDS_MAX_SUPPORTED_BUFFER_SIZE_OF_SERVICE + } + }) as c_int + } +} + +pub(super) union PortFactorySubscriberBuilderUnion { + ipc: ManuallyDrop< + PortFactorySubscriber<'static, zero_copy::Service, PayloadFfi, NoUserHeaderFfi>, + >, + local: ManuallyDrop< + PortFactorySubscriber<'static, process_local::Service, PayloadFfi, NoUserHeaderFfi>, + >, +} + +impl PortFactorySubscriberBuilderUnion { + pub(super) fn new_ipc( + port_factory: PortFactorySubscriber< + 'static, + zero_copy::Service, + PayloadFfi, + NoUserHeaderFfi, + >, + ) -> Self { + Self { + ipc: ManuallyDrop::new(port_factory), + } + } + pub(super) fn new_local( + port_factory: PortFactorySubscriber< + 'static, + process_local::Service, + PayloadFfi, + NoUserHeaderFfi, + >, + ) -> Self { + Self { + local: ManuallyDrop::new(port_factory), + } + } +} + +#[repr(C)] +#[repr(align(16))] // alignment of Option +pub struct iox2_port_factory_subscriber_builder_storage_t { + internal: [u8; 112], // magic number obtained with size_of::>() +} + +#[repr(C)] +#[iceoryx2_ffi(PortFactorySubscriberBuilderUnion)] +pub struct iox2_port_factory_subscriber_builder_t { + service_type: iox2_service_type_e, + value: iox2_port_factory_subscriber_builder_storage_t, + deleter: fn(*mut iox2_port_factory_subscriber_builder_t), +} + +impl iox2_port_factory_subscriber_builder_t { + pub(super) fn init( + &mut self, + service_type: iox2_service_type_e, + value: PortFactorySubscriberBuilderUnion, + deleter: fn(*mut iox2_port_factory_subscriber_builder_t), + ) { + self.service_type = service_type; + self.value.init(value); + self.deleter = deleter; + } +} + +pub struct iox2_port_factory_subscriber_builder_h_t; +/// The owning handle for `iox2_port_factory_subscriber_builder_t`. Passing the handle to an function transfers the ownership. +pub type iox2_port_factory_subscriber_builder_h = *mut iox2_port_factory_subscriber_builder_h_t; + +pub struct iox2_port_factory_subscriber_builder_ref_h_t; +/// The non-owning handle for `iox2_port_factory_subscriber_builder_t`. Passing the handle to an function does not transfers the ownership. +pub type iox2_port_factory_subscriber_builder_ref_h = + *mut iox2_port_factory_subscriber_builder_ref_h_t; + +impl HandleToType for iox2_port_factory_subscriber_builder_h { + type Target = *mut iox2_port_factory_subscriber_builder_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +impl HandleToType for iox2_port_factory_subscriber_builder_ref_h { + type Target = *mut iox2_port_factory_subscriber_builder_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +// END type definition + +// BEGIN C API + +/// This function casts an owning [`iox2_port_factory_subscriber_builder_h`] into a non-owning [`iox2_port_factory_subscriber_builder_ref_h`] +/// +/// # Arguments +/// +/// * `port_factory_handle` obtained by [`iox2_port_factory_pub_sub_subscriber_builder`](crate::iox2_port_factory_pub_sub_subscriber_builder) +/// +/// Returns a [`iox2_port_factory_subscriber_builder_ref_h`] +/// +/// # Safety +/// +/// * The `port_factory_handle` must be a valid handle. +/// * The `port_factory_handle` is still valid after the call to this function. +#[no_mangle] +pub unsafe extern "C" fn iox2_cast_port_factory_subscriber_builder_ref_h( + port_factory_handle: iox2_port_factory_subscriber_builder_h, +) -> iox2_port_factory_subscriber_builder_ref_h { + debug_assert!(!port_factory_handle.is_null()); + + (*port_factory_handle.as_type()).as_ref_handle() as *mut _ as _ +} + +/// Sets the buffer size for the subscriber +/// +/// # Arguments +/// +/// * `port_factory_handle` - Must be a valid [`iox2_port_factory_subscriber_builder_ref_h`] +/// obtained by [`iox2_port_factory_pub_sub_subscriber_builder`](crate::iox2_port_factory_pub_sub_subscriber_builder) and +/// casted by [`iox2_cast_port_factory_subscriber_builder_ref_h`]. +/// * `value` - The value to set buffer size to +/// +/// # Safety +/// +/// * `port_factory_handle` must be valid handles +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_subscriber_builder_set_buffer_size( + port_factory_handle: iox2_port_factory_subscriber_builder_ref_h, + value: c_size_t, +) { + debug_assert!(!port_factory_handle.is_null()); + + let port_factory_struct = unsafe { &mut *port_factory_handle.as_type() }; + match port_factory_struct.service_type { + iox2_service_type_e::IPC => { + let port_factory = ManuallyDrop::take(&mut port_factory_struct.value.as_mut().ipc); + + port_factory_struct.set(PortFactorySubscriberBuilderUnion::new_ipc( + port_factory.buffer_size(value), + )); + } + iox2_service_type_e::LOCAL => { + let port_factory = ManuallyDrop::take(&mut port_factory_struct.value.as_mut().local); + + port_factory_struct.set(PortFactorySubscriberBuilderUnion::new_local( + port_factory.buffer_size(value), + )); + } + } +} + +// TODO [#210] add all the other setter methods + +/// Creates a subscriber and consumes the builder +/// +/// # Arguments +/// +/// * `port_factory_handle` - Must be a valid [`iox2_port_factory_subscriber_builder_h`] obtained by [`iox2_port_factory_pub_sub_subscriber_builder`](crate::iox2_port_factory_pub_sub_subscriber_builder). +/// * `subscriber_struct_ptr` - Must be either a NULL pointer or a pointer to a valid [`iox2_subscriber_t`]. If it is a NULL pointer, the storage will be allocated on the heap. +/// * `subscriber_handle_ptr` - An uninitialized or dangling [`iox2_subscriber_h`] handle which will be initialized by this function call. +/// +/// Returns IOX2_OK on success, an [`iox2_subscriber_create_error_e`] otherwise. +/// +/// # Safety +/// +/// * The `port_factory_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call! +/// * The corresponding [`iox2_port_factory_subscriber_builder_t`] +/// can be re-used with a call to [`iox2_port_factory_pub_sub_subscriber_builder`](crate::iox2_port_factory_pub_sub_subscriber_builder)! +#[no_mangle] +pub unsafe extern "C" fn iox2_port_factory_subscriber_builder_create( + port_factory_handle: iox2_port_factory_subscriber_builder_h, + subscriber_struct_ptr: *mut iox2_subscriber_t, + subscriber_handle_ptr: *mut iox2_subscriber_h, +) -> c_int { + debug_assert!(!port_factory_handle.is_null()); + debug_assert!(!subscriber_handle_ptr.is_null()); + + let mut subscriber_struct_ptr = subscriber_struct_ptr; + fn no_op(_: *mut iox2_subscriber_t) {} + let mut deleter: fn(*mut iox2_subscriber_t) = no_op; + if subscriber_struct_ptr.is_null() { + subscriber_struct_ptr = iox2_subscriber_t::alloc(); + deleter = iox2_subscriber_t::dealloc; + } + debug_assert!(!subscriber_struct_ptr.is_null()); + + let subscriber_builder_struct = unsafe { &mut *port_factory_handle.as_type() }; + let service_type = subscriber_builder_struct.service_type; + let subscriber_builder = subscriber_builder_struct + .value + .as_option_mut() + .take() + .unwrap_or_else(|| { + panic!("Trying to use an invalid 'iox2_port_factory_subscriber_builder_h'!") + }); + (subscriber_builder_struct.deleter)(subscriber_builder_struct); + + match service_type { + iox2_service_type_e::IPC => { + let subscriber_builder = ManuallyDrop::into_inner(subscriber_builder.ipc); + + match subscriber_builder.create() { + Ok(subscriber) => { + (*subscriber_struct_ptr).init( + service_type, + SubscriberUnion::new_ipc(subscriber), + deleter, + ); + } + Err(error) => { + return error.into_c_int(); + } + } + } + iox2_service_type_e::LOCAL => { + let subscriber_builder = ManuallyDrop::into_inner(subscriber_builder.local); + + match subscriber_builder.create() { + Ok(subscriber) => { + (*subscriber_struct_ptr).init( + service_type, + SubscriberUnion::new_local(subscriber), + deleter, + ); + } + Err(error) => { + return error.into_c_int(); + } + } + } + } + + *subscriber_handle_ptr = (*subscriber_struct_ptr).as_handle(); + + IOX2_OK +} + +// END C API diff --git a/iceoryx2-ffi/ffi/src/api/publisher.rs b/iceoryx2-ffi/ffi/src/api/publisher.rs index 3dccb8ed4..6c4763a6d 100644 --- a/iceoryx2-ffi/ffi/src/api/publisher.rs +++ b/iceoryx2-ffi/ffi/src/api/publisher.rs @@ -10,74 +10,362 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT -use core::time::Duration; +#![allow(non_camel_case_types)] + +use crate::api::{iox2_service_type_e, HandleToType, NoUserHeaderFfi, PayloadFfi, SampleMutUnion}; +use crate::IOX2_OK; + +use iceoryx2::port::publisher::{Publisher, PublisherLoanError, PublisherSendError}; use iceoryx2::prelude::*; -use iceoryx2_bb_log::set_log_level; +use iceoryx2_bb_elementary::static_assert::*; +use iceoryx2_ffi_macros::iceoryx2_ffi; -const CYCLE_TIME: Duration = Duration::from_secs(1); +use super::{iox2_sample_mut_h, iox2_sample_mut_t, IntoCInt}; -#[no_mangle] -pub extern "C" fn run_publisher(seconds: u32) -> i32 { - set_log_level(iceoryx2_bb_log::LogLevel::Info); +use core::ffi::{c_int, c_void}; +use core::mem::ManuallyDrop; - let service_name = ServiceName::new("Hello/from/C"); - let node = NodeBuilder::new().create::(); +// BEGIN types definition - if service_name.is_err() || node.is_err() { - return -1; +#[repr(C)] +#[derive(Copy, Clone)] +pub enum iox2_publisher_send_error_e { + CONNECTION_BROKEN_SINCE_PUBLISHER_NO_LONGER_EXISTS = IOX2_OK as isize + 1, + CONNECTION_CORRUPTED, + LOAN_ERROR_OUT_OF_MEMORY, + LOAN_ERROR_EXCEEDS_MAX_LOANED_SAMPLES, + LOAN_ERROR_EXCEEDS_MAX_LOAN_SIZE, + LOAN_ERROR_INTERNAL_FAILURE, + CONNECTION_ERROR, +} + +impl IntoCInt for PublisherSendError { + fn into_c_int(self) -> c_int { + (match self { + PublisherSendError::ConnectionBrokenSincePublisherNoLongerExists => { + iox2_publisher_send_error_e::CONNECTION_BROKEN_SINCE_PUBLISHER_NO_LONGER_EXISTS + } + PublisherSendError::ConnectionCorrupted => { + iox2_publisher_send_error_e::CONNECTION_CORRUPTED + } + PublisherSendError::LoanError(PublisherLoanError::OutOfMemory) => { + iox2_publisher_send_error_e::LOAN_ERROR_OUT_OF_MEMORY + } + PublisherSendError::LoanError(PublisherLoanError::ExceedsMaxLoanedSamples) => { + iox2_publisher_send_error_e::LOAN_ERROR_EXCEEDS_MAX_LOANED_SAMPLES + } + PublisherSendError::LoanError(PublisherLoanError::ExceedsMaxLoanSize) => { + iox2_publisher_send_error_e::LOAN_ERROR_EXCEEDS_MAX_LOAN_SIZE + } + PublisherSendError::LoanError(PublisherLoanError::InternalFailure) => { + iox2_publisher_send_error_e::LOAN_ERROR_INTERNAL_FAILURE + } + PublisherSendError::ConnectionError(_) => iox2_publisher_send_error_e::CONNECTION_ERROR, + }) as c_int } +} - let service_name = service_name.unwrap(); - let node = node.unwrap(); +impl IntoCInt for PublisherLoanError { + fn into_c_int(self) -> c_int { + (match self { + PublisherLoanError::OutOfMemory => { + iox2_publisher_send_error_e::LOAN_ERROR_OUT_OF_MEMORY + } + PublisherLoanError::ExceedsMaxLoanedSamples => { + iox2_publisher_send_error_e::LOAN_ERROR_EXCEEDS_MAX_LOANED_SAMPLES + } + PublisherLoanError::ExceedsMaxLoanSize => { + iox2_publisher_send_error_e::LOAN_ERROR_EXCEEDS_MAX_LOAN_SIZE + } + PublisherLoanError::InternalFailure => { + iox2_publisher_send_error_e::LOAN_ERROR_INTERNAL_FAILURE + } + }) as c_int + } +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub enum iox2_publisher_loan_error_e { + OUT_OF_MEMORY = IOX2_OK as isize + 1, + EXCEEDS_MAX_LOANED_SAMPLES, + EXCEEDS_MAX_LOAN_SIZE, + INTERNAL_FAILURE, +} - let service = node - .service_builder(&service_name) - .publish_subscribe::() - .open_or_create(); +pub(super) union PublisherUnion { + ipc: ManuallyDrop>, + local: ManuallyDrop>, +} - if service.is_err() { - return -1; +impl PublisherUnion { + pub(super) fn new_ipc( + publisher: Publisher, + ) -> Self { + Self { + ipc: ManuallyDrop::new(publisher), + } + } + pub(super) fn new_local( + publisher: Publisher, + ) -> Self { + Self { + local: ManuallyDrop::new(publisher), + } } +} - let service = service.unwrap(); +#[repr(C)] +#[repr(align(16))] // alignment of Option +pub struct iox2_publisher_storage_t { + internal: [u8; 40], // magic number obtained with size_of::>() +} - let publisher = service.publisher_builder().create(); +#[repr(C)] +#[iceoryx2_ffi(PublisherUnion)] +pub struct iox2_publisher_t { + service_type: iox2_service_type_e, + value: iox2_publisher_storage_t, + deleter: fn(*mut iox2_publisher_t), +} - if publisher.is_err() { - return -1; +impl iox2_publisher_t { + pub(super) fn init( + &mut self, + service_type: iox2_service_type_e, + value: PublisherUnion, + deleter: fn(*mut iox2_publisher_t), + ) { + self.service_type = service_type; + self.value.init(value); + self.deleter = deleter; } +} - let publisher = publisher.unwrap(); +pub struct iox2_publisher_h_t; +/// The owning handle for `iox2_publisher_t`. Passing the handle to an function transfers the ownership. +pub type iox2_publisher_h = *mut iox2_publisher_h_t; - let mut counter: u64 = 0; +pub struct iox2_publisher_ref_h_t; +/// The non-owning handle for `iox2_publisher_t`. Passing the handle to an function does not transfers the ownership. +pub type iox2_publisher_ref_h = *mut iox2_publisher_ref_h_t; - let mut remaining_seconds = seconds; +impl HandleToType for iox2_publisher_h { + type Target = *mut iox2_publisher_t; - while let NodeEvent::Tick = node.wait(CYCLE_TIME) { - counter += 1; - let sample = publisher.loan_uninit(); + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} - if sample.is_err() { - return -1; - } +impl HandleToType for iox2_publisher_ref_h { + type Target = *mut iox2_publisher_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +// END type definition - let sample = sample.unwrap(); +unsafe fn send_copy( + publisher: &Publisher, + data_ptr: *const c_void, + data_len: usize, + number_of_recipients: *mut usize, +) -> c_int { + // loan_slice_uninit(1) <= 1 is correct here since it defines the number of + // slice elements not bytes. The element was set via TypeDetails and has a + // defined size and alignment. + let mut sample = match publisher.loan_slice_uninit(1) { + Ok(sample) => sample, + Err(e) => return e.into_c_int(), + }; - let sample = sample.write_payload(counter); + if sample.payload().len() < data_len { + return iox2_publisher_send_error_e::LOAN_ERROR_EXCEEDS_MAX_LOAN_SIZE as c_int; + } - if sample.send().is_err() { - return -1; + let sample_ptr = sample.payload_mut().as_mut_ptr(); + core::ptr::copy_nonoverlapping(data_ptr, sample_ptr.cast(), data_len); + match sample.assume_init().send() { + Ok(v) => { + if !number_of_recipients.is_null() { + *number_of_recipients = v; + } } + Err(e) => return e.into_c_int(), + } + + IOX2_OK +} + +// BEGIN C API + +/// This function casts an owning [`iox2_publisher_h`] into a non-owning [`iox2_publisher_ref_h`] +/// +/// # Arguments +/// +/// * `handle` obtained by [`iox2_port_factory_publisher_builder_create`](crate::iox2_port_factory_publisher_builder_create) +/// +/// Returns a [`iox2_publisher_ref_h`] +/// +/// # Safety +/// +/// * The `handle` must be a valid handle. +/// * The `handle` is still valid after the call to this function. +#[no_mangle] +pub unsafe extern "C" fn iox2_cast_publisher_ref_h( + handle: iox2_publisher_h, +) -> iox2_publisher_ref_h { + debug_assert!(!handle.is_null()); + + (*handle.as_type()).as_ref_handle() as *mut _ as _ +} + +/// Sends a copy of the provided data via the publisher. The data must be copyable via `memcpy`. +/// +/// # Arguments +/// +/// * `handle` obtained by [`iox2_port_factory_publisher_builder_create`](crate::iox2_port_factory_publisher_builder_create) +/// * `data_ptr` pointer to the payload that shall be transmitted +/// * `data_len` the size of the payload in bytes +/// * `number_of_recipients` (optional) used to store the number of subscriber that received the data +/// +/// Return [`IOX2_OK`] on success, otherwise [`iox2_publisher_send_error_e`]. +/// +/// # Safety +/// +/// * `publisher_handle` is valid, non-null and was obtained via [`iox2_cast_publisher_ref_h`] +/// * `data_ptr` non-null pointer to a valid position in memory +/// * `data_len` the size of the payload memory +/// * `number_of_recipients` can be null, otherwise a valid pointer to an [`usize`] +#[no_mangle] +pub unsafe extern "C" fn iox2_publisher_send_copy( + publisher_handle: iox2_publisher_ref_h, + data_ptr: *const c_void, + data_len: usize, + number_of_recipients: *mut usize, +) -> c_int { + debug_assert!(!publisher_handle.is_null()); + debug_assert!(!data_ptr.is_null()); + debug_assert!(data_len != 0); + + let publisher = &mut *publisher_handle.as_type(); + + match publisher.service_type { + iox2_service_type_e::IPC => send_copy( + &publisher.value.as_mut().ipc, + data_ptr, + data_len, + number_of_recipients, + ), + iox2_service_type_e::LOCAL => send_copy( + &publisher.value.as_mut().local, + data_ptr, + data_len, + number_of_recipients, + ), + } +} + +/// Loans memory from the publishers data segment. +/// +/// # Arguments +/// +/// * `handle` obtained by [`iox2_port_factory_publisher_builder_create`](crate::iox2_port_factory_publisher_builder_create) +/// * `sample_struct_ptr` - Must be either a NULL pointer or a pointer to a valid [`iox2_sample_mut_t`]. +/// If it is a NULL pointer, the storage will be allocated on the heap. +/// * `sample_handle_ptr` - An uninitialized or dangling [`iox2_sample_mut_h`] handle which will be initialized by this function call if a sample is obtained, otherwise it will be set to NULL. +/// +/// Return [`IOX2_OK`] on success, otherwise [`iox2_publisher_loan_error_e`]. +/// +/// # Safety +/// +/// * `publisher_handle` is valid, non-null and was obtained via [`iox2_cast_publisher_ref_h`] +/// * The `sample_handle_ptr` is pointing to a valid [`iox2_sample_mut_h`]. +#[no_mangle] +pub unsafe extern "C" fn iox2_publisher_loan( + publisher_handle: iox2_publisher_ref_h, + sample_struct_ptr: *mut iox2_sample_mut_t, + sample_handle_ptr: *mut iox2_sample_mut_h, +) -> c_int { + debug_assert!(!publisher_handle.is_null()); + debug_assert!(!sample_handle_ptr.is_null()); - println!("Send sample {} ...", counter); + *sample_handle_ptr = std::ptr::null_mut(); - remaining_seconds = remaining_seconds.saturating_sub(1); - if remaining_seconds == 0 { - break; + let init_sample_struct_ptr = |sample_struct_ptr: *mut iox2_sample_mut_t| { + let mut sample_struct_ptr = sample_struct_ptr; + fn no_op(_: *mut iox2_sample_mut_t) {} + let mut deleter: fn(*mut iox2_sample_mut_t) = no_op; + if sample_struct_ptr.is_null() { + sample_struct_ptr = iox2_sample_mut_t::alloc(); + deleter = iox2_sample_mut_t::dealloc; } + debug_assert!(!sample_struct_ptr.is_null()); + + (sample_struct_ptr, deleter) + }; + + let publisher = &mut *publisher_handle.as_type(); + + match publisher.service_type { + iox2_service_type_e::IPC => match publisher.value.as_ref().ipc.loan_slice_uninit(1) { + Ok(sample) => { + let (sample_struct_ptr, deleter) = init_sample_struct_ptr(sample_struct_ptr); + (*sample_struct_ptr).init( + publisher.service_type, + SampleMutUnion::new_ipc(sample), + deleter, + ); + *sample_handle_ptr = (*sample_struct_ptr).as_handle(); + IOX2_OK + } + Err(error) => error.into_c_int(), + }, + iox2_service_type_e::LOCAL => match publisher.value.as_ref().local.loan_slice_uninit(1) { + Ok(sample) => { + let (sample_struct_ptr, deleter) = init_sample_struct_ptr(sample_struct_ptr); + (*sample_struct_ptr).init( + publisher.service_type, + SampleMutUnion::new_local(sample), + deleter, + ); + *sample_handle_ptr = (*sample_struct_ptr).as_handle(); + IOX2_OK + } + Err(error) => error.into_c_int(), + }, } +} - println!("exit"); +/// This function needs to be called to destroy the publisher! +/// +/// # Arguments +/// +/// * `publisher_handle` - A valid [`iox2_publisher_h`] +/// +/// # Safety +/// +/// * The `publisher_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call! +/// * The corresponding [`iox2_publisher_t`] can be re-used with a call to +/// [`iox2_port_factory_publisher_builder_create`](crate::iox2_port_factory_publisher_builder_create)! +#[no_mangle] +pub unsafe extern "C" fn iox2_publisher_drop(publisher_handle: iox2_publisher_h) { + debug_assert!(!publisher_handle.is_null()); + + let publisher = &mut *publisher_handle.as_type(); - 0 + match publisher.service_type { + iox2_service_type_e::IPC => { + ManuallyDrop::drop(&mut publisher.value.as_mut().ipc); + } + iox2_service_type_e::LOCAL => { + ManuallyDrop::drop(&mut publisher.value.as_mut().local); + } + } + (publisher.deleter)(publisher); } + +// END C API diff --git a/iceoryx2-ffi/ffi/src/api/quirks_correction.rs b/iceoryx2-ffi/ffi/src/api/quirks_correction.rs index dad8f1d48..a0eb0e823 100644 --- a/iceoryx2-ffi/ffi/src/api/quirks_correction.rs +++ b/iceoryx2-ffi/ffi/src/api/quirks_correction.rs @@ -19,8 +19,10 @@ use crate::{ iox2_event_open_or_create_error_e, iox2_listener_create_error_e, iox2_listener_wait_error_e, iox2_node_creation_failure_e, iox2_node_event_e, iox2_node_list_failure_e, iox2_notifier_create_error_e, iox2_notifier_notify_error_e, - iox2_pub_sub_open_or_create_error_e, iox2_semantic_string_error_e, - iox2_service_details_error_e, iox2_type_detail_error_e, + iox2_pub_sub_open_or_create_error_e, iox2_publisher_create_error_e, + iox2_publisher_loan_error_e, iox2_publisher_send_error_e, iox2_semantic_string_error_e, + iox2_service_details_error_e, iox2_subscriber_create_error_e, iox2_subscriber_receive_error_e, + iox2_type_detail_error_e, }; #[doc(hidden)] @@ -114,3 +116,43 @@ pub unsafe extern "C" fn __iox2_internal_notifier_notify_error_stub() -> iox2_no pub unsafe extern "C" fn __iox2_internal_listener_wait_error_stub() -> iox2_listener_wait_error_e { iox2_listener_wait_error_e::INTERNAL_FAILURE } + +#[doc(hidden)] +#[no_mangle] +// TODO: enums are only exported when they are actually used by some function +pub unsafe extern "C" fn __iox2_internal_publisher_create_error_stub( +) -> iox2_publisher_create_error_e { + iox2_publisher_create_error_e::EXCEEDS_MAX_SUPPORTED_PUBLISHERS +} + +#[doc(hidden)] +#[no_mangle] +// TODO: enums are only exported when they are actually used by some function +pub unsafe extern "C" fn __iox2_internal_subscriber_create_error_stub( +) -> iox2_subscriber_create_error_e { + iox2_subscriber_create_error_e::EXCEEDS_MAX_SUPPORTED_SUBSCRIBERS +} + +#[doc(hidden)] +#[no_mangle] +// TODO: enums are only exported when they are actually used by some function +pub unsafe extern "C" fn __iox2_internal_publisher_send_error_stub() -> iox2_publisher_send_error_e +{ + iox2_publisher_send_error_e::CONNECTION_ERROR +} + +#[doc(hidden)] +#[no_mangle] +// TODO: enums are only exported when they are actually used by some function +pub unsafe extern "C" fn __iox2_internal_publisher_loan_error_stub() -> iox2_publisher_loan_error_e +{ + iox2_publisher_loan_error_e::INTERNAL_FAILURE +} + +#[doc(hidden)] +#[no_mangle] +// TODO: enums are only exported when they are actually used by some function +pub unsafe extern "C" fn __iox2_internal_subscriber_receive_error_stub( +) -> iox2_subscriber_receive_error_e { + iox2_subscriber_receive_error_e::CONNECTION_FAILURE +} diff --git a/iceoryx2-ffi/ffi/src/api/sample.rs b/iceoryx2-ffi/ffi/src/api/sample.rs new file mode 100644 index 000000000..cbbf1c4f9 --- /dev/null +++ b/iceoryx2-ffi/ffi/src/api/sample.rs @@ -0,0 +1,178 @@ +// 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 + +#![allow(non_camel_case_types)] + +use crate::api::{iox2_service_type_e, HandleToType, NoUserHeaderFfi, PayloadFfi}; +use crate::c_size_t; + +use iceoryx2::prelude::*; +use iceoryx2::sample::Sample; +use iceoryx2_bb_elementary::static_assert::*; +use iceoryx2_ffi_macros::iceoryx2_ffi; + +use core::ffi::c_void; +use core::mem::ManuallyDrop; + +// BEGIN types definition + +pub(super) union SampleUnion { + ipc: ManuallyDrop>, + local: ManuallyDrop>, +} + +impl SampleUnion { + pub(super) fn new_ipc(sample: Sample) -> Self { + Self { + ipc: ManuallyDrop::new(sample), + } + } + pub(super) fn new_local( + sample: Sample, + ) -> Self { + Self { + local: ManuallyDrop::new(sample), + } + } +} + +#[repr(C)] +#[repr(align(8))] // alignment of Option +pub struct iox2_sample_storage_t { + internal: [u8; 80], // magic number obtained with size_of::>() +} + +#[repr(C)] +#[iceoryx2_ffi(SampleUnion)] +pub struct iox2_sample_t { + service_type: iox2_service_type_e, + value: iox2_sample_storage_t, + deleter: fn(*mut iox2_sample_t), +} + +impl iox2_sample_t { + pub(super) fn init( + &mut self, + service_type: iox2_service_type_e, + value: SampleUnion, + deleter: fn(*mut iox2_sample_t), + ) { + self.service_type = service_type; + self.value.init(value); + self.deleter = deleter; + } +} + +pub struct iox2_sample_h_t; +/// The owning handle for `iox2_sample_t`. Passing the handle to an function transfers the ownership. +pub type iox2_sample_h = *mut iox2_sample_h_t; + +pub struct iox2_sample_ref_h_t; +/// The non-owning handle for `iox2_sample_t`. Passing the handle to an function does not transfers the ownership. +pub type iox2_sample_ref_h = *mut iox2_sample_ref_h_t; + +impl HandleToType for iox2_sample_h { + type Target = *mut iox2_sample_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +impl HandleToType for iox2_sample_ref_h { + type Target = *mut iox2_sample_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +// END type definition + +// BEGIN C API + +/// This function casts an owning [`iox2_sample_h`] into a non-owning [`iox2_sample_ref_h`] +/// +/// # Arguments +/// +/// * `handle` obtained by [`iox2_subscriber_receive()`](crate::iox2_subscriber_receive()) +/// +/// Returns a [`iox2_sample_ref_h`] +/// +/// # Safety +/// +/// * The `handle` must be a valid handle. +/// * The `handle` is still valid after the call to this function. +#[no_mangle] +pub unsafe extern "C" fn iox2_cast_sample_ref_h(handle: iox2_sample_h) -> iox2_sample_ref_h { + debug_assert!(!handle.is_null()); + (*handle.as_type()).as_ref_handle() as *mut _ as _ +} + +/// Acquires the samples payload. +/// +/// # Safety +/// +/// * `handle` obtained by [`iox2_subscriber_receive()`](crate::iox2_subscriber_receive()) +/// * `payload_ptr` a valid, non-null pointer pointing to a [`*const c_void`] pointer. +/// * `payload_len` (optional) either a null poitner or a valid pointer pointing to a [`c_size_t`]. +#[no_mangle] +pub unsafe extern "C" fn iox2_sample_payload( + sample_handle: iox2_sample_ref_h, + payload_ptr: *mut *const c_void, + payload_len: *mut c_size_t, +) { + debug_assert!(!sample_handle.is_null()); + debug_assert!(!payload_ptr.is_null()); + + let sample = &mut *sample_handle.as_type(); + + let payload = match sample.service_type { + iox2_service_type_e::IPC => sample.value.as_mut().ipc.payload(), + iox2_service_type_e::LOCAL => sample.value.as_mut().local.payload(), + }; + + *payload_ptr = payload.as_ptr().cast(); + if !payload_len.is_null() { + *payload_len = payload.len(); + } +} + +/// This function needs to be called to destroy the sample! +/// +/// # Arguments +/// +/// * `sample_handle` - A valid [`iox2_sample_h`] +/// +/// # Safety +/// +/// * The `sample_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call! +/// * The corresponding [`iox2_sample_t`] can be re-used with a call to +/// [`iox2_subscriber_receive`](crate::iox2_subscriber_receive)! +#[no_mangle] +pub unsafe extern "C" fn iox2_sample_drop(sample_handle: iox2_sample_h) { + debug_assert!(!sample_handle.is_null()); + + let sample = &mut *sample_handle.as_type(); + + match sample.service_type { + iox2_service_type_e::IPC => { + ManuallyDrop::drop(&mut sample.value.as_mut().ipc); + } + iox2_service_type_e::LOCAL => { + ManuallyDrop::drop(&mut sample.value.as_mut().local); + } + } + (sample.deleter)(sample); +} + +// END C API diff --git a/iceoryx2-ffi/ffi/src/api/sample_mut.rs b/iceoryx2-ffi/ffi/src/api/sample_mut.rs new file mode 100644 index 000000000..3d30a2d4e --- /dev/null +++ b/iceoryx2-ffi/ffi/src/api/sample_mut.rs @@ -0,0 +1,269 @@ +// 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 + +#![allow(non_camel_case_types)] + +use crate::api::{iox2_service_type_e, HandleToType, IntoCInt, NoUserHeaderFfi}; +use crate::{c_size_t, IOX2_OK}; + +use iceoryx2::prelude::*; +use iceoryx2::sample_mut::SampleMut; +use iceoryx2_bb_elementary::static_assert::*; +use iceoryx2_ffi_macros::iceoryx2_ffi; + +use core::ffi::{c_int, c_void}; +use core::mem::ManuallyDrop; + +use super::UninitPayloadFfi; + +// BEGIN types definition + +pub(super) union SampleMutUnion { + ipc: ManuallyDrop>, + local: ManuallyDrop>, +} + +impl SampleMutUnion { + pub(super) fn new_ipc( + sample: SampleMut, + ) -> Self { + Self { + ipc: ManuallyDrop::new(sample), + } + } + pub(super) fn new_local( + sample: SampleMut, + ) -> Self { + Self { + local: ManuallyDrop::new(sample), + } + } +} + +#[repr(C)] +#[repr(align(8))] // alignment of Option +pub struct iox2_sample_mut_storage_t { + internal: [u8; 56], // magic number obtained with size_of::>() +} + +#[repr(C)] +#[iceoryx2_ffi(SampleMutUnion)] +pub struct iox2_sample_mut_t { + service_type: iox2_service_type_e, + value: iox2_sample_mut_storage_t, + deleter: fn(*mut iox2_sample_mut_t), +} + +impl iox2_sample_mut_t { + pub(super) fn init( + &mut self, + service_type: iox2_service_type_e, + value: SampleMutUnion, + deleter: fn(*mut iox2_sample_mut_t), + ) { + self.service_type = service_type; + self.value.init(value); + self.deleter = deleter; + } +} + +pub struct iox2_sample_mut_h_t; +/// The owning handle for `iox2_sample_mut_t`. Passing the handle to an function transfers the ownership. +pub type iox2_sample_mut_h = *mut iox2_sample_mut_h_t; + +pub struct iox2_sample_mut_ref_h_t; +/// The non-owning handle for `iox2_sample_mut_t`. Passing the handle to an function does not transfers the ownership. +pub type iox2_sample_mut_ref_h = *mut iox2_sample_mut_ref_h_t; + +impl HandleToType for iox2_sample_mut_h { + type Target = *mut iox2_sample_mut_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +impl HandleToType for iox2_sample_mut_ref_h { + type Target = *mut iox2_sample_mut_t; + + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} + +// END type definition + +// BEGIN C API + +/// This function casts an owning [`iox2_sample_mut_h`] into a non-owning [`iox2_sample_mut_ref_h`] +/// +/// # Arguments +/// +/// * `handle` obtained by [`iox2_publisher_loan()`](crate::iox2_publisher_loan()) +/// +/// Returns a [`iox2_sample_mut_ref_h`] +/// +/// # Safety +/// +/// * The `handle` must be a valid handle. +/// * The `handle` is still valid after the call to this function. +#[no_mangle] +pub unsafe extern "C" fn iox2_cast_sample_mut_ref_h( + handle: iox2_sample_mut_h, +) -> iox2_sample_mut_ref_h { + debug_assert!(!handle.is_null()); + (*handle.as_type()).as_ref_handle() as *mut _ as _ +} + +/// Acquires the samples mutable payload. +/// +/// # Safety +/// +/// * `handle` obtained by [`iox2_publisher_loan()`](crate::iox2_publisher_loan()) +/// * `payload_ptr` a valid, non-null pointer pointing to a [`*const c_void`] pointer. +/// * `payload_len` (optional) either a null poitner or a valid pointer pointing to a [`c_size_t`]. +#[no_mangle] +pub unsafe extern "C" fn iox2_sample_mut_payload_mut( + sample_handle: iox2_sample_mut_ref_h, + payload_ptr: *mut *mut c_void, + payload_len: *mut c_size_t, +) { + debug_assert!(!sample_handle.is_null()); + debug_assert!(!payload_ptr.is_null()); + + let sample = &mut *sample_handle.as_type(); + + let payload = match sample.service_type { + iox2_service_type_e::IPC => sample.value.as_mut().ipc.payload_mut(), + iox2_service_type_e::LOCAL => sample.value.as_mut().local.payload_mut(), + }; + + *payload_ptr = payload.as_mut_ptr().cast(); + if !payload_len.is_null() { + *payload_len = payload.len(); + } +} + +/// Acquires the samples payload. +/// +/// # Safety +/// +/// * `handle` obtained by [`iox2_publisher_loan()`](crate::iox2_publisher_loan()) +/// * `payload_ptr` a valid, non-null pointer pointing to a [`*const c_void`] pointer. +/// * `payload_len` (optional) either a null poitner or a valid pointer pointing to a [`c_size_t`]. +#[no_mangle] +pub unsafe extern "C" fn iox2_sample_mut_payload( + sample_handle: iox2_sample_mut_ref_h, + payload_ptr: *mut *const c_void, + payload_len: *mut c_size_t, +) { + debug_assert!(!sample_handle.is_null()); + debug_assert!(!payload_ptr.is_null()); + + let sample = &mut *sample_handle.as_type(); + + let payload = match sample.service_type { + iox2_service_type_e::IPC => sample.value.as_mut().ipc.payload(), + iox2_service_type_e::LOCAL => sample.value.as_mut().local.payload(), + }; + + *payload_ptr = payload.as_ptr().cast(); + if !payload_len.is_null() { + *payload_len = payload.len(); + } +} + +/// Takes the ownership of the sample and sends it +/// +/// # Safety +/// +/// * `handle` obtained by [`iox2_publisher_loan()`](crate::iox2_publisher_loan()) +/// * `number_of_recipients`, can be null or must point to a valid [`c_size_t`] to store the number +/// of subscribers that received the sample +#[no_mangle] +pub unsafe extern "C" fn iox2_sample_mut_send( + sample_handle: iox2_sample_mut_h, + number_of_recipients: *mut c_size_t, +) -> c_int { + debug_assert!(!sample_handle.is_null()); + + let sample_struct = &mut *sample_handle.as_type(); + let service_type = sample_struct.service_type; + + let sample = sample_struct + .value + .as_option_mut() + .take() + .unwrap_or_else(|| panic!("Trying to send an already sent sample!")); + (sample_struct.deleter)(sample_struct); + + match service_type { + iox2_service_type_e::IPC => { + let sample = ManuallyDrop::into_inner(sample.ipc); + match sample.assume_init().send() { + Ok(v) => { + if !number_of_recipients.is_null() { + *number_of_recipients = v; + } + } + Err(e) => { + return e.into_c_int(); + } + } + } + iox2_service_type_e::LOCAL => { + let sample = ManuallyDrop::into_inner(sample.local); + match sample.assume_init().send() { + Ok(v) => { + if !number_of_recipients.is_null() { + *number_of_recipients = v; + } + } + Err(e) => { + return e.into_c_int(); + } + } + } + } + + IOX2_OK +} + +/// This function needs to be called to destroy the sample! +/// +/// # Arguments +/// +/// * `sample_handle` - A valid [`iox2_sample_mut_h`] +/// +/// # Safety +/// +/// * The `sample_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call! +/// * The corresponding [`iox2_sample_mut_t`] can be re-used with a call to +/// [`iox2_subscriber_receive`](crate::iox2_subscriber_receive)! +#[no_mangle] +pub unsafe extern "C" fn iox2_sample_mut_drop(sample_handle: iox2_sample_mut_h) { + debug_assert!(!sample_handle.is_null()); + + let sample = &mut *sample_handle.as_type(); + + match sample.service_type { + iox2_service_type_e::IPC => { + ManuallyDrop::drop(&mut sample.value.as_mut().ipc); + } + iox2_service_type_e::LOCAL => { + ManuallyDrop::drop(&mut sample.value.as_mut().local); + } + } + (sample.deleter)(sample); +} + +// END C API diff --git a/iceoryx2-ffi/ffi/src/api/service_builder.rs b/iceoryx2-ffi/ffi/src/api/service_builder.rs index 63a1a1f1f..bdb457e1d 100644 --- a/iceoryx2-ffi/ffi/src/api/service_builder.rs +++ b/iceoryx2-ffi/ffi/src/api/service_builder.rs @@ -23,12 +23,14 @@ use iceoryx2_bb_elementary::static_assert::*; use iceoryx2_ffi_macros::iceoryx2_ffi; use core::mem::ManuallyDrop; +use core::mem::MaybeUninit; // BEGIN types definition pub(super) type NoUserHeaderFfi = (); pub(super) type _UserHeaderFfi = [u8; 128]; pub(super) type PayloadFfi = [u8]; +pub(super) type UninitPayloadFfi = [MaybeUninit]; pub(super) union ServiceBuilderUnionNested { pub(super) base: ManuallyDrop>, diff --git a/iceoryx2-ffi/ffi/src/api/service_builder_event.rs b/iceoryx2-ffi/ffi/src/api/service_builder_event.rs index 26081d16b..120744779 100644 --- a/iceoryx2-ffi/ffi/src/api/service_builder_event.rs +++ b/iceoryx2-ffi/ffi/src/api/service_builder_event.rs @@ -18,7 +18,11 @@ use crate::api::{ PortFactoryEventUnion, ServiceBuilderUnion, IOX2_OK, }; -use iceoryx2::service::builder::event::{EventCreateError, EventOpenError, EventOpenOrCreateError}; +use iceoryx2::prelude::*; +use iceoryx2::service::builder::event::{ + Builder, EventCreateError, EventOpenError, EventOpenOrCreateError, +}; +use iceoryx2::service::port_factory::event::PortFactory; use core::ffi::c_int; use core::mem::ManuallyDrop; @@ -150,24 +154,24 @@ pub unsafe extern "C" fn iox2_service_builder_event_set_max_notifiers( ) { debug_assert!(!service_builder_handle.is_null()); - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; - match service_builders_struct.service_type { + match service_builder_struct.service_type { iox2_service_type_e::IPC => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().ipc); let service_builder = ManuallyDrop::into_inner(service_builder.event); - service_builders_struct.set(ServiceBuilderUnion::new_ipc_event( + service_builder_struct.set(ServiceBuilderUnion::new_ipc_event( service_builder.max_notifiers(value), )); } iox2_service_type_e::LOCAL => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().local); let service_builder = ManuallyDrop::into_inner(service_builder.event); - service_builders_struct.set(ServiceBuilderUnion::new_local_event( + service_builder_struct.set(ServiceBuilderUnion::new_local_event( service_builder.max_notifiers(value), )); } @@ -193,24 +197,24 @@ pub unsafe extern "C" fn iox2_service_builder_event_set_max_listeners( ) { debug_assert!(!service_builder_handle.is_null()); - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; - match service_builders_struct.service_type { + match service_builder_struct.service_type { iox2_service_type_e::IPC => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().ipc); let service_builder = ManuallyDrop::into_inner(service_builder.event); - service_builders_struct.set(ServiceBuilderUnion::new_ipc_event( + service_builder_struct.set(ServiceBuilderUnion::new_ipc_event( service_builder.max_listeners(value), )); } iox2_service_type_e::LOCAL => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().local); let service_builder = ManuallyDrop::into_inner(service_builder.event); - service_builders_struct.set(ServiceBuilderUnion::new_local_event( + service_builder_struct.set(ServiceBuilderUnion::new_local_event( service_builder.max_listeners(value), )); } @@ -242,65 +246,13 @@ pub unsafe extern "C" fn iox2_service_builder_event_open_or_create( port_factory_struct_ptr: *mut iox2_port_factory_event_t, port_factory_handle_ptr: *mut iox2_port_factory_event_h, ) -> c_int { - debug_assert!(!service_builder_handle.is_null()); - debug_assert!(!port_factory_handle_ptr.is_null()); - - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; - - let mut port_factory_struct_ptr = port_factory_struct_ptr; - fn no_op(_: *mut iox2_port_factory_event_t) {} - let mut deleter: fn(*mut iox2_port_factory_event_t) = no_op; - if port_factory_struct_ptr.is_null() { - port_factory_struct_ptr = iox2_port_factory_event_t::alloc(); - deleter = iox2_port_factory_event_t::dealloc; - } - debug_assert!(!port_factory_struct_ptr.is_null()); - - let service_type = service_builders_struct.service_type; - match service_type { - iox2_service_type_e::IPC => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); - - let service_builder = ManuallyDrop::into_inner(service_builder.event); - - match service_builder.open_or_create() { - Ok(port_factory) => { - (*port_factory_struct_ptr).init( - service_type, - PortFactoryEventUnion::new_ipc(port_factory), - deleter, - ); - } - Err(error) => { - return error.into_c_int(); - } - } - } - iox2_service_type_e::LOCAL => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); - - let service_builder = ManuallyDrop::into_inner(service_builder.event); - - match service_builder.open_or_create() { - Ok(port_factory) => { - (*port_factory_struct_ptr).init( - service_type, - PortFactoryEventUnion::new_local(port_factory), - deleter, - ); - } - Err(error) => { - return error.into_c_int(); - } - } - } - } - - *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); - - IOX2_OK + iox2_service_builder_event_open_create_impl( + service_builder_handle, + port_factory_struct_ptr, + port_factory_handle_ptr, + |service_builder| service_builder.open_or_create(), + |service_builder| service_builder.open_or_create(), + ) } /// Opens an event service and returns a port factory to create notifiers and listeners. @@ -326,65 +278,13 @@ pub unsafe extern "C" fn iox2_service_builder_event_open( port_factory_struct_ptr: *mut iox2_port_factory_event_t, port_factory_handle_ptr: *mut iox2_port_factory_event_h, ) -> c_int { - debug_assert!(!service_builder_handle.is_null()); - debug_assert!(!port_factory_handle_ptr.is_null()); - - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; - - let mut port_factory_struct_ptr = port_factory_struct_ptr; - fn no_op(_: *mut iox2_port_factory_event_t) {} - let mut deleter: fn(*mut iox2_port_factory_event_t) = no_op; - if port_factory_struct_ptr.is_null() { - port_factory_struct_ptr = iox2_port_factory_event_t::alloc(); - deleter = iox2_port_factory_event_t::dealloc; - } - debug_assert!(!port_factory_struct_ptr.is_null()); - - let service_type = service_builders_struct.service_type; - match service_type { - iox2_service_type_e::IPC => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); - - let service_builder = ManuallyDrop::into_inner(service_builder.event); - - match service_builder.open() { - Ok(port_factory) => { - (*port_factory_struct_ptr).init( - service_type, - PortFactoryEventUnion::new_ipc(port_factory), - deleter, - ); - } - Err(error) => { - return error.into_c_int(); - } - } - } - iox2_service_type_e::LOCAL => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); - - let service_builder = ManuallyDrop::into_inner(service_builder.event); - - match service_builder.open() { - Ok(port_factory) => { - (*port_factory_struct_ptr).init( - service_type, - PortFactoryEventUnion::new_local(port_factory), - deleter, - ); - } - Err(error) => { - return error.into_c_int(); - } - } - } - } - - *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); - - IOX2_OK + iox2_service_builder_event_open_create_impl( + service_builder_handle, + port_factory_struct_ptr, + port_factory_handle_ptr, + |service_builder| service_builder.open(), + |service_builder| service_builder.open(), + ) } /// Creates an event service and returns a port factory to create notifiers and listeners. @@ -409,36 +309,67 @@ pub unsafe extern "C" fn iox2_service_builder_event_create( service_builder_handle: iox2_service_builder_event_h, port_factory_struct_ptr: *mut iox2_port_factory_event_t, port_factory_handle_ptr: *mut iox2_port_factory_event_h, +) -> c_int { + iox2_service_builder_event_open_create_impl( + service_builder_handle, + port_factory_struct_ptr, + port_factory_handle_ptr, + |service_builder| service_builder.create(), + |service_builder| service_builder.create(), + ) +} + +unsafe fn iox2_service_builder_event_open_create_impl( + service_builder_handle: iox2_service_builder_event_h, + port_factory_struct_ptr: *mut iox2_port_factory_event_t, + port_factory_handle_ptr: *mut iox2_port_factory_event_h, + func_ipc: impl FnOnce(Builder) -> Result, E>, + func_local: impl FnOnce( + Builder, + ) -> Result, E>, ) -> c_int { debug_assert!(!service_builder_handle.is_null()); debug_assert!(!port_factory_handle_ptr.is_null()); - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; - - let mut port_factory_struct_ptr = port_factory_struct_ptr; - fn no_op(_: *mut iox2_port_factory_event_t) {} - let mut deleter: fn(*mut iox2_port_factory_event_t) = no_op; - if port_factory_struct_ptr.is_null() { - port_factory_struct_ptr = iox2_port_factory_event_t::alloc(); - deleter = iox2_port_factory_event_t::dealloc; - } - debug_assert!(!port_factory_struct_ptr.is_null()); + let init_port_factory_struct_ptr = |port_factory_struct_ptr: *mut iox2_port_factory_event_t| { + let mut port_factory_struct_ptr = port_factory_struct_ptr; + fn no_op(_: *mut iox2_port_factory_event_t) {} + let mut deleter: fn(*mut iox2_port_factory_event_t) = no_op; + if port_factory_struct_ptr.is_null() { + port_factory_struct_ptr = iox2_port_factory_event_t::alloc(); + deleter = iox2_port_factory_event_t::dealloc; + } + debug_assert!(!port_factory_struct_ptr.is_null()); + + (port_factory_struct_ptr, deleter) + }; + + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; + let service_type = service_builder_struct.service_type; + let service_builder = service_builder_struct + .value + .as_option_mut() + .take() + .unwrap_or_else(|| { + panic!("Trying to use an invalid 'iox2_service_builder_event_h'!"); + }); + (service_builder_struct.deleter)(service_builder_struct); - let service_type = service_builders_struct.service_type; match service_type { iox2_service_type_e::IPC => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); - + let service_builder = ManuallyDrop::into_inner(service_builder.ipc); let service_builder = ManuallyDrop::into_inner(service_builder.event); - match service_builder.create() { + match func_ipc(service_builder) { Ok(port_factory) => { + let (port_factory_struct_ptr, deleter) = + init_port_factory_struct_ptr(port_factory_struct_ptr); (*port_factory_struct_ptr).init( service_type, PortFactoryEventUnion::new_ipc(port_factory), deleter, ); + *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); } Err(error) => { return error.into_c_int(); @@ -446,18 +377,19 @@ pub unsafe extern "C" fn iox2_service_builder_event_create( } } iox2_service_type_e::LOCAL => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); - + let service_builder = ManuallyDrop::into_inner(service_builder.local); let service_builder = ManuallyDrop::into_inner(service_builder.event); - match service_builder.create() { + match func_local(service_builder) { Ok(port_factory) => { + let (port_factory_struct_ptr, deleter) = + init_port_factory_struct_ptr(port_factory_struct_ptr); (*port_factory_struct_ptr).init( service_type, PortFactoryEventUnion::new_local(port_factory), deleter, ); + *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); } Err(error) => { return error.into_c_int(); @@ -466,8 +398,6 @@ pub unsafe extern "C" fn iox2_service_builder_event_create( } } - *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); - IOX2_OK } diff --git a/iceoryx2-ffi/ffi/src/api/service_builder_pub_sub.rs b/iceoryx2-ffi/ffi/src/api/service_builder_pub_sub.rs index becb2c134..4a39fc37b 100644 --- a/iceoryx2-ffi/ffi/src/api/service_builder_pub_sub.rs +++ b/iceoryx2-ffi/ffi/src/api/service_builder_pub_sub.rs @@ -15,12 +15,16 @@ use crate::api::{ c_size_t, iox2_port_factory_pub_sub_h, iox2_port_factory_pub_sub_t, iox2_service_builder_pub_sub_h, iox2_service_builder_pub_sub_ref_h, iox2_service_type_e, - HandleToType, IntoCInt, PortFactoryPubSubUnion, ServiceBuilderUnion, IOX2_OK, + HandleToType, IntoCInt, NoUserHeaderFfi, PayloadFfi, PortFactoryPubSubUnion, + ServiceBuilderUnion, IOX2_OK, }; +use iceoryx2::prelude::*; use iceoryx2::service::builder::publish_subscribe::{ - PublishSubscribeCreateError, PublishSubscribeOpenError, PublishSubscribeOpenOrCreateError, + Builder, PublishSubscribeCreateError, PublishSubscribeOpenError, + PublishSubscribeOpenOrCreateError, }; +use iceoryx2::service::port_factory::publish_subscribe::PortFactory; use iceoryx2::service::static_config::message_type_details::{TypeDetail, TypeVariant}; use iceoryx2_bb_log::fatal_panic; @@ -163,6 +167,15 @@ pub enum iox2_type_variant_e { DYNAMIC, } +impl From<&TypeVariant> for iox2_type_variant_e { + fn from(value: &TypeVariant) -> Self { + match value { + TypeVariant::Dynamic => iox2_type_variant_e::DYNAMIC, + TypeVariant::FixedSize => iox2_type_variant_e::FIXED_SIZE, + } + } +} + #[repr(C)] #[derive(Copy, Clone)] pub enum iox2_type_detail_error_e { @@ -238,24 +251,24 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_set_payload_type_details( alignment, }; - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; - match service_builders_struct.service_type { + match service_builder_struct.service_type { iox2_service_type_e::IPC => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().ipc); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - service_builders_struct.set(ServiceBuilderUnion::new_ipc_pub_sub( + service_builder_struct.set(ServiceBuilderUnion::new_ipc_pub_sub( service_builder.__internal_set_payload_type_details(value), )); } iox2_service_type_e::LOCAL => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().local); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - service_builders_struct.set(ServiceBuilderUnion::new_local_pub_sub( + service_builder_struct.set(ServiceBuilderUnion::new_local_pub_sub( service_builder.__internal_set_payload_type_details(value), )); } @@ -283,24 +296,24 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_set_max_publishers( ) { debug_assert!(!service_builder_handle.is_null()); - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; - match service_builders_struct.service_type { + match service_builder_struct.service_type { iox2_service_type_e::IPC => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().ipc); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - service_builders_struct.set(ServiceBuilderUnion::new_ipc_pub_sub( + service_builder_struct.set(ServiceBuilderUnion::new_ipc_pub_sub( service_builder.max_publishers(value), )); } iox2_service_type_e::LOCAL => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().local); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - service_builders_struct.set(ServiceBuilderUnion::new_local_pub_sub( + service_builder_struct.set(ServiceBuilderUnion::new_local_pub_sub( service_builder.max_publishers(value), )); } @@ -326,24 +339,24 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_set_max_subscribers( ) { debug_assert!(!service_builder_handle.is_null()); - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; - match service_builders_struct.service_type { + match service_builder_struct.service_type { iox2_service_type_e::IPC => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().ipc); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - service_builders_struct.set(ServiceBuilderUnion::new_ipc_pub_sub( + service_builder_struct.set(ServiceBuilderUnion::new_ipc_pub_sub( service_builder.max_subscribers(value), )); } iox2_service_type_e::LOCAL => { let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); + ManuallyDrop::take(&mut service_builder_struct.value.as_mut().local); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - service_builders_struct.set(ServiceBuilderUnion::new_local_pub_sub( + service_builder_struct.set(ServiceBuilderUnion::new_local_pub_sub( service_builder.max_subscribers(value), )); } @@ -375,65 +388,13 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_open_or_create( port_factory_struct_ptr: *mut iox2_port_factory_pub_sub_t, port_factory_handle_ptr: *mut iox2_port_factory_pub_sub_h, ) -> c_int { - debug_assert!(!service_builder_handle.is_null()); - debug_assert!(!port_factory_handle_ptr.is_null()); - - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; - - let mut port_factory_struct_ptr = port_factory_struct_ptr; - fn no_op(_: *mut iox2_port_factory_pub_sub_t) {} - let mut deleter: fn(*mut iox2_port_factory_pub_sub_t) = no_op; - if port_factory_struct_ptr.is_null() { - port_factory_struct_ptr = iox2_port_factory_pub_sub_t::alloc(); - deleter = iox2_port_factory_pub_sub_t::dealloc; - } - debug_assert!(!port_factory_struct_ptr.is_null()); - - let service_type = service_builders_struct.service_type; - match service_type { - iox2_service_type_e::IPC => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); - - let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - - match service_builder.open_or_create() { - Ok(port_factory) => { - (*port_factory_struct_ptr).init( - service_type, - PortFactoryPubSubUnion::new_ipc(port_factory), - deleter, - ); - } - Err(error) => { - return error.into_c_int(); - } - } - } - iox2_service_type_e::LOCAL => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); - - let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - - match service_builder.open_or_create() { - Ok(port_factory) => { - (*port_factory_struct_ptr).init( - service_type, - PortFactoryPubSubUnion::new_local(port_factory), - deleter, - ); - } - Err(error) => { - return error.into_c_int(); - } - } - } - } - - *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); - - IOX2_OK + iox2_service_builder_pub_sub_open_create_impl( + service_builder_handle, + port_factory_struct_ptr, + port_factory_handle_ptr, + |service_builder| service_builder.open_or_create(), + |service_builder| service_builder.open_or_create(), + ) } /// Opens a publish-subscribe service and returns a port factory to create publishers and subscribers. @@ -459,65 +420,13 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_open( port_factory_struct_ptr: *mut iox2_port_factory_pub_sub_t, port_factory_handle_ptr: *mut iox2_port_factory_pub_sub_h, ) -> c_int { - debug_assert!(!service_builder_handle.is_null()); - debug_assert!(!port_factory_handle_ptr.is_null()); - - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; - - let mut port_factory_struct_ptr = port_factory_struct_ptr; - fn no_op(_: *mut iox2_port_factory_pub_sub_t) {} - let mut deleter: fn(*mut iox2_port_factory_pub_sub_t) = no_op; - if port_factory_struct_ptr.is_null() { - port_factory_struct_ptr = iox2_port_factory_pub_sub_t::alloc(); - deleter = iox2_port_factory_pub_sub_t::dealloc; - } - debug_assert!(!port_factory_struct_ptr.is_null()); - - let service_type = service_builders_struct.service_type; - match service_type { - iox2_service_type_e::IPC => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); - - let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - - match service_builder.open() { - Ok(port_factory) => { - (*port_factory_struct_ptr).init( - service_type, - PortFactoryPubSubUnion::new_ipc(port_factory), - deleter, - ); - } - Err(error) => { - return error.into_c_int(); - } - } - } - iox2_service_type_e::LOCAL => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); - - let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - - match service_builder.open() { - Ok(port_factory) => { - (*port_factory_struct_ptr).init( - service_type, - PortFactoryPubSubUnion::new_local(port_factory), - deleter, - ); - } - Err(error) => { - return error.into_c_int(); - } - } - } - } - - *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); - - IOX2_OK + iox2_service_builder_pub_sub_open_create_impl( + service_builder_handle, + port_factory_struct_ptr, + port_factory_handle_ptr, + |service_builder| service_builder.open(), + |service_builder| service_builder.open(), + ) } /// Creates a publish-subscribe service and returns a port factory to create publishers and subscribers. @@ -542,36 +451,74 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_create( service_builder_handle: iox2_service_builder_pub_sub_h, port_factory_struct_ptr: *mut iox2_port_factory_pub_sub_t, port_factory_handle_ptr: *mut iox2_port_factory_pub_sub_h, +) -> c_int { + iox2_service_builder_pub_sub_open_create_impl( + service_builder_handle, + port_factory_struct_ptr, + port_factory_handle_ptr, + |service_builder| service_builder.create(), + |service_builder| service_builder.create(), + ) +} + +unsafe fn iox2_service_builder_pub_sub_open_create_impl( + service_builder_handle: iox2_service_builder_pub_sub_h, + port_factory_struct_ptr: *mut iox2_port_factory_pub_sub_t, + port_factory_handle_ptr: *mut iox2_port_factory_pub_sub_h, + func_ipc: impl FnOnce( + Builder, + ) + -> Result, E>, + func_local: impl FnOnce( + Builder, + ) -> Result< + PortFactory, + E, + >, ) -> c_int { debug_assert!(!service_builder_handle.is_null()); debug_assert!(!port_factory_handle_ptr.is_null()); - let service_builders_struct = unsafe { &mut *service_builder_handle.as_type() }; - - let mut port_factory_struct_ptr = port_factory_struct_ptr; - fn no_op(_: *mut iox2_port_factory_pub_sub_t) {} - let mut deleter: fn(*mut iox2_port_factory_pub_sub_t) = no_op; - if port_factory_struct_ptr.is_null() { - port_factory_struct_ptr = iox2_port_factory_pub_sub_t::alloc(); - deleter = iox2_port_factory_pub_sub_t::dealloc; - } - debug_assert!(!port_factory_struct_ptr.is_null()); + let init_port_factory_struct_ptr = + |port_factory_struct_ptr: *mut iox2_port_factory_pub_sub_t| { + let mut port_factory_struct_ptr = port_factory_struct_ptr; + fn no_op(_: *mut iox2_port_factory_pub_sub_t) {} + let mut deleter: fn(*mut iox2_port_factory_pub_sub_t) = no_op; + if port_factory_struct_ptr.is_null() { + port_factory_struct_ptr = iox2_port_factory_pub_sub_t::alloc(); + deleter = iox2_port_factory_pub_sub_t::dealloc; + } + debug_assert!(!port_factory_struct_ptr.is_null()); + + (port_factory_struct_ptr, deleter) + }; + + let service_builder_struct = unsafe { &mut *service_builder_handle.as_type() }; + let service_type = service_builder_struct.service_type; + let service_builder = service_builder_struct + .value + .as_option_mut() + .take() + .unwrap_or_else(|| { + panic!("Trying to use an invalid 'iox2_service_builder_pub_sub_h'!"); + }); + (service_builder_struct.deleter)(service_builder_struct); - let service_type = service_builders_struct.service_type; match service_type { iox2_service_type_e::IPC => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().ipc); - + let service_builder = ManuallyDrop::into_inner(service_builder.ipc); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - match service_builder.create() { + match func_ipc(service_builder) { Ok(port_factory) => { + let (port_factory_struct_ptr, deleter) = + init_port_factory_struct_ptr(port_factory_struct_ptr); (*port_factory_struct_ptr).init( service_type, PortFactoryPubSubUnion::new_ipc(port_factory), deleter, ); + *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); } Err(error) => { return error.into_c_int(); @@ -579,18 +526,19 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_create( } } iox2_service_type_e::LOCAL => { - let service_builder = - ManuallyDrop::take(&mut service_builders_struct.value.as_mut().local); - + let service_builder = ManuallyDrop::into_inner(service_builder.local); let service_builder = ManuallyDrop::into_inner(service_builder.pub_sub); - match service_builder.create() { + match func_local(service_builder) { Ok(port_factory) => { + let (port_factory_struct_ptr, deleter) = + init_port_factory_struct_ptr(port_factory_struct_ptr); (*port_factory_struct_ptr).init( service_type, PortFactoryPubSubUnion::new_local(port_factory), deleter, ); + *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); } Err(error) => { return error.into_c_int(); @@ -599,8 +547,6 @@ pub unsafe extern "C" fn iox2_service_builder_pub_sub_create( } } - *port_factory_handle_ptr = (*port_factory_struct_ptr).as_handle(); - IOX2_OK } diff --git a/iceoryx2-ffi/ffi/src/api/static_config_publish_subscribe.rs b/iceoryx2-ffi/ffi/src/api/static_config_publish_subscribe.rs new file mode 100644 index 000000000..72fb09b2a --- /dev/null +++ b/iceoryx2-ffi/ffi/src/api/static_config_publish_subscribe.rs @@ -0,0 +1,45 @@ +// 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 + +#![allow(non_camel_case_types)] + +use iceoryx2::service::static_config::publish_subscribe::StaticConfig; + +use crate::iox2_message_type_details_t; + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct iox2_static_config_publish_subscribe_t { + pub max_subscribers: usize, + pub max_publishers: usize, + pub max_nodes: usize, + pub history_size: usize, + pub subscriber_max_buffer_size: usize, + pub subscriber_max_borrowed_samples: usize, + pub enable_safe_overflow: bool, + pub message_type_details: iox2_message_type_details_t, +} + +impl From<&StaticConfig> for iox2_static_config_publish_subscribe_t { + fn from(c: &StaticConfig) -> Self { + Self { + max_subscribers: c.max_subscribers(), + max_publishers: c.max_publishers(), + max_nodes: c.max_nodes(), + history_size: c.history_size(), + subscriber_max_buffer_size: c.subscriber_max_buffer_size(), + subscriber_max_borrowed_samples: c.subscriber_max_borrowed_samples(), + enable_safe_overflow: c.has_safe_overflow(), + message_type_details: c.message_type_details().into(), + } + } +} diff --git a/iceoryx2-ffi/ffi/src/api/subscriber.rs b/iceoryx2-ffi/ffi/src/api/subscriber.rs index 06b38101a..a0b67e6fa 100644 --- a/iceoryx2-ffi/ffi/src/api/subscriber.rs +++ b/iceoryx2-ffi/ffi/src/api/subscriber.rs @@ -10,63 +10,270 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT -use core::time::Duration; +#![allow(non_camel_case_types)] + +use crate::api::{ + c_size_t, iox2_sample_h, iox2_sample_t, iox2_service_type_e, HandleToType, IntoCInt, + NoUserHeaderFfi, PayloadFfi, SampleUnion, IOX2_OK, +}; + +use iceoryx2::port::subscriber::{Subscriber, SubscriberReceiveError}; use iceoryx2::prelude::*; -use iceoryx2_bb_log::set_log_level; +use iceoryx2_bb_elementary::static_assert::*; +use iceoryx2_ffi_macros::iceoryx2_ffi; -const CYCLE_TIME: Duration = Duration::from_secs(1); +use core::ffi::c_int; +use core::mem::ManuallyDrop; -#[no_mangle] -pub extern "C" fn run_subscriber(seconds: u32) -> i32 { - set_log_level(iceoryx2_bb_log::LogLevel::Info); +// BEGIN types definition - let service_name = ServiceName::new("Hello/from/C"); - let node = NodeBuilder::new().create::(); +#[repr(C)] +#[derive(Copy, Clone)] +pub enum iox2_subscriber_receive_error_e { + EXCEEDS_MAX_BORROWED_SAMPLES = IOX2_OK as isize + 1, + CONNECTION_FAILURE, +} - if service_name.is_err() || node.is_err() { - return -1; +impl IntoCInt for SubscriberReceiveError { + fn into_c_int(self) -> c_int { + (match self { + SubscriberReceiveError::ExceedsMaxBorrowedSamples => { + iox2_subscriber_receive_error_e::EXCEEDS_MAX_BORROWED_SAMPLES + } + SubscriberReceiveError::ConnectionFailure(_) => { + iox2_subscriber_receive_error_e::CONNECTION_FAILURE + } + }) as c_int + } +} + +pub(super) union SubscriberUnion { + ipc: ManuallyDrop>, + local: ManuallyDrop>, +} + +impl SubscriberUnion { + pub(super) fn new_ipc( + subscriber: Subscriber, + ) -> Self { + Self { + ipc: ManuallyDrop::new(subscriber), + } + } + pub(super) fn new_local( + subscriber: Subscriber, + ) -> Self { + Self { + local: ManuallyDrop::new(subscriber), + } } +} - let service_name = service_name.unwrap(); - let node = node.unwrap(); +#[repr(C)] +#[repr(align(16))] // alignment of Option +pub struct iox2_subscriber_storage_t { + internal: [u8; 448], // magic number obtained with size_of::>() +} - let service = node - .service_builder(&service_name) - .publish_subscribe::() - .open_or_create(); +#[repr(C)] +#[iceoryx2_ffi(SubscriberUnion)] +pub struct iox2_subscriber_t { + service_type: iox2_service_type_e, + value: iox2_subscriber_storage_t, + deleter: fn(*mut iox2_subscriber_t), +} - if service.is_err() { - return -1; +impl iox2_subscriber_t { + pub(super) fn init( + &mut self, + service_type: iox2_service_type_e, + value: SubscriberUnion, + deleter: fn(*mut iox2_subscriber_t), + ) { + self.service_type = service_type; + self.value.init(value); + self.deleter = deleter; } +} + +pub struct iox2_subscriber_h_t; +/// The owning handle for `iox2_subscriber_t`. Passing the handle to an function transfers the ownership. +pub type iox2_subscriber_h = *mut iox2_subscriber_h_t; - let service = service.unwrap(); +pub struct iox2_subscriber_ref_h_t; +/// The non-owning handle for `iox2_subscriber_t`. Passing the handle to an function does not transfers the ownership. +pub type iox2_subscriber_ref_h = *mut iox2_subscriber_ref_h_t; - let subscriber = service.subscriber_builder().create(); +impl HandleToType for iox2_subscriber_h { + type Target = *mut iox2_subscriber_t; - if subscriber.is_err() { - return -1; + fn as_type(self) -> Self::Target { + self as *mut _ as _ } +} - let subscriber = subscriber.unwrap(); +impl HandleToType for iox2_subscriber_ref_h { + type Target = *mut iox2_subscriber_t; - let mut remaining_seconds = seconds; + fn as_type(self) -> Self::Target { + self as *mut _ as _ + } +} - while let NodeEvent::Tick = node.wait(CYCLE_TIME) { - loop { - match subscriber.receive() { - Ok(Some(sample)) => println!("received: {:?}", *sample), - Ok(None) => break, - Err(_) => return -1, - } - } +// END type definition + +// BEGIN C API + +/// This function casts an owning [`iox2_subscriber_h`] into a non-owning [`iox2_subscriber_ref_h`] +/// +/// # Arguments +/// +/// * `subscriber_handle` obtained by [`iox2_port_factory_subscriber_builder_create`](crate::iox2_port_factory_subscriber_builder_create) +/// +/// Returns a [`iox2_subscriber_ref_h`] +/// +/// # Safety +/// +/// * The `subscriber_handle` must be a valid handle. +/// * The `subscriber_handle` is still valid after the call to this function. +#[no_mangle] +pub unsafe extern "C" fn iox2_cast_subscriber_ref_h( + subscriber_handle: iox2_subscriber_h, +) -> iox2_subscriber_ref_h { + debug_assert!(!subscriber_handle.is_null()); + + (*subscriber_handle.as_type()).as_ref_handle() as *mut _ as _ +} + +/// Returns the buffer size of the subscriber +/// +/// # Arguments +/// +/// * `subscriber_handle` - Must be a valid [`iox2_subscriber_ref_h`] +/// obtained by [`iox2_port_factory_subscriber_builder_create`](crate::iox2_port_factory_subscriber_builder_create) and +/// casted by [`iox2_cast_subscriber_ref_h`]. +/// +/// # Safety +/// +/// * `subscriber_handle` must be valid handles +#[no_mangle] +pub unsafe extern "C" fn iox2_subscriber_buffer_size( + subscriber_handle: iox2_subscriber_ref_h, +) -> c_size_t { + debug_assert!(!subscriber_handle.is_null()); + + let subscriber = &mut *subscriber_handle.as_type(); + + match subscriber.service_type { + iox2_service_type_e::IPC => subscriber.value.as_ref().ipc.buffer_size(), + iox2_service_type_e::LOCAL => subscriber.value.as_ref().local.buffer_size(), + } +} + +// TODO [#210] add all the other setter methods + +/// Takes a sample ouf of the subscriber queue. +/// +/// # Arguments +/// +/// * `subscriber_handle` - Must be a valid [`iox2_subscriber_ref_h`] +/// obtained by [`iox2_port_factory_subscriber_builder_create`](crate::iox2_port_factory_subscriber_builder_create) and +/// casted by [`iox2_cast_subscriber_ref_h`]. +/// * `sample_struct_ptr` - Must be either a NULL pointer or a pointer to a valid [`iox2_sample_t`]. +/// If it is a NULL pointer, the storage will be allocated on the heap. +/// * `sample_handle_ptr` - An uninitialized or dangling [`iox2_sample_h`] handle which will be initialized by this function call if a sample is obtained, otherwise it will be set to NULL. +/// +/// Returns IOX2_OK on success, an [`iox2_subscriber_receive_error_e`] otherwise. +/// Attention, an empty subscriber queue is not an error and even with IOX2_OK it is possible to get a NULL in `sample_handle_ptr`. +/// +/// # Safety +/// +/// * The `subscriber_handle` is still valid after the return of this function and can be use in another function call. +/// * The `sample_handle_ptr` is pointing to a valid [`iox2_sample_h`]. +#[no_mangle] +pub unsafe extern "C" fn iox2_subscriber_receive( + subscriber_handle: iox2_subscriber_ref_h, + sample_struct_ptr: *mut iox2_sample_t, + sample_handle_ptr: *mut iox2_sample_h, +) -> c_int { + debug_assert!(!subscriber_handle.is_null()); + debug_assert!(!sample_handle_ptr.is_null()); + + *sample_handle_ptr = std::ptr::null_mut(); - remaining_seconds = remaining_seconds.saturating_sub(1); - if remaining_seconds == 0 { - break; + let init_sample_struct_ptr = |sample_struct_ptr: *mut iox2_sample_t| { + let mut sample_struct_ptr = sample_struct_ptr; + fn no_op(_: *mut iox2_sample_t) {} + let mut deleter: fn(*mut iox2_sample_t) = no_op; + if sample_struct_ptr.is_null() { + sample_struct_ptr = iox2_sample_t::alloc(); + deleter = iox2_sample_t::dealloc; } + debug_assert!(!sample_struct_ptr.is_null()); + + (sample_struct_ptr, deleter) + }; + + let subscriber = &mut *subscriber_handle.as_type(); + + match subscriber.service_type { + iox2_service_type_e::IPC => match subscriber.value.as_ref().ipc.receive() { + Ok(Some(sample)) => { + let (sample_struct_ptr, deleter) = init_sample_struct_ptr(sample_struct_ptr); + (*sample_struct_ptr).init( + subscriber.service_type, + SampleUnion::new_ipc(sample), + deleter, + ); + *sample_handle_ptr = (*sample_struct_ptr).as_handle(); + } + Ok(None) => (), + Err(error) => return error.into_c_int(), + }, + iox2_service_type_e::LOCAL => match subscriber.value.as_ref().local.receive() { + Ok(Some(sample)) => { + let (sample_struct_ptr, deleter) = init_sample_struct_ptr(sample_struct_ptr); + (*sample_struct_ptr).init( + subscriber.service_type, + SampleUnion::new_local(sample), + deleter, + ); + *sample_handle_ptr = (*sample_struct_ptr).as_handle(); + } + Ok(None) => (), + Err(error) => return error.into_c_int(), + }, } - println!("exit"); + IOX2_OK +} - 0 +/// This function needs to be called to destroy the subscriber! +/// +/// # Arguments +/// +/// * `subscriber_handle` - A valid [`iox2_subscriber_h`] +/// +/// # Safety +/// +/// * The `subscriber_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call! +/// * The corresponding [`iox2_subscriber_t`] can be re-used with a call to +/// [`iox2_port_factory_subscriber_builder_create`](crate::iox2_port_factory_subscriber_builder_create)! +#[no_mangle] +pub unsafe extern "C" fn iox2_subscriber_drop(subscriber_handle: iox2_subscriber_h) { + debug_assert!(!subscriber_handle.is_null()); + + let subscriber = &mut *subscriber_handle.as_type(); + + match subscriber.service_type { + iox2_service_type_e::IPC => { + ManuallyDrop::drop(&mut subscriber.value.as_mut().ipc); + } + iox2_service_type_e::LOCAL => { + ManuallyDrop::drop(&mut subscriber.value.as_mut().local); + } + } + (subscriber.deleter)(subscriber); } + +// END C API diff --git a/iceoryx2/src/port/publisher.rs b/iceoryx2/src/port/publisher.rs index b09788338..e2e1684d0 100644 --- a/iceoryx2/src/port/publisher.rs +++ b/iceoryx2/src/port/publisher.rs @@ -534,6 +534,7 @@ impl DataSegment { pub struct Publisher { pub(crate) data_segment: Arc>, dynamic_publisher_handle: Option, + payload_size: usize, _payload: PhantomData, _user_header: PhantomData, } @@ -619,9 +620,17 @@ impl loan_counter: IoxAtomicUsize::new(0), }); + let payload_size = data_segment + .subscriber_connections + .static_config + .message_type_details + .payload + .size; + let mut new_self = Self { data_segment, dynamic_publisher_handle: None, + payload_size, _payload: PhantomData, _user_header: PhantomData, }; @@ -970,7 +979,8 @@ impl slice_len, max_slice_len); } - let chunk = self.allocate(self.sample_layout(slice_len))?; + let sample_layout = self.sample_layout(slice_len); + let chunk = self.allocate(sample_layout)?; let header_ptr = chunk.data_ptr as *mut Header; let user_header_ptr = self.user_header_ptr(header_ptr) as *mut UserHeader; let payload_ptr = self.payload_ptr(header_ptr) as *mut MaybeUninit; @@ -982,11 +992,17 @@ impl )) }; + let slice_len_adjusted_to_payload_type_details = + self.payload_size * slice_len / core::mem::size_of::(); + let sample = unsafe { RawSampleMut::new_unchecked( header_ptr, user_header_ptr, - core::slice::from_raw_parts_mut(payload_ptr, slice_len), + core::slice::from_raw_parts_mut( + payload_ptr, + slice_len_adjusted_to_payload_type_details, + ), ) }; diff --git a/iceoryx2/tests/publisher_tests.rs b/iceoryx2/tests/publisher_tests.rs index df60cf499..8203bd448 100644 --- a/iceoryx2/tests/publisher_tests.rs +++ b/iceoryx2/tests/publisher_tests.rs @@ -19,6 +19,7 @@ mod publisher { use iceoryx2::port::publisher::{PublisherCreateError, PublisherLoanError}; use iceoryx2::prelude::*; use iceoryx2::service::port_factory::publisher::UnableToDeliverStrategy; + use iceoryx2::service::static_config::message_type_details::{TypeDetail, TypeVariant}; use iceoryx2::service::{service_name::ServiceName, Service}; use iceoryx2_bb_posix::barrier::*; use iceoryx2_bb_posix::unique_system_id::UniqueSystemId; @@ -377,6 +378,31 @@ mod publisher { } } + #[test] + fn publisher_with_overridden_payload_details_adjusts_slice_len() -> TestResult<()> + { + const TYPE_SIZE_OVERRIDE: usize = 128; + let service_name = generate_name()?; + let node = NodeBuilder::new().create::().unwrap(); + let mut type_detail = TypeDetail::__internal_new::(TypeVariant::FixedSize); + type_detail.size = TYPE_SIZE_OVERRIDE; + + let service = unsafe { + node.service_builder(&service_name) + .publish_subscribe::<[u8]>() + .__internal_set_payload_type_details(type_detail) + .create()? + }; + + let sut = service.publisher_builder().create()?; + + let sample = sut.loan_slice(1)?; + + assert_that!(sample.payload(), len TYPE_SIZE_OVERRIDE); + + Ok(()) + } + #[instantiate_tests()] mod zero_copy {}