diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 7ce445a2b4..7d840b286a 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -18,7 +18,7 @@ For a calendar event, you can subscribe to the [ROS calendar](https://calendar.g ### Topics * Propose features - * E.g. present and discuss a design document + * E.g. present and discuss design documents * Discuss pull requests without going into details * E.g. a small demo * Steer direction of Eclipse iceoryx diff --git a/PLANNED_FEATURES.md b/PLANNED_FEATURES.md index abd03fb357..7c5731f3f0 100644 --- a/PLANNED_FEATURES.md +++ b/PLANNED_FEATURES.md @@ -1,10 +1,24 @@ # Planned features -This list contains rather high-prio features that will most likely be implemented in the near future. +If you have ideas or wishes and don't find them on this list, +feel free to start the discussion by raising a feature request. -* Windows support +## iceoryx v3.0 and later + +This is an incomplete list of possible features for later versions of +iceoryx. + +* Windows command line support +* Dynamic types +* Sending dynamic types like vector, string, list +* iceoryx tools to debug connections and communication content +* User defined platform support +* User defined memory provider to support hardware accelerators * Man page for RouDi and the runtime -* Request-response communication +* Packages for linux distributions like debian, archlinux, gentoo -If you have ideas or wishes and don't find them on this list, -feel free to start the discussion by raising a feature request. +## iceoryx v2.0 + +* Windows support +* Request-response communication +* Service Discovery diff --git a/QUALITY_DECLARATION.md b/QUALITY_DECLARATION.md index 141726d85f..7419975696 100644 --- a/QUALITY_DECLARATION.md +++ b/QUALITY_DECLARATION.md @@ -23,7 +23,7 @@ On Git, the tags have a `v` prefix before the version numbers. A [release script Since release `1.0.0` iceoryx is at a stable version, i.e. `>= 1.0.0`. The latest valid release can be found on the [release page](https://github.com/eclipse-iceoryx/iceoryx/releases) of iceoryx. -The change history can be found in the [release notes section](https://iceoryx.io/latest/release-notes). +The change history can be found in the [release notes section](https://iceoryx.io/v2.0.0/release-notes). ### Public API Declaration [1.iii] @@ -89,8 +89,8 @@ It is required to create/modify the Doxygen/design and user documentation within ### Feature Documentation [3.i] -The documentation of the main iceoryx features (sending, receiving data) can be found in the [overview](https://iceoryx.io/latest/getting-started/overview/) and [iceoryx examples](https://iceoryx.io/latest/getting-started/examples/) including a user-friendly description on how to use the iceoryx API. -The [configuration guide](https://iceoryx.io/latest/advanced/configuration-guide/) completes the documentation on how to use iceoryx. +The documentation of the main iceoryx features (sending, receiving data) can be found in the [overview](https://iceoryx.io/v2.0.0/getting-started/overview/) and [iceoryx examples](https://iceoryx.io/v2.0.0/getting-started/examples/) including a user-friendly description on how to use the iceoryx API. +The [configuration guide](https://iceoryx.io/v2.0.0/advanced/configuration-guide/) completes the documentation on how to use iceoryx. Detailed technical documentation about iceoryx features can be found in the [design document](https://github.com/eclipse-iceoryx/iceoryx/tree/master/doc/design) section with descriptions and diagrams about internal mechanisms of iceoryx. @@ -131,7 +131,7 @@ There is continuous effort to cover the corner cases in the usage of iceoryx in ### Public API Testing [4.ii] All tests are executed for every major feature. New features must provide unit and integration tests that cover the code changes in the Pull-Request. The tests reside in separated folders for every package following a defined structure and naming convention. -The features are tested at module(unit) -integration and system test level. The [guidelines](https://iceoryx.io/latest/advanced/best-practice-for-testing/) for Contributors ensure a high quality of test development. +The features are tested at module(unit) -integration and system test level. The [guidelines](https://iceoryx.io/v2.0.0/advanced/best-practice-for-testing/) for Contributors ensure a high quality of test development. ### Coverage [4.iii] diff --git a/README.md b/README.md index e54057478a..cf9b12fe35 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,13 @@ ## Introduction -Great that you've made it to this little Eclipse project! Let's get you started by providing a quick background +Great that you've made it to this neat Eclipse project! Let's get you started by providing a quick background tour, introducing the project scope and all you need for installation and a first running example. So first off: What is iceoryx? -iceoryx is an inter-process-communication (IPC) middleware for various operating systems (currently we support Linux, MacOS, QNX and Windows 10). +iceoryx is an inter-process-communication (IPC) middleware for various operating systems (currently we support Linux, +macOS, QNX, FreeBSD and Windows 10). It has its origins in the automotive industry, where large amounts of data have to be transferred between different processes when it comes to driver assistance or automated driving systems. However, the efficient communication mechanisms can also be applied to a wider range of use cases, e.g. in the field of robotics or game development. @@ -29,17 +30,16 @@ to a wider range of use cases, e.g. in the field of robotics or game development iceoryx uses a true zero-copy, shared memory approach that allows to transfer data from publishers to subscribers without a single copy. This ensures data transmissions with constant latency, regardless of the size of the payload. For more information have a look at the -[1000 words iceoryx introduction](https://www.eclipse.org/community/eclipse_newsletter/2019/december/4.php) +[1000 words iceoryx introduction](https://www.eclipse.org/community/eclipse_newsletter/2019/december/4.php).

- +

-You're right, middleware is a cluttered term and can somehow be all or nothing, so let's talk about the [goals and non-goals](doc/goals-non-goals.md) of iceoryx. +You're right, middleware is a cluttered term and can somehow be all or nothing. To get a better impression what +this means for iceoryx, please have a loot at our [goals and non-goals](doc/goals-non-goals.md). -It's all about the API?! - -Don't get too frightened of the API when strolling through the codebase. Think of the untyped C++ and the C API as a +Don't get too frightened of the API when strolling through the examples. Think of the untyped C++ and the C API as a "plumbing" one ("plumbing" as defined in Git, which means low-level). We're not using the "plumbing" APIs ourselves, but instead the typed C++ API. The normal use case is that iceoryx is integrated as high-performance IPC transport layer in a bigger framework with additional API layers. @@ -65,7 +65,7 @@ In general unix platforms should work with iceoryx but we only test FreeBSD on o |---|---| | [ROS 2](https://github.com/ros2/rmw_iceoryx) | Eclipse iceoryx can be used inside the [Robot Operating System](https://www.ros.org/) with [rmw_iceoryx](https://github.com/ros2/rmw_iceoryx.git) | | [eCAL](https://github.com/continental/ecal) | Open-source framework from [Continental AG](https://www.continental.com/) supporting pub/sub and various message protocols | -| [RTA-VRTE](https://www.etas.com/en/products/rta-vrte.php) | [Adaptive AUTOSAR](https://www.autosar.org/standards/adaptive-platform/) platform software framework for vehicle computer from [ETAS GmbH](https://www.etas.com) | +| [RTA-VRTE](https://www.etas.com/en/products/rta-vrte.php) | [AUTOSAR Adaptive Platform](https://www.autosar.org/standards/adaptive-platform/) software framework for vehicle computer from [ETAS GmbH](https://www.etas.com) | | [Cyclone DDS](https://github.com/eclipse-cyclonedds/cyclonedds) | Performant and robust open-source DDS implementation maintained by [ADLINK Technology Inc.](https://www.adlinktech.com/) | | [Apex.Middleware](https://www.apex.ai/apex-middleware) | Safe and certified middleware for autonomous mobility systems from [Apex.AI](https://www.apex.ai/) | | [AVIN AP](https://www.avinsystems.com/products/autosar_ap_solutions/) | AUTOSAR Adaptive Platform Product from AVIN Systems | diff --git a/VERSION b/VERSION index 6979a6c066..227cea2156 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.91.0 +2.0.0 diff --git a/cmake/package/package.cmake b/cmake/package/package.cmake index b7d98d62bc..e6f8bb1432 100644 --- a/cmake/package/package.cmake +++ b/cmake/package/package.cmake @@ -14,7 +14,7 @@ # # SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.16) -set(IOX_VERSION_STRING "1.91.0") +set(IOX_VERSION_STRING "2.0.0") project(iceoryx_package VERSION ${IOX_VERSION_STRING}) @@ -22,7 +22,7 @@ project(iceoryx_package VERSION ${IOX_VERSION_STRING}) set(CPACK_GENERATOR "DEB") set(CPACK_PACKAGE_NAME "iceoryx-${iceoryx_package_VERSION}") set(CPACK_PACKAGE_FILE_NAME "iceoryx_${iceoryx_package_VERSION}_${CMAKE_CXX_COMPILER_ID}-${CMAKE_CXX_COMPILER_VERSION}") -set(CPACK_PACKAGE_CONTACT "michael.poehnl@apex.ai") +set(CPACK_PACKAGE_CONTACT "iceoryx-oss-support@apex.ai") set(CPACK_DEBIAN_PACKAGE_DEPENDS "libacl1-dev,libncurses5-dev") set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "iceoryx inter-process-communication (IPC) middleware") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/eclipse-iceoryx/iceoryx") diff --git a/doc/aspice_swe3_4/CMakeLists.txt b/doc/aspice_swe3_4/CMakeLists.txt index 1169d0a4e2..53bbb01d27 100644 --- a/doc/aspice_swe3_4/CMakeLists.txt +++ b/doc/aspice_swe3_4/CMakeLists.txt @@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.16) -set(IOX_VERSION_STRING "1.91.0") +set(IOX_VERSION_STRING "2.0.0") project(iceoryx_doc VERSION ${IOX_VERSION_STRING}) diff --git a/doc/goals-non-goals.md b/doc/goals-non-goals.md index 5c087b863d..c7dea48cea 100644 --- a/doc/goals-non-goals.md +++ b/doc/goals-non-goals.md @@ -10,17 +10,18 @@ ## Goals * High-performance inter-process-communication for various operating systems +* Safe and flexible API * Service discovery functionality with dynamic connections -* Data agnostic, there are no restrictions on payload data +* Data agnostic without restrictions on payload data * Compatibility with AUTOSAR Adaptive and ROS 2 communication patterns -* Providing the building blocks for being able to build gateways to network protocols +* Providing the building blocks for being able to build gateways to network protocols * Automotive-grade SW quality * Modern C++ ## Non-Goals * Providing a data model and things like IDL or code generators -* Shrinking it down for being able to run on µControllers (e.g. with < 1MB of memory) +* Shrinking it down for being able to run on µ-Controllers (e.g. with < 1MB of memory) * Full compliance with the DDS standard ## User personas diff --git a/doc/website/.pages b/doc/website/.pages index a1f9abefb5..0891faf48e 100644 --- a/doc/website/.pages +++ b/doc/website/.pages @@ -7,4 +7,3 @@ nav: - FAQ.md - advanced - release-notes - - API-reference diff --git a/doc/website/FAQ.md b/doc/website/FAQ.md index 1d3fc53334..dac5e0fabb 100644 --- a/doc/website/FAQ.md +++ b/doc/website/FAQ.md @@ -4,7 +4,7 @@ In this document are tips and hints documented which can help for troubleshootin ## Does iceoryx run in a docker environment -Yes. Take a look at the [icedocker example](https://github.com/eclipse-iceoryx/iceoryx/blob/master/iceoryx_examples/icedelivery) +Yes. Take a look at the [icedocker example](https://github.com/eclipse-iceoryx/iceoryx/blob/master/iceoryx_examples/icedocker) ## iox-roudi fails on startup diff --git a/doc/website/advanced/configuration-guide.md b/doc/website/advanced/configuration-guide.md index 0fa9b22d6e..ed2ec267c6 100644 --- a/doc/website/advanced/configuration-guide.md +++ b/doc/website/advanced/configuration-guide.md @@ -15,7 +15,7 @@ These options adjust the limits of Publisher and Subscriber Ports for resource m | `IOX_MAX_CHUNKS_HELD_PER_SUBSCRIBER_SIMULTANEOUSLY` | Maximum number of chunks a subscriber can hold at a given time (subscriber history size)| | `IOX_MAX_INTERFACE_NUMBER` | Maximum number of interface ports which are used for gateways | -Have a look at [iceoryx_posh_deployment.cmake](https://github.com/eclipse-iceoryx/iceoryx/blob/master/iceoryx_posh/cmake/iceoryx_posh_deployment.cmake) for the default values of the constants. +Have a look at [IceoryxPoshDeployment.cmake](https://github.com/eclipse-iceoryx/iceoryx/blob/master/iceoryx_posh/cmake/IceoryxPoshDeployment.cmake) for the default values of the constants. !!! hint With the default values set, the size of `iceoryx_mgmt` is ~64.5 MByte. You can reduce the size by decreasing the values from the table via the CMake options. The current values are printed in the CMake stage when building iceoryx. diff --git a/doc/website/advanced/installation-guide-for-contributors.md b/doc/website/advanced/installation-guide-for-contributors.md index 1c049367ef..715d61196a 100644 --- a/doc/website/advanced/installation-guide-for-contributors.md +++ b/doc/website/advanced/installation-guide-for-contributors.md @@ -2,27 +2,32 @@ ## :material-test-tube: Build and run tests -While developing on iceoryx you want to know if your changes are breaking existing functions or if your newly written tests are passing. -For that purpose, we are generating CMake targets that are executing the tests. First, we need to build them: +While developing on iceoryx, you may want to know if your changes will break existing functionality or if your +newly written tests will pass. For that purpose, we generate CMake targets that execute the tests. First, +we need to build them: ```bash cmake -Bbuild -Hiceoryx_meta -DBUILD_TEST=ON cmake --build build ``` -CMake is automatically installing GoogleTest as a local dependency and build the tests against it. Please note that if you want to build tests for extensions like the DDS-Gateway you need to enable this extension as well in the CMake build. To build tests for all extensions simply add `-DBUILD_ALL` to the CMake command. +CMake automatically installs GoogleTest as a local dependency and builds the tests against it. Please note that +if you want to build tests for extensions like the DDS-Gateway you need to enable this extension as well in the +CMake build. To build the tests for all extensions simply add `-DBUILD_ALL` to the CMake command. !!! hint - Before creating a Pull-Request, you should check your code for compiler warnings. For that purpose is the `-DBUILD_STRICT` CMake option available which treats compiler warnings as errors. This flag is enabled on the GitHub CI for building Pull-Requests. + Before creating a Pull-Request, you should check your code for compiler warnings. The `-DBUILD_STRICT` CMake option + is available for this purpose, which treats compiler warnings as errors. This flag is enabled on the GitHub + CI for building Pull-Requests. -Now let's execute tests: +Now let's execute all tests: ```bash cd iceoryx/build make all_tests ``` -Some of the tests are timing critical and need a stable environment. We call them timing tests and have them in separate targets available: +Some of the tests are time-dependent and need a stable environment. These timing tests are available in separate targets: ```bash make timing_module_tests @@ -30,12 +35,14 @@ make timing_integration_tests ``` In iceoryx we distinguish between different test levels. The most important are: Module tests and Integration tests. -Module tests are basically Unit-tests where the focus is on class level with black-box testing. -In integration tests multiple classes (e.g. mepoo config) are tested together. +Module or unit tests are basically black box tests that test the public interface of a class. +In integration tests the interaction of several classes is tested. The source code of the tests is placed into the folder `test` within the different iceoryx components. You can find there at least a folder `moduletests` and sometimes `integrationtests`. -If you now want to create a new test you can place the sourcefile directly into the right folder. CMake will automatically detect the new file when doing a clean build and will add it to a executable. There is no need to add a gtest main function because we already provide it. -For every test level are executables created, for example `posh_moduletests`. They are placed into the corresponding build folder (e.g. `iceoryx/build/posh/test/posh_moduletests`). +If you now want to create a new test, you can place the source file directly into the right folder. CMake will +automatically detect the new file when doing a clean build and will add it to the corresponding executable. +There is no need to add a gtest main function because we already provide it. Executables are created for every test +level, for example `posh_moduletests`. They are placed into the corresponding build folder (e.g. `iceoryx/build/posh/test/posh_moduletests`). If you want to execute only individual test cases, you can use these executables together with a filter command. Let's assume you want to execute only `ServiceDescription_test` from posh_moduletests: @@ -51,7 +58,8 @@ Let's assume you want to execute only `ServiceDescription_test` from posh_module ## :fontawesome-solid-pump-soap: Use Sanitizer Scan Due to the fact that iceoryx works a lot with system memory, it should be ensured that errors like memory leaks are not introduced. -To prevent this, we use the clang toolchain which offers several tools for scanning the codebase. One of them is the [Address-Sanitizer](https://clang.llvm.org/docs/AddressSanitizer.html) which checks for example on dangling pointers. +To prevent this, we use the clang toolchain which offers several tools for scanning the codebase. One of them is the +[AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html) which checks e.g. for dangling pointers. The below-listed sanitizers are enabled at the moment. @@ -61,7 +69,7 @@ The below-listed sanitizers are enabled at the moment. - [LeakSanitizer](https://clang.llvm.org/docs/LeakSanitizer.html) (LSan) is a run-time memory leak detector. In iceoryx, it runs as part of the AddressSanitizer. - [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html) (UBSan) is a fast undefined behavior detector. iceoryx uses default behavior i.e. `print a verbose error report and continue execution` -With the `iceoryx_build_test.sh` script you can do the scan on your own. Additionally, the scans are running on the CI in every Pull-Request. +With the `iceoryx_build_test.sh` script you can run the scan yourself. Additionally, the scans are running on the CI in every Pull-Request. As a prerequisite, you need to install the clang compiler: ```bash @@ -80,25 +88,35 @@ Now we can run the tests with enabled sanitizer options: cd build && ./tools/run_tests.sh ``` -If errors occur, an error report is shown with a stack trace to find the place where the leak occurs. If the leak comes from an external dependency or shall be handled later then it is possible to set a function on a suppression list. -This should be only rarely used and only in coordination with an iceoryx maintainer. +If errors occur, an error report is shown with a stack trace to find the place where the leak occurs. If the leak +has its origin in an external dependency or shall be handled later then it is possible to set a function on a suppression list. +This should be used only rarely and only in coordination with an iceoryx maintainer. !!! note - iceoryx needs to be built as a static library for working with sanitizer flags. The script does it automatically. - Except when you want to use the ${ICEORYX_WARNINGS} then you have to call `findpackage(iceoryx_hoofs)` + iceoryx needs to be built as a static library to work with sanitizer flags, which is automatically achieved when using + the script. If you want to use the ${ICEORYX_WARNINGS} then you have to call `find_package(iceoryx_hoofs)` and `include(IceoryxPlatform)` + to make use of the ${ICEORYX_SANITIZER_FLAGS}. ## :material-library: iceoryx library build -The iceoryx build consists of several libraries which have dependencies on each other. The goal is to have self-encapsulated library packages available where the end-user can easily find it with the CMake command `find-package(...)`. -In the default case, the iceoryx libraries are installed by `make install` into `/usr/lib` which requires root access. As an alternative you can install the libs into a custom folder by setting `-DCMAKE_INSTALL_PREFIX=/custom/install/path` as build-flag for the CMake file in iceoryx_meta. +The iceoryx build consists of several libraries which have dependencies on each other. The goal is to have encapsulated +library packages available so that the end-user can easily find them with the CMake command `find_package(...)`. +In the default case, the iceoryx libraries are installed by `make install` into `/usr/lib` which requires root access. +As an alternative you can install the libs into a custom folder by setting `-DCMAKE_INSTALL_PREFIX=/custom/install/path` +as build flag for the CMake file in iceoryx_meta. -As a starting point for the CMake build, iceoryx_meta collects all libraries (hoofs, posh etc.) and extensions (binding_c, dds) together. The provided build script `iceoryx_build_test.sh` in the `tools` folder uses iceoryx_meta. +iceoryx_meta collects all libraries (hoofs, posh etc.) and extensions (binding_c, dds) and can be a starting point for +the CMake build. The provided build script `tools/iceoryx_build_test.sh` uses iceoryx_meta. Per default, iceoryx is built as static lib for better usability. Additionally, we offer to build as shared library because it is a cleaner solution for resolving dependency issues and it reduces the linker time. -This is done by the flag `BUILD_SHARED_LIBS` which is set to OFF per default. If you want to have shared libraries, just pass `-DBUILD_SHARED_LIBS=ON` to CMake or use `build-shared` as a flag in the build script. +This is done by the flag `BUILD_SHARED_LIBS` which is set to OFF per default in iceoryx_meta. If you want to have shared libraries, just pass `-DBUILD_SHARED_LIBS=ON` to CMake or use `build-shared` as a flag in the build script. -If iceoryx builds shared libraries you have to copy them into a custom path and need to set the LD_LIBRARY_PATH to the custom path (e.g. build/install/prefix). +!!! note + When building with `colcon` in ROS 2, the packages `iceoryx_hoofs`, `iceoryx_posh` and `iceoryx_binding_c` are built + automatically as shared libraries. + +If iceoryx builds shared libraries you have to copy them into a custom path and set the LD_LIBRARY_PATH to the custom path (e.g. build/install/prefix). ```bash export LD_LIBRARY_PATH=/your/path/to/iceoryx/libs @@ -113,7 +131,9 @@ LD_LIBRARY_PATH=/your/path/to/lib iox-roudi If you want to share iceoryx to other users, you can create a debian package. This can be done by using: `./tools/iceoryx_build_test.sh package` where it will be deployed into the `build_package` folder. !!! note - The CMake libraries export their dependencies for easier integration. This means that you do not need to do a `findpackage()` to all the dependencies. For example, you don't need to call `findpackage(iceoryx_hoofs)` when you have it done for iceoryx_posh. It includes it already. + The CMake libraries export their dependencies for easier integration. This means that you do not need to do a `find_package()` + for all the dependencies. For example, you don't need to call `find_package(iceoryx_hoofs)` when you already called + `find_package(iceoryx_posh)` since iceoryx_posh includes iceoryx_hoofs. ## Tips & Tricks @@ -123,7 +143,9 @@ docker container with preinstalled dependencies and a configuration similar to the CI target container. When for instance the target ubuntu 18.04 fails we can start the container with + ```sh ./tools/scripts/ice_env.sh enter ubuntu:18.04 ``` + which enters the environment automatically and one can start debugging. diff --git a/doc/website/concepts/qos-policies.md b/doc/website/concepts/qos-policies.md index 06937f7eba..3dfe1a5f9e 100644 --- a/doc/website/concepts/qos-policies.md +++ b/doc/website/concepts/qos-policies.md @@ -66,4 +66,4 @@ If `requiresPublisherHistorySupport` is set, additionally to the matching criter 1. Same `capro::ServiceDescription` 2. Matching `ConsumerTooSlowPolicy` and `QueueFullPolicy` in `PublisherOptions`/`SubscriberOptions` -3. `SubscriberOptions::historyRequest` <= `PublisherOptions::historyCapacity` +3. `PublisherOptions::historyCapacity` > 0 diff --git a/doc/website/examples/icediscovery_in_c.md b/doc/website/examples/icediscovery_in_c.md index de0d67a29a..ef521bb817 100644 --- a/doc/website/examples/icediscovery_in_c.md +++ b/doc/website/examples/icediscovery_in_c.md @@ -2,4 +2,4 @@ title: Searching for currently available services using C --- -{! ../iceoryx/iceoryx_examples/icediscovery/README.md !} +{! ../iceoryx/iceoryx_examples/icediscovery_in_c/README.md !} diff --git a/doc/website/getting-started/overview.md b/doc/website/getting-started/overview.md index 25fe1e18b5..917e7e35ac 100644 --- a/doc/website/getting-started/overview.md +++ b/doc/website/getting-started/overview.md @@ -1,16 +1,17 @@ # Overview -This document covers the core functionality of Eclipse iceoryx and is intended to quickly get started to -set up iceoryx applications. +This document covers the core functionality of Eclipse iceoryx and is intended to +provide a quick introduction to setting up iceoryx applications. ## General To set up a collection of applications using iceoryx (an _iceoryx system_), the applications need to initialize a -runtime and create _publishers_ and _subscribers_. The publishers send data of a specific _topic_ which can be -received by subscribers of the same topic. To enable publishers to offer their topic and subscribers to subscribe to -these offered topics, the middleware daemon, called `RouDi`, must be running. +runtime and create communication participants like _publishers_ and _subscribers_ or _clients_ and _servers_. +Publishers send data of a specific _topic_ which can be received by subscribers of the same topic. Servers wait +on a topic for requests from clients and respond to these requests. To enable the operation of these participants, +the middleware daemon, called `RouDi`, must be running. -But before we get into more details, let's start with a simple example. +But before we get into more details, let's start with a simple publish-subscribe example. ### A first example @@ -54,9 +55,9 @@ else Here `result` is an `expected` and hence we may get an error. This can happen if we try to loan too many samples and exhaust memory. We have to handle this potential error since the expected class has the `nodiscard` keyword -attached. This means we get a warning (or an error when build in strict mode) when we don't handle it. We could also -explicitly discard it with `IOX_DISCARD_RESULT` which is discouraged. If you want to know more about `expected`, -take a look at +attached. This means we get a warning (or an error when build in strict mode) when we don't handle it. We could +also explicitly discard it with `IOX_DISCARD_RESULT` which is discouraged. If you want to know more about +`expected`, take a look at [How optional and error values are returned in iceoryx](how-optional-and-error-values-are-returned-in-iceoryx.md). Let's create a corresponding subscriber. @@ -66,9 +67,8 @@ iox::popo::Subscriber subscriber({"Group", "Instance", "CounterTop ``` Now we can use the subscriber to receive data. For simplicity, we assume that we periodically check for new data. It -is also possible to explicitly wait for data using the [WaitSet](waitset.md) or the -[Listener](callbacks.md). The code to receive the data is the same, the only difference is the way we wake -up before checking for data. +is also possible to explicitly wait for data using the [WaitSet](waitset.md) or the [Listener](callbacks.md). The +code to receive the data is the same, the only difference is the way we wake up before checking for data. ```cpp while (keepRunning) @@ -117,7 +117,7 @@ Afterwards, we can start the applications which immediately connect to the RouDi When the application terminates, the runtime cleans up all resources needed for communication with RouDi. This includes all memory chunks used for the data transmission which may still be held by the application. -Before going into the details in the next section, the following animations depicts the course of events. +Before going into the details in the next sections, the following animation depicts the course of events. ![Overview](https://user-images.githubusercontent.com/8661268/74612998-b962bc80-510a-11ea-97f0-62f41c5d287b.gif) @@ -126,12 +126,13 @@ We now briefly define the main entities of an iceoryx system which were partiall ### RouDi RouDi is an abbreviation for **Rou**ting and **Di**scovery. RouDi takes care of the communication setup but does not -actually participate in the communication between the publisher and the subscriber. RouDi can be thought of as the -switchboard operator of iceoryx. One of its other major tasks is the setup of the shared memory, which the -applications use for exchanging payload data. Sometimes referred to as daemon, RouDi manages the shared memory and is -responsible for the service discovery, i.e. enabling subscribers to find topics offered by publishers. It also keeps -track of all applications which have initialized a runtime and are hence able to create publishers, subscribers, -servers or clients. It provides facilities for applications to query this information. +actually participate in the communication between the publisher and the subscriber or the client and the server. +RouDi can be thought of as the switchboard operator of iceoryx. One of its other major tasks is the setup of the +shared memory, which the applications use for exchanging payload data. Sometimes referred to as daemon, RouDi +manages the shared memory and is responsible for the service discovery, i.e. enabling subscribers/clients to find +topics offered by publishers/servers. It also keeps track of all applications which have initialized a runtime and +are hence able to create publishers, subscribers, servers or clients. It provides facilities for applications to +query this information. When an application crashes, RouDi cleans up all resources. Due to our mostly lock-free inter-process mechanisms (only one last lock; we are working to remove it), iceoryx-based communication is much more reliable compared to @@ -142,7 +143,7 @@ To view the available command line options for RouDi call `$ICEORYX_ROOT/build/i ### Shared memory To enable zero-copy inter-process communication, iceoryx uses the shared memory approach, i.e. publishers and -subscribers can communicate via shared memory resulting in zero-copy communication. +subscribers or clients and servers can communicate via shared memory resulting in zero-copy communication. Shared memory is physical memory that is made accessible to multiple processes via a mapping to a memory area in their virtual address spaces. @@ -169,8 +170,8 @@ application's address space. ### Creating service descriptions for topics -A `ServiceDescription` in iceoryx represents a topic under which publisher and subscribers can exchange data and is -uniquely identified by three string identifiers. +A `ServiceDescription` in iceoryx represents a topic under which publishers and subscribers or clients and servers +can exchange data and is uniquely identified by three string identifiers. 1. `Group` name 2. `Instance` name @@ -188,13 +189,13 @@ The following table gives an overview of the different terminologies and the cur | | Group | Instance | Topic | |-----------------------------------------------------------------------------------|---------|------------------|------------------------| -| [rmw_iceoryx](https://github.com/ros2/rmw_iceoryx/) | Type | Namespace/Topic | - | +| [rmw_iceoryx](https://github.com/ros2/rmw_iceoryx/) | Type | Namespace/Topic | - | | AUTOSAR | Service | Instance | Event | | [DDS Gateway](https://github.com/eclipse-iceoryx/iceoryx/tree/master/iceoryx_dds) | - | - | /Group/Instance/Topic | -| [Cyclone DDS](https://github.com/ros2/rmw_cyclonedds/pull/303/files) | - | Type Name | Topic Name | +| [Cyclone DDS](https://github.com/ros2/rmw_cyclonedds) | - | Type Name | Topic Name | -Service is related to instance like classes are related to objects in C++. Service describes an abstract topic and an -instance is one instantiation of that abstraction. Like an object is an instantiated class. Events are in this context +Service is related to instance like classes are related to objects in C++. A service describes an abstract topic and an +instance is one instantiation of that abstraction, like an object is an instantiated class. Events are in this context like members of a class. Example: @@ -210,9 +211,10 @@ MyRadarService frontLeftRadarInstance; std::cout << frontLeftRadarInstance.hasObstacleDetected << std::endl; ``` -In the iceoryx world we would subscribe to the service `("MyRadarService", "frontLeftRadarInstance", "hasObstacleDetected")` -and would receive a sample whenever an obstacle was detected. Or we would subscribe to `distanceToObstacle` and would -receive a constant stream of data which presents the distance to the obstacle. +In the iceoryx world, we would for instance subscribe to the service +`("MyRadarService", "frontLeftRadarInstance", "hasObstacleDetected")` and would receive a sample whenever +an obstacle was detected. Or we would subscribe to `distanceToObstacle` and would receive a constant stream +of data which presents the distance to the obstacle. #### Restrictions @@ -237,7 +239,7 @@ the user has to ensure that it is interpreted correctly. Once it has offered its topic, it is able to publish (send) data of the specific type. Note that the default is to have multiple publishers for the same topic (n:m communication). A compile-time option to restrict iceoryx to -1:n communication is available. Should 1:n communication be used RouDi checks for multiple publishers on the same +1:n communication is available. Should 1:n communication be used, RouDi checks for multiple publishers on the same topics and raises an error if there is more than one publisher for a topic. ### Subscriber @@ -250,27 +252,47 @@ untyped case this is raw memory and the user must take care that it is interpret data that was actually sent. When multiple publishers have offered the same topic the subscriber will receive the data of all of them (but in -indeterminate order between different publishers). +indeterminate order between different publishers). Note that the subscriber will not receive data from servers or +clients, even when they use the same topic. + +### Client + +Similar to publishers and subscribers, clients are tied to a topic and need a service description to be constructed. +If the client is typed, one needs to specify the request and response data types as template parameters. In the +untyped case, the client is only aware of raw memory and the user has to take care of its correct interpretation. + +Once a client is connected to a server, it can send requests to and receive responses from the server. A sequence +ID is used to match a response to a specific request. It must be set on request and checked on response by the user. + +### Server + +Like a client, a server needs a service description to be constructed and can be typed or untyped. In the typed +case, the user has to provide the request and the response data types as template parameters. Otherwise, the server +handles raw memory and the user has to ensure that it is interpreted correctly. + +Once connected, the server can receive requests from clients and send the corresponding responses. ## Avoid polling -The easiest way to receive data is to periodically poll whether data is available as depicted on the left side of -picture below. This is sufficient for simple use cases but inefficient in general, as it often leads to unnecessary -latency and wake-ups without data. An alternative approach to receive data is to wait for user-defined events to occur. -This is provided by our `WaitSet` and `Listener` which are introduced in the following sections. +The easiest way to receive data is to periodically poll whether data is available as depicted on the left side of the +picture below for the publish-subscribe messaging pattern. This is sufficient for simple use cases but inefficient in +general, as it often leads to unnecessary latency and wake-ups without data. An alternative approach to receive data +is to wait for user-defined events to occur. This is provided by our `WaitSet` and `Listener` which are introduced in +the following sections. -![Polling alternatives](avoid-polling.svg) +![Polling alternatives](/images/avoid-polling.svg) ### WaitSet -The `WaitSet` can be used to relinquish control by putting the thread to sleep with a non-busy wait and wait for -user-defined events to occur. Usually, these events correspond to the availability of data at specific subscribers. -This way we can immediately wake up when data is available and avoid unnecessary wake-ups if no data is available. +The WaitSet can be used to relinquish control by putting the thread to sleep with a non-busy wait and wait for +user-defined events to occur. Usually, these events correspond to the availability of data at specific subscribers +or clients. This way we can immediately wake up when data is available and avoid unnecessary wake-ups if no data +is available. -One typical use case is to create a `WaitSet`, attach multiple subscribers and user triggers and then wait until one -or many of the attached objects signal an event. If this happens one receives a list of all occured events called -`notificationVector`. This makes it possible to collect data directly from the subscriber when it signals the WaitSet -that new data is available. +One typical use case is to create a WaitSet, attach multiple subscribers and/or clients and user triggers and then +wait until one or many of the attached objects signal an event. If this happens one receives a list of all occured +events called `notificationVector`. This makes it possible to collect data directly from the subscriber or client +when it signals the WaitSet that new data or a new response is available. The WaitSet uses the [reactor pattern](https://en.wikipedia.org/wiki/Reactor_pattern) and is informed with a push strategy that one of the attached events occured at which it informs the user. @@ -280,9 +302,9 @@ For more information on how to use the WaitSet see our ### Listener -The `Listener` can be used to connect custom callbacks to user-defined events. Unlike the WaitSet, it reacts to those -events by executing the connected custom callbacks in a background thread, that will be created by the `Listener`. -As with the `WaitSet` the background thread waits non-busy on the reception of new data. +The Listener can be used to connect custom callbacks to user-defined events. Unlike the WaitSet, it reacts to those +events by executing the connected custom callbacks in a background thread, that will be created by the Listener. +As with the WaitSet the background thread waits non-busy on the reception of new data. !!! note The Listener is completely thread-safe but please be aware that most of the objects which can be attached to the @@ -292,17 +314,19 @@ As with the `WaitSet` the background thread waits non-busy on the reception of n One use case could be that one creates a Listener and attaches multiple subscribers. Every time new data is available, the corresponding connected callback will be executed, e.g. print something to the console or calculate an algorithm. +Another use case could be that a server is attached to the Listener and every time a request is received, the +connected callback that creates and sends a response, is executed. Like the WaitSet, the Listener uses the reactor pattern. For more information about the Listener see our -[callbacks example](https://github.com/eclipse-iceoryx/iceoryx/blob/master/iceoryx_examples/callbacks). +[callbacks examples](https://github.com/eclipse-iceoryx/iceoryx/blob/master/iceoryx_examples/callbacks). ## API The API is offered in two languages, C++ and C. Detailed information can be found in the -[C++ example](icedelivery.md) and -[C example](icedelivery_in_c.md). +[C++ example](https://github.com/eclipse-iceoryx/iceoryx/blob/master/iceoryx_examples/icedelivery) and +[C example](https://github.com/eclipse-iceoryx/iceoryx/blob/master/iceoryx_examples/icedelivery_in_c). Many parts of the C++ API follow a functional programming approach which is less error-prone. This requires using the monadic types `cxx::expected` and `cxx::optional` which are introduced diff --git a/doc/website/getting-started/what-is-iceoryx.md b/doc/website/getting-started/what-is-iceoryx.md index bf87d97a7b..cec488d31f 100644 --- a/doc/website/getting-started/what-is-iceoryx.md +++ b/doc/website/getting-started/what-is-iceoryx.md @@ -9,14 +9,15 @@ The simple answer was to avoid copying of messages inside the middleware that ma the different software nodes. This can be done by using shared memory that can be accessed by the producers and consumers of messages. On its own, this is not a new innovation as the approach has been used since the 1970s. However, iceoryx takes the approach further, ending up in an inter-process-communication technology with a -publish/subscribe architecture that is fast, flexible and dependable. +publish/subscribe and request/response architecture that is fast, flexible and dependable. ## Fast With the iceoryx API, a publisher can write the message directly into a chunk of memory that was previously requested from the middleware. When the message is delivered, subscribers receive reference counted pointers to these memory -chunks, which are stored in queues with configurable capacities. With this iceoryx achieves what we refer to as true -zero-copy — an end-to-end approach from publishers to subscribers without creating a single copy. +chunks, which are stored in queues with configurable capacities. The same principle applies to clients and servers. +With this iceoryx achieves what we refer to as true zero-copy — an end-to-end approach from producers to consumers +without creating a single copy. Avoiding the copies on API level is crucial when GBytes of sensor data have to be processed per second on robotics and autonomous driving systems. Therefore the iceoryx team contributed to the standardization of true zero-copy capable @@ -33,7 +34,7 @@ are the next ones on the list. The typed C++ API is the most comfortable when yo on the user side. The untyped C++ API and the C API provide a data agnostic interface that is often preferred when integrating iceoryx as shared memory backbone into a bigger framework. -The APIs support polling access and event-driven interactions with the [Waitset](../overview/#waitset) and +The APIs support polling access and event-driven interactions with the [WaitSet](../overview/#waitset) and [Listener](../overview/#listener). Applications can be started and stopped flexibly as there is a service discovery behind the scenes that dynamically connects matching communication entities. diff --git a/doc/website/overrides/partials/footer.html b/doc/website/overrides/partials/footer.html index df5ad7c67d..c2482a7446 100644 --- a/doc/website/overrides/partials/footer.html +++ b/doc/website/overrides/partials/footer.html @@ -88,4 +88,4 @@ - \ No newline at end of file + diff --git a/doc/website/release-notes/iceoryx-v2-0-0.md b/doc/website/release-notes/iceoryx-v2-0-0.md index be5dd571b6..8caa3f83f6 100644 --- a/doc/website/release-notes/iceoryx-v2-0-0.md +++ b/doc/website/release-notes/iceoryx-v2-0-0.md @@ -26,7 +26,7 @@ - Extend `cxx::optional` constructor for in place construction so that copy/move for values inside the optional even could be deleted [\#967](https://github.com/eclipse-iceoryx/iceoryx/issues/967) - Add templated `from`/`into` free functions to formalize conversions from enums and other types [#992](https://github.com/eclipse-iceoryx/iceoryx/issues/992) - `UniqueId` class for unique IDs within a process [#1010](https://github.com/eclipse-iceoryx/iceoryx/issues/1010) -- Add `requirePublisherHistorySupport` option at subscriber side (if set to true requires historyRequest <= historyCapacity to be eligible for connection) [#1029](https://github.com/eclipse-iceoryx/iceoryx/issues/1029) +- Add `requirePublisherHistorySupport` option at subscriber side (if set to true requires historyCapacity > 0 to be eligible for connection) [#1029](https://github.com/eclipse-iceoryx/iceoryx/issues/1029), [#1278](https://github.com/eclipse-iceoryx/iceoryx/issues/1278) - Add `/tools/scripts/ice_env.sh` shell script to provide simple access to docker containers for CI debugging [#1049](https://github.com/eclipse-iceoryx/iceoryx/issues/1049) - Introduce `cxx::FunctionalInterface` to enrich nullable classes with `and_then`, `or_else`, `value_or`, `expect` [\#996](https://github.com/eclipse-iceoryx/iceoryx/issues/996) - Add C++17 `std::perms` as `cxx::perms` to `iceoryx_hoofs/cxx/filesystem.hpp`. [#1059](https://github.com/eclipse-iceoryx/iceoryx/issues/1059) @@ -88,6 +88,9 @@ fb913bf0de288ba84fe98f7a23d35edfdb22381 - Update cyclone dds version used by gateway to support aarch64 [\#1223](https://github.com/eclipse-iceoryx/iceoryx/issues/1223) - The file lock posix wrapper unlocks and removes a file correctly [\#1216](https://github.com/eclipse-iceoryx/iceoryx/issues/1216) - Minor fixes for the examples [\#743](https://github.com/eclipse-iceoryx/iceoryx/issues/743) +- Fix race condition in Windows platform semaphore/mutex posix implementation [\#1271](https://github.com/eclipse-iceoryx/iceoryx/issues/1271) +- Fix race condition in Windows platform HandleTranslator [\#1264](https://github.com/eclipse-iceoryx/iceoryx/issues/1264) +- Fix race condition in Windows platform shared memory implementation [\#1269](https://github.com/eclipse-iceoryx/iceoryx/issues/1269) **Refactoring:** diff --git a/iceoryx_binding_c/CMakeLists.txt b/iceoryx_binding_c/CMakeLists.txt index c97c3d10c3..db4cdd2b0b 100644 --- a/iceoryx_binding_c/CMakeLists.txt +++ b/iceoryx_binding_c/CMakeLists.txt @@ -16,7 +16,7 @@ # SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.16) -set(IOX_VERSION_STRING "1.91.0") +set(IOX_VERSION_STRING "2.0.0") diff --git a/iceoryx_binding_c/include/iceoryx_binding_c/config.h b/iceoryx_binding_c/include/iceoryx_binding_c/config.h index ef23068cf0..0c89a018dd 100644 --- a/iceoryx_binding_c/include/iceoryx_binding_c/config.h +++ b/iceoryx_binding_c/include/iceoryx_binding_c/config.h @@ -20,79 +20,79 @@ #include /// @brief returns maximum supported amount of publishers -uint32_t iox_cfg_max_publishers(); +uint32_t iox_cfg_max_publishers(void); /// @brief returns maximum amount of subscribers which can be subscribed to one publisher -uint32_t iox_cfg_max_subscribers_per_publisher(); +uint32_t iox_cfg_max_subscribers_per_publisher(void); /// @brief returns maximum amount of samples a publisher can acquire at the same time with loan -uint32_t iox_cfg_max_chunks_allocated_per_publisher_simultaneously(); +uint32_t iox_cfg_max_chunks_allocated_per_publisher_simultaneously(void); /// @brief returns maximum history size for a publisher (e.g. samples which are hold back so // that new subscribers can acquire past data) -uint64_t iox_cfg_max_publisher_history(); +uint64_t iox_cfg_max_publisher_history(void); /// @brief returns maximum supported amount of subscribers -uint32_t iox_cfg_max_subscribers(); +uint32_t iox_cfg_max_subscribers(void); /// @brief returns the maximum amount of samples which a subscriber can hold without releasing them -uint32_t iox_cfg_max_chunks_held_per_subscriber_simultaneously(); +uint32_t iox_cfg_max_chunks_held_per_subscriber_simultaneously(void); /// @brief returns the maximum subscriber queue capacity which is used when the publisher delivers samples to the /// subscriber if the queue capacity is reached new samples will discard old samples -uint32_t iox_cfg_max_subscriber_queue_capacity(); +uint32_t iox_cfg_max_subscriber_queue_capacity(void); /// @brief returns the maximum supported amount of condition variables. this determines how many listeners and waitsets /// can be used in one iceoryx system -uint32_t iox_cfg_max_number_of_condition_variables(); +uint32_t iox_cfg_max_number_of_condition_variables(void); /// @brief returns the maximum supported amount of notifiers per condition variable. this determines how many /// attachments a listener or waitset can have -uint32_t iox_cfg_max_number_of_notifiers_per_condition_variable(); +uint32_t iox_cfg_max_number_of_notifiers_per_condition_variable(void); /// @brief returns the maximum amount of attachments per waitset /// @note is less or equal to iox_cfg_max_number_of_notifiers_per_condition_variable -uint32_t iox_cfg_max_number_of_attachments_per_waitset(); +uint32_t iox_cfg_max_number_of_attachments_per_waitset(void); /// @brief returns the maximum amount of evens per listener /// @note is less or equal to iox_cfg_max_number_of_notifiers_per_condition_variable -uint32_t iox_cfg_max_number_of_events_per_listener(); +uint32_t iox_cfg_max_number_of_events_per_listener(void); /// @brief returns the maximum amount of mempools for roudi. restricts also the number of mempools in the roudi config /// file -uint32_t iox_cfg_max_number_of_mempools(); +uint32_t iox_cfg_max_number_of_mempools(void); /// @brief returns the maximum number of shared memory segments. restricts also the number of configurable segments in /// the roudi config file -uint32_t iox_cfg_max_shm_segments(); +uint32_t iox_cfg_max_shm_segments(void); /// @brief returns the maximum supported amount of shared memory providers -uint32_t iox_cfg_max_number_of_memory_provider(); +uint32_t iox_cfg_max_number_of_memory_provider(void); /// @brief returns the maximum supported amount of memory blocks per shared memory provider -uint32_t iox_cfg_max_number_of_memory_blocks_per_memory_provider(); +uint32_t iox_cfg_max_number_of_memory_blocks_per_memory_provider(void); /// @brief returns the alignment of the user payload when it is not set explicitly -uint32_t iox_cfg_chunk_default_user_payload_alignment(); +uint32_t iox_cfg_chunk_default_user_payload_alignment(void); /// @brief returns the size of the user header when no user header is requested by the user -uint32_t iox_cfg_no_user_header_size(); +uint32_t iox_cfg_no_user_header_size(void); /// @brief returns the alignment of the user header when no user header is requested by the user -uint32_t iox_cfg_no_user_header_alignment(); +uint32_t iox_cfg_no_user_header_alignment(void); /// @brief returns the maximum supported amount of processes which can register at roudi by initializing the posh /// runtime -uint32_t iox_cfg_max_process_number(); +uint32_t iox_cfg_max_process_number(void); /// @brief returns the maximum number of services that are supported byt the service registry -uint32_t iox_cfg_service_registry_capacity(); +uint32_t iox_cfg_service_registry_capacity(void); /// @brief returns the maximum number of services a findservice call can return -uint32_t iox_cfg_max_findservice_result_size(); +uint32_t iox_cfg_max_findservice_result_size(void); /// @brief returns the maximum runtime name length -uint32_t iox_cfg_max_runtime_name_length(); +uint32_t iox_cfg_max_runtime_name_length(void); /// @brief the maximum size of a node name string + \0 terminator #define IOX_CONFIG_NODE_NAME_SIZE 101 diff --git a/iceoryx_binding_c/include/iceoryx_binding_c/subscriber.h b/iceoryx_binding_c/include/iceoryx_binding_c/subscriber.h index b1c4ca97ad..416f5dc910 100644 --- a/iceoryx_binding_c/include/iceoryx_binding_c/subscriber.h +++ b/iceoryx_binding_c/include/iceoryx_binding_c/subscriber.h @@ -45,8 +45,8 @@ typedef struct /// @brief describes whether a publisher blocks when subscriber queue is full ENUM iox_QueueFullPolicy queueFullPolicy; - /// @brief Indicates whether we require the publisher to have historyCapacity >= historyRequest. - /// If true and the condition is not met (i.e. historyCapacity < historyRequest), the subscriber will + /// @brief Indicates whether we require the publisher to have historyCapacity > 0. + /// If true and the condition is not met (i.e. historyCapacity = 0), the subscriber will /// not be connected to the publisher. bool requirePublisherHistorySupport; diff --git a/iceoryx_binding_c/package.xml b/iceoryx_binding_c/package.xml index a322e8a0eb..31dee3adbd 100644 --- a/iceoryx_binding_c/package.xml +++ b/iceoryx_binding_c/package.xml @@ -2,9 +2,9 @@ iceoryx_binding_c - 1.91.0 + 2.0.0 Eclipse iceoryx inter-process-communication (IPC) middleware C-Language Binding - Eclipse Foundation, Inc. + Eclipse Foundation, Inc. Apache 2.0 https://iceoryx.io https://github.com/eclipse-iceoryx/iceoryx/issues diff --git a/iceoryx_dds/CMakeLists.txt b/iceoryx_dds/CMakeLists.txt index 7a420fc575..7b6c4f0c33 100644 --- a/iceoryx_dds/CMakeLists.txt +++ b/iceoryx_dds/CMakeLists.txt @@ -16,7 +16,7 @@ # SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.16) -set(IOX_VERSION_STRING "1.91.0") +set(IOX_VERSION_STRING "2.0.0") project(iceoryx_dds VERSION ${IOX_VERSION_STRING}) diff --git a/iceoryx_dds/README.md b/iceoryx_dds/README.md index d473dbd870..aa6ff7fcb2 100644 --- a/iceoryx_dds/README.md +++ b/iceoryx_dds/README.md @@ -1,4 +1,3 @@ - # Gateway to DDS Networks A gateway for bridging between iceoryx systems and DDS networks. The gateway enables iceoryx systems running on separate hosts to communicate with each other. @@ -6,13 +5,11 @@ The gateway enables iceoryx systems running on separate hosts to communicate wit i.e. Data published by a publisher on `Host A` can be received by a matching subscriber on `Host B`. # Organization -This module exports the following executables: -* `iox-gw-iceoryx2dds` -* `iox-gw-dds2iceoryx` - -Each executable manages a POSH runtime that runs the gateway logic for a single direction of communication. +This module exports the `iox-dds-gateway` executable which manages a POSH runtime +and handles the gateway logic of communication. -The common building blocks logic for these binaries are consolidated in the exported library, `libiceoryx_dds`. +The common building blocks logic for this binary are consolidated in the exported +library, `libiceoryx_dds`. Applications may instead directly embed the gateway by using the exported lib. @@ -39,7 +36,7 @@ make # Usage ## Configuration In `/etc/iceoryx/gateway_config.toml` you find the dds gateway configuration. -Every service which should be offered or to which you would like to +Every service which should be offered or to which you would like to subscribe has to be listed in here. ```toml [[services]] @@ -75,20 +72,18 @@ via iceoryx. Open three terminals on machine `A` and execute the following commands: - Terminal 1: `./build/iox-roudi` -- Terminal 2: `./build/iceoryx_dds/iox-gw-iceoryx2dds` to send all samples from the publisher to DDS +- Terminal 2: `./build/iceoryx_dds/iox-dds-gateway` to send all samples from the publisher to DDS - Terminal 3: `./build/iceoryx_examples/icedelivery/iox-cpp-publisher` Open another three terminals on machine `B` and execute the commands: - Terminal 1: `./build/iox-roudi` -- Terminal 2: `./build/iceoryx_dds/iox-gw-dds2iceoryx` to receive all samples from the publisher via DDS +- Terminal 2: `./build/iceoryx_dds/iox-dds-gateway` to receive all samples from the publisher via DDS - Terminal 3: `./build/iceoryx_examples/icedelivery/iox-cpp-subscriber` -If you would like to have a bidirectional communication just run `iox-gw-dds2iceoryx` and -`iox-gw-iceoryx2dds` on the same machine. - ## Running with shared libraries -Before running, you may need to add the install directory to the library load path if it is not standard (so that the runtime dependencies can be found). +Before running, you may need to add the install directory to the library load +path if it is not standard (so that the runtime dependencies can be found). i.e. ``` export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$INSTALL_DIR/lib @@ -98,8 +93,7 @@ Then, simply run the gateway executables as desired. e.g. ```bash -$INSTALL_DIR/bin/iox-gw-iceoryx2dds -$INSTALL_DIR/bin/iox-gw-dds2iceoryx +$INSTALL_DIR/bin/iox-dds-gateway ``` diff --git a/iceoryx_dds/examples/README.md b/iceoryx_dds/examples/README.md index a4889ada92..7238c937c6 100644 --- a/iceoryx_dds/examples/README.md +++ b/iceoryx_dds/examples/README.md @@ -1,15 +1,17 @@ # DDS Gateway Examples -Here you can find examples of now to use the dds gateway in a real system. -The examples use docker to simulate separated hosts running iceoryx. +Here you can find examples how to use the dds gateway in a real system. +The examples use docker to simulate separated hosts running iceoryx. ## Running the Examples Simply run the script: `run_example.sh` -You will be given an option to choose an example to run, after which the necessary Docker containers will be built and run automatically. +You will be given an option to choose an example to run, after which the +necessary Docker containers will be built and run automatically. ## Setting up your own system -For help setting up your own iceoryx systems to use the DDS gateway, refer to the entrypoint scripts used by the example docker containers. +For help setting up your own iceoryx systems to use the DDS gateway, +refer to the entrypoint scripts used by the example docker containers. diff --git a/iceoryx_examples/callbacks/ice_callbacks_subscriber.cpp b/iceoryx_examples/callbacks/ice_callbacks_subscriber.cpp index 5450fb37d4..e2cd85e78b 100644 --- a/iceoryx_examples/callbacks/ice_callbacks_subscriber.cpp +++ b/iceoryx_examples/callbacks/ice_callbacks_subscriber.cpp @@ -130,7 +130,7 @@ int main() }); //! [attach everything] - // wait until someone presses CTRL+c + // wait until someone presses CTRL+C //! [wait for sigterm] iox::posix::waitForTerminationRequest(); //! [wait for sigterm] diff --git a/iceoryx_examples/callbacks_in_c/README.md b/iceoryx_examples/callbacks_in_c/README.md index fef118bb37..b1b8b712c5 100644 --- a/iceoryx_examples/callbacks_in_c/README.md +++ b/iceoryx_examples/callbacks_in_c/README.md @@ -112,7 +112,7 @@ Since we are following a push-based approach, i.e. without an event loop that is the events and processing them, we require a blocking call that waits until the process is signaled to terminate. - + ```c while (keepRunning) { diff --git a/iceoryx_examples/callbacks_in_c/ice_c_callbacks_subscriber.c b/iceoryx_examples/callbacks_in_c/ice_c_callbacks_subscriber.c index 827a496522..533f26247c 100644 --- a/iceoryx_examples/callbacks_in_c/ice_c_callbacks_subscriber.c +++ b/iceoryx_examples/callbacks_in_c/ice_c_callbacks_subscriber.c @@ -162,12 +162,12 @@ int main() listener, subscriberRight, SubscriberEvent_DATA_RECEIVED, &onSampleReceivedCallback); //! [attach everything to the listener] - //! [wait until someone presses CTRL+c] + //! [wait until someone presses CTRL+C] while (keepRunning) { sleep_for(100); } - //! [wait until someone presses CTRL+c] + //! [wait until someone presses CTRL+C] // when the listener goes out of scope it will detach all events and when a // subscriber goes out of scope it will detach itself from the listener diff --git a/iceoryx_examples/callbacks_in_c/ice_c_callbacks_with_context_data.c b/iceoryx_examples/callbacks_in_c/ice_c_callbacks_with_context_data.c index 359207da9c..a37d312932 100644 --- a/iceoryx_examples/callbacks_in_c/ice_c_callbacks_with_context_data.c +++ b/iceoryx_examples/callbacks_in_c/ice_c_callbacks_with_context_data.c @@ -136,7 +136,7 @@ int main() listener, subscriberRight, SubscriberEvent_DATA_RECEIVED, &onSampleReceivedCallback, &counterService); //! [attach everything to the listener] - // wait until someone presses CTRL+c + // wait until someone presses CTRL+C while (keepRunning) { sleep_for(100); diff --git a/iceoryx_examples/complexdata/README.md b/iceoryx_examples/complexdata/README.md index 63891fd60a..cfaa5eeefa 100644 --- a/iceoryx_examples/complexdata/README.md +++ b/iceoryx_examples/complexdata/README.md @@ -4,7 +4,7 @@ To implement zero-copy data transfer we use a shared memory approach. This requires that every data structure needs to be entirely contained in the shared memory and must not internally use pointers or references. The complete list of restrictions can be found -[here](https://iceoryx.io/latest/getting-started/overview/#restrictions). Therefore, most of the STL types cannot be used, but we +[here](https://iceoryx.io/v2.0.0/getting-started/overview/#restrictions). Therefore, most of the STL types cannot be used, but we reimplemented some [constructs](https://github.com/eclipse-iceoryx/iceoryx/tree/master/iceoryx_hoofs#cxx). This example shows how to send/receive a iox::cxx::vector and how to send/receive a complex data structure containing some of our STL container surrogates. diff --git a/iceoryx_examples/icecrystal/Readme.md b/iceoryx_examples/icecrystal/Readme.md index 6fd755e962..110bf1e3fb 100644 --- a/iceoryx_examples/icecrystal/Readme.md +++ b/iceoryx_examples/icecrystal/Readme.md @@ -19,7 +19,7 @@ We re-use the binaries from ## Feature walkthrough This example does not contain any additional code. The code of the `iceoryx_introspection_client` can be found under -[tools/introspection/](../../tools/introspection/). +[tools/introspection/](https://github.com/eclipse-iceoryx/iceoryx/tree/master/tools/introspection). The introspection can be started with several command line arguments. diff --git a/iceoryx_examples/icediscovery/README.md b/iceoryx_examples/icediscovery/README.md index d7de0c8ed1..1aa95a7b36 100644 --- a/iceoryx_examples/icediscovery/README.md +++ b/iceoryx_examples/icediscovery/README.md @@ -22,9 +22,9 @@ the availability of services respectively. We create several publishers which offer their services on construction by default. For more dynamism the `cameraPublishers` offer/stop their services periodically. If you want more information on how to create a publisher, have a -look at the [icehello](https://github.com/eclipse-iceoryx/iceoryx/tree/v2.0.0/iceoryx_examples/icehello), -[icedelivery](https://github.com/eclipse-iceoryx/iceoryx/tree/v2.0.0/iceoryx_examples/icedelivery), -and [iceoptions](https://github.com/eclipse-iceoryx/iceoryx/tree/v2.0.0/iceoryx_examples/icedelivery) +look at the [icehello](https://github.com/eclipse-iceoryx/iceoryx/tree/master/iceoryx_examples/icehello), +[icedelivery](https://github.com/eclipse-iceoryx/iceoryx/tree/master/iceoryx_examples/icedelivery), +and [iceoptions](https://github.com/eclipse-iceoryx/iceoryx/tree/master/iceoryx_examples/iceoptions) examples. ### Find services @@ -44,7 +44,7 @@ It is included via: ``` On that object we can call the method `findService` which expects the three -service [string identifiers](https://github.com/eclipse-iceoryx/iceoryx/blob/v2.0.0/doc/website/getting-started/overview.md#creating-service-descriptions-for-topics) +service [string identifiers](https://github.com/eclipse-iceoryx/iceoryx/blob/master/doc/website/getting-started/overview.md#creating-service-descriptions-for-topics) and a callable which will be applied to all matching services. In addition we have to specify whether we want to search for publishers (`MessagingPattern::PUB_SUB`) used in publish-subscribe communication or servers (`MessagingPattern::REQ_RES`) used in @@ -180,7 +180,7 @@ if (discoveryPtr) ### Monitor service availability If we want to continously monitor the availability of some service or check some discovery condition we can do so by -using e.g. a listener to conditionally execute [callbacks](https://github.com/eclipse-iceoryx/iceoryx/tree/v2.0.0/iceoryx_examples/callbacks). +using e.g. a listener to conditionally execute [callbacks](https://github.com/eclipse-iceoryx/iceoryx/tree/master/iceoryx_examples/callbacks). To do so, we start the applications `iox-discovery-monitor` and `iox-offer-service` (again in any order, but for demonstration purposes `iox-offer-service` should be started last). @@ -368,7 +368,7 @@ The benefit is that this way we can choose containers which do not necessrily re ### Implementation of Discovery monitoring To implement a `Discovery` where we actively monitor availability of services we employ a -[listener](https://github.com/eclipse-iceoryx/iceoryx/tree/v2.0.0/iceoryx_examples/callbacks). +[listener](https://github.com/eclipse-iceoryx/iceoryx/tree/master/iceoryx_examples/callbacks). Contrary to the blocking solution this does not block the user threads and executes any callback in a background thread created by the listener. The callback will be executed on any change of the available services. @@ -431,5 +431,5 @@ which detaches the callback from the listener. As before we built on an `iox::runtime::ServiceDiscovery` by composition and define a custom`findService` function which returns a `std::vector`.
-[Check out icediscovery on GitHub :fontawesome-brands-github:](https://github.com/eclipse-iceoryx/iceoryx/tree/v2.0.0/iceoryx_examples/icediscovery){ .md-button } +[Check out icediscovery on GitHub :fontawesome-brands-github:](https://github.com/eclipse-iceoryx/iceoryx/tree/master/iceoryx_examples/icediscovery){ .md-button }
diff --git a/iceoryx_examples/icediscovery_in_c/README.md b/iceoryx_examples/icediscovery_in_c/README.md index 14ec1443ec..cb2b61bed5 100644 --- a/iceoryx_examples/icediscovery_in_c/README.md +++ b/iceoryx_examples/icediscovery_in_c/README.md @@ -5,7 +5,7 @@ This example demonstrates how to search for specific services using iceoryx's service discovery. It provides two applications - one offering different services and one searching for those with different search queries. The -behavior and structure is quite similar to the [icediscovery C++ example](https://github.com/eclipse-iceoryx/iceoryx/tree/v2.0.0/iceoryx_examples/icedelivery). +behavior and structure is quite similar to the [icediscovery C++ example](https://github.com/eclipse-iceoryx/iceoryx/tree/master/iceoryx_examples/icediscovery). @@ -17,7 +17,7 @@ behavior and structure is quite similar to the [icediscovery C++ example](https: We create several publishers which offer their services on construction by default. For more dynamism the `cameraPublishers` offer/stop their services periodically. If you want more information on how to create publishers, -have a look at the [icedelivery C example](https://github.com/eclipse-iceoryx/iceoryx/tree/v2.0.0/iceoryx_examples/icedelivery_in_c). +have a look at the [icedelivery C example](https://github.com/eclipse-iceoryx/iceoryx/tree/master/iceoryx_examples/icedelivery_in_c). ### Find services @@ -119,8 +119,8 @@ all found services: ```c void searchFrontDevices(const iox_service_description_t service, void* count) { - if (strncmp("FrontLeft", service.instanceString, IOX_CONFIG_SERVICE_STRING_SIZE) == 0 - || strncmp("FrontRight", service.instanceString, IOX_CONFIG_SERVICE_STRING_SIZE) == 0) + if (strncmp(service.instanceString, "FrontLeft", IOX_CONFIG_SERVICE_STRING_SIZE) == 0 + || strncmp(service.instanceString, "FrontRight", IOX_CONFIG_SERVICE_STRING_SIZE) == 0) { ++*(uint32_t*)count; } @@ -128,5 +128,5 @@ void searchFrontDevices(const iox_service_description_t service, void* count) ```
-[Check out icediscovery on GitHub :fontawesome-brands-github:](https://github.com/eclipse-iceoryx/iceoryx/tree/v2.0.0/iceoryx_examples/icediscovery_in_c){ .md-button } +[Check out icediscovery on GitHub :fontawesome-brands-github:](https://github.com/eclipse-iceoryx/iceoryx/tree/master/iceoryx_examples/icediscovery_in_c){ .md-button }
diff --git a/iceoryx_examples/icedocker/README.md b/iceoryx_examples/icedocker/README.md index 94eb27ea94..6432c4b78a 100644 --- a/iceoryx_examples/icedocker/README.md +++ b/iceoryx_examples/icedocker/README.md @@ -7,7 +7,7 @@ environment and it should orchestrate two applications which are running again in two different docker containers so that we end up with a system of 3 different docker containers. -To demonstrate the setup we use the +To demonstrate the setup we use the [icedelivery C++ example](https://github.com/eclipse-iceoryx/iceoryx/tree/master/iceoryx_examples/icedelivery). ``` @@ -32,7 +32,7 @@ Every iceoryx application registers itself at our central broker RouDi by sending a message to the unix domain socket located at `IOX_UDS_SOCKET_PATH_PREFIX/roudi` which is defined in the corresponding platform settings file `platform_settings.hpp`. In linux the socket file handle -can be found at `/tmp/roudi`. When the application registers at RouDi it +can be found at `/tmp/roudi`. When the application registers at RouDi it announces its unix domain socket as well to receive responses of requests which will be sent during runtime to RouDi. This socket is stored as well in `/tmp/IOX_RUNTIME_NAME`. The `iox-cpp-publisher` @@ -42,8 +42,8 @@ runtime has the same name as the binary which leads to the socket ### Shared Access to File Locks Iceoryx applications ensure that every runtime name is unique in the system -by creating a file lock before creating the runtime. This is stored in -`IOX_LOCK_FILE_PATH_PREFIX/IOX_RUNTIME_NAME.lock` whereby +by creating a file lock before creating the runtime. This is stored in +`IOX_LOCK_FILE_PATH_PREFIX/IOX_RUNTIME_NAME.lock` whereby `IOX_LOCK_FILE_PATH_PREFIX` is defined in the platform settings file `platform_settings.hpp`. When running the icedelivery example in a linux environment one can observe @@ -62,7 +62,7 @@ semaphores. For instance to signal a subscriber that data has arrived. ## Implementation -To have shared access to the required resources we have to bind the host +To have shared access to the required resources we have to bind the host filesystem: * `/tmp` @@ -75,8 +75,8 @@ into every docker container. We start in 3 separate terminals 3 docker instances. In this example we use `archlinux:latest` but one is free to choose any other linux distribution. The iceoryx repository which contains an already built iceoryx can be found at -`/home/user/iceoryx` which is bound to `/iceoryx`. The usage is -explained in detail in the +`/home/user/iceoryx` which is bound to `/iceoryx`. The usage is +explained in detail in the [icedelivery C++ example](https://github.com/eclipse-iceoryx/iceoryx/tree/master/iceoryx_examples/icedelivery). #### Terminal 1 (iox-roudi) diff --git a/iceoryx_examples/iceoptions/README.md b/iceoryx_examples/iceoptions/README.md index 7e2fc47434..4e904e3ecc 100644 --- a/iceoryx_examples/iceoptions/README.md +++ b/iceoryx_examples/iceoptions/README.md @@ -89,15 +89,15 @@ subscriberOptions.queueCapacity = 10U; `historyRequest` will enable a subscriber to receive the last n samples of matching publishers on subscription e.g. in case it was started later than the publisher. If the publisher does not have a sufficient `historyCapacity` (smaller than `historyRequest`), it will still be connected but we will not be able to receive the requested amount of historical data (if it was available). Instead we will receive the largest amount of historical sample -the publisher has available, i.e. best-effort. +the publisher has available, i.e. best-effort. In particular we will be connected to a publisher with `historyCapacity` = 0. -If we want to enforce the contract that the publisher needs to support a `historyCapacity` of at least `historyRequest`, we can do so by setting -`requirePublisherHistorySupport` to `true`. In this case, the subscriber will only connect if the publisher history support is at least as large as its request. +If we want to enforce the contract that the publisher needs to support a `historyCapacity`, we can do so by setting `requirePublisherHistorySupport` +to `true`. In this case, the subscriber will only connect if the publisher history support is at least 1, i.e. `historyCapacity` > 0. By default this is set to `false` and best-effort behavior is used. !!! warning In case of n:m communication, the history feature will **not** provide the overall last n samples based on delivery point in time! - For more information about this limitation see the [QoS article](https://iceoryx.io/latest/concepts/qos-policies/). + For more information about this limitation see the [QoS article](https://iceoryx.io/v2.0.0/concepts/qos-policies/). ```cpp diff --git a/iceoryx_examples/iceperf/README.md b/iceoryx_examples/iceperf/README.md index 13e8aa9a4c..12e1ae7cac 100644 --- a/iceoryx_examples/iceperf/README.md +++ b/iceoryx_examples/iceperf/README.md @@ -2,9 +2,9 @@ ## Introduction -!!! hint - Since not all IPC mechanisms are supported on all platforms this benchmark - runs fully on QNX and Linux. +!!! note + Since not all IPC mechanisms are supported on all platforms the IPC benchmark + only runs fully on QNX and Linux. The iceoryx C or C++ API related benchmark is supported on all platforms. This example measures the latency of IPC transmissions between two applications. @@ -13,8 +13,8 @@ We compare the latency of iceoryx with message queues and unix domain sockets. The measurement is carried out with several payload sizes. Round trips are performed for each payload size, using either the default setting or the provided command line parameter for the number of round trips to do. -The measured time is just allocating/releasing memory and the time to send the data. -The construction and writing of the payload is not part of the measurement. +The time measurement only considers the time to allocate/release memory and the time to send the data. +The construction and initialization of the payload is not part of the measurement. At the end of the benchmark, the average latency for each payload size is printed. @@ -25,6 +25,7 @@ In this setup the leader is doing the ping pong measurements with the follower. You can set the number of measurement iterations (number of round trips) with a command line parameter of iceperf-bench-leader (e.g. `./iceperf-bench-leader -n 100000`). There are further options which can be printed by calling `./iceperf-bench-leader -h`. + ```sh # If installed and available in PATH environment variable iox-roudi @@ -38,6 +39,7 @@ There are further options which can be printed by calling `./iceperf-bench-leade If you would like to test only the C++ API or the C API you can start `iceperf-bench-leader` with the parameter `-t iceoryx-cpp-api` or `-t iceoryx-c-api`. + ```sh build/iceoryx_examples/iceperf/iceperf-bench-follower @@ -46,11 +48,10 @@ with the parameter `-t iceoryx-cpp-api` or `-t iceoryx-c-api`. ## Expected Output -The numbers will differ depending on parameters and the performance of the hardware. -Which technologies are measured depends on the operating system (e.g. no message queue on MacOS). -Here an example output with Ubuntu 18.04 on Intel(R) Xeon(R) CPU E3-1505M v5 @ 2.80GHz. +The measured transmission modes depend on the operating system (e.g. no message queue on MacOS). +The measurements depend on the benchmark parameters and the hardware. - +The following shows an example output with Ubuntu 18.04 on Intel(R) Xeon(R) CPU E3-1505M v5 @ 2.80GHz. ### iceperf-bench-leader Application @@ -180,12 +181,12 @@ Here an example output with Ubuntu 18.04 on Intel(R) Xeon(R) CPU E3-1505M v5 @ 2 ## Code Walkthrough -Here we roughly describe the setup for performing the measurements in `iceperf_bench_leader.hpp/cpp` and `iceperf_bench_follower.hpp/cpp`. Things like initialization, sending and receiving of data are technology specific and can be found in the respective files (e.g. uds.cpp for -unix domain socket). Our focus here is on the abstraction layer on top which allows us or you to add new IPC technologies to extend and compare them. +Here we briefly describe the setup for performing the measurements in `iceperf_bench_leader.hpp/cpp` and `iceperf_bench_follower.hpp/cpp`. Things like initialization, sending and receiving of data are technology specific and can be found in the respective files (e.g. uds.cpp for +unix domain socket). Our focus here is on the top-most abstraction layer which allows us to add new IPC technologies to extend and compare them. -### iceperf-bench-leader Application +### iceperf-bench-leader Application Code -Besides includes for the different IPC technologies, the `topic_data.hpp` file is included which contains the `PerSettings` and `PerTopic` structs, which are used to transfer some information between the applications. Independent of the real payload size, the `PerTopic` struct is used as some kind of header in each transferred sample. +Apart from headers for the different IPC technologies, the `topic_data.hpp` file is included which contains the `PerSettings` and `PerTopic` structs. These are used to transfer some information between the applications. The `PerTopic` struct is used as some kind of header in each transferred sample and is independent of the payload size. ```cpp @@ -206,8 +207,11 @@ struct PerfTopic The `PerfSettings` struct is used to synchronize the settings between the leader and the follower application. -The `PerfTopic` struct is used to share some information during the measurement. -With `payloadSize` as the payload size used for the current measurement. In case it is not possible to transfer the `payloadSize` with a single data transfer (e.g. OS limit for the payload of a single socket send), the payload is divided into several sub-packets. This is indicated with `subPackets`. The `runFlag` is used to shutdown the iceperf-bench follower at the end of the benchmark. +The `PerfTopic` struct is used to share some information during the measurement. It contains `payloadSize` +to specify the payload size used for the current measurement. If it is not possible to transmit the `payloadSize` +with a single data transfer (e.g. OS limit for the payload of a single socket send), the payload is divided +into several sub-packets. This is indicated with `subPackets`. The `runFlag` is used to shut down the +iceperf-bench follower at the end of the benchmark. Let's use some constants to prevent magic values and set and names for the communication resources that are used. @@ -227,8 +231,8 @@ MQ::cleanupOutdatedResources(PUBLISHER, SUBSCRIBER); UDS::cleanupOutdatedResources(PUBLISHER, SUBSCRIBER); ``` -The `doMeasurement()` method executes a measurement for the provided IPC technology the and number of round trips. -For being able to always perform the same steps and avoiding code duplications, +The `doMeasurement()` method executes a measurement for the provided IPC technology and number of round trips. +To be able to always perform the same steps and avoiding code duplications, we use a base class with technology independent functionality and the technology has to implement the technology dependent part. @@ -281,16 +285,17 @@ void IcePerfLeader::doMeasurement(IcePerfBase& ipcTechnology) noexcept ``` Initialization is different for each IPC technology. Here we have to create sockets, message queues or iceoryx publisher and subscriber. -With `ipcTechnology.initLeader()` we are setting up these resources on the leader side. +With `ipcTechnology.initLeader()` we set up these resources on the leader side. After the definition of the different payload sizes to use, we execute a single round trip measurement for each individual payload size. -The leader has to orchestrate the whole process and has a pre- and post-step for each ping pong round trip measurement. +The leader has to orchestrate the whole process and has a pre- and post-step for each round trip measurement. `ipcTechnology.preLatencyPerfTestLeader(...)` sets the payload size for the upcoming measurement. -`ipcTechnology.latencyPerfTestLeader(m_settings.numberOfSamples)` performs the ping pong between leader and follower and returns -the time it took to transmit the number of samples in a ping pong round trip. After the measurements were done for all the different payload sizes, -`ipcTechnology.releaseFollower()` releases the follower since it is not aware of things like how many payload sizes are considered. -After cleaning up the communication resources with `ipcTechnology.shutdown()` the results are printed. +`ipcTechnology.latencyPerfTestLeader(m_settings.numberOfSamples)` performs the data exchange between leader and follower and returns +the time it took to transmit the number of samples in a round trip. After the measurements are taken for each payload size, +`ipcTechnology.releaseFollower()` releases the follower. This is required since the follower is not aware of the benchmark settings, +e.g. how many payload sizes are considered and hence we need to issue a shutdown. +We clean up the communication resources with `ipcTechnology.shutdown()` before we print the results. -In the `run()` method we create instances for the different IPC technologies we want to compare. Each technology is implemented in an own class and implements the pure virtual functions provided with the `IcePerfBase` class. But before this is done, we send the `PerfSettings` to the follower application. +In the `run()` method we create instances for the different IPC technologies we want to compare. Each technology is implemented in its own class and implements the pure virtual functions provided with the `IcePerfBase` class. Before this is done, we send the `PerfSettings` to the follower application. ```cpp @@ -361,7 +366,7 @@ int IcePerfLeader::run() noexcept ### iceperf_bench_follower Application -The `iceperf-bench-follower` application is similar to `iceperf-bench-leader`. The first change is the `SUBSCRIBER` and `PUBLISHER` switched their names. +The `iceperf-bench-follower` application is similar to `iceperf-bench-leader`. The first change is that the `SUBSCRIBER` and `PUBLISHER` switch their names. ```c++ constexpr const char APP_NAME[]{"iceperf-bench-follower"}; @@ -370,7 +375,7 @@ constexpr const char SUBSCRIBER[]{"Leader"}; ``` While the `run()` method of the leader publishes the `PerfSettings`, the follower is subscribed to those settings -and waits for them before the technologies are created, which is again equal to the leader. +and waits for them before the technologies are created, which is done similarly as for the leader. ```cpp int IcePerfFollower::run() noexcept @@ -388,8 +393,8 @@ int IcePerfFollower::run() noexcept } ``` -The `doMeasurement()` method is much simpler than the one from the leader, it reacts only and does not have the control. -Besides `ipcTechnology.initFollower()` and `ipcTechnology.shutdown()` all the functionality to do the ping pong for different payload sizes is done in `ipcTechnology.latencyPerfTestFollower()` +The `doMeasurement()` method is much simpler than the one from the leader, since it only has to react on incoming data. +Apart from `ipcTechnology.initFollower()` and `ipcTechnology.shutdown()` all the functionality to perform the round trip for different payload sizes is contained in `ipcTechnology.latencyPerfTestFollower()` ```cpp @@ -402,6 +407,7 @@ void IcePerfFollower::doMeasurement(IcePerfBase& ipcTechnology) noexcept ipcTechnology.shutdown(); } ``` +
[Check out iceperf on GitHub :fontawesome-brands-github:](https://github.com/eclipse-iceoryx/iceoryx/tree/master/iceoryx_examples/iceperf){ .md-button }
diff --git a/iceoryx_examples/request_response/README.md b/iceoryx_examples/request_response/README.md index 3023db5751..92ab4342b9 100644 --- a/iceoryx_examples/request_response/README.md +++ b/iceoryx_examples/request_response/README.md @@ -63,7 +63,7 @@ iox::runtime::PoshRuntime::initRuntime(APP_NAME); ``` After creating the runtime, the client is created and attached to the WaitSet. -The [options](https://iceoryx.io/latest/getting-started/examples/iceoptions/) can be used to alter the behavior of the client, like setting the response +The [options](https://iceoryx.io/v2.0.0/getting-started/examples/iceoptions/) can be used to alter the behavior of the client, like setting the response queue capacity or blocking behavior when the response queue is full or the server is too slow. The `ClientOptions` are similar to `PublisherOptions`/`SubscriberOptions`. diff --git a/iceoryx_examples/request_response_in_c/README.md b/iceoryx_examples/request_response_in_c/README.md index 18c28620ba..c3fb42a759 100644 --- a/iceoryx_examples/request_response_in_c/README.md +++ b/iceoryx_examples/request_response_in_c/README.md @@ -62,7 +62,7 @@ When the users presses Control+C the term signal is emitted and `keepRunning` is `false` via the signal handler callback. We start by loaning memory to send out our `request`. - + ```c struct AddRequest* request = NULL; enum iox_AllocationResult loanResult = @@ -72,7 +72,7 @@ enum iox_AllocationResult loanResult = To set the sequence id we have to acquire the request header first from the payload. Additionally, we set `expectedResponseSequenceId` so that we can verify the response later and increment the `requestSequenceId` for the next run. - + ```c iox_request_header_t requestHeader = iox_request_header_from_payload(request); iox_request_header_set_sequence_id(requestHeader, requestSequenceId); @@ -81,7 +81,7 @@ requestSequenceId += 1; ``` Before we can send out the `request` we have to set the two fibonacci numbers. - + ```c request->augend = fibonacciLast; request->addend = fibonacciCurrent; @@ -89,11 +89,15 @@ printf("%s Send Request: %lu + %lu\n", APP_NAME, (unsigned long)fibonacciLast, (unsigned long)fibonacciCurrent); -iox_client_send(client, request); +enum iox_ClientSendResult sendResult = iox_client_send(client, request); +if (sendResult != ClientSendResult_SUCCESS) +{ + printf("Error sending Request! Error code: %d\n", sendResult); +} ``` Now we give the server a little time to process the `request`. - + ```c const uint32_t DELAY_TIME_IN_MS = 150U; sleep_for(DELAY_TIME_IN_MS); @@ -103,7 +107,7 @@ We process the `response` by acquiring it first with `iox_client_take_response`. If this is successful we verify the sequence number, adjust our fibonacci numbers and print our response to the console. When the sequence number does not fulfill our expectation we print an error message. - + ```c const struct AddResponse* response = NULL; while (iox_client_take_response(client, (const void**)&response) == ChunkReceiveResult_SUCCESS) @@ -218,7 +222,7 @@ for (uint64_t i = 0; i < numberOfNotifications; ++i) The cleanup is done when we exit our mainloop. We detach the client state from the waitset first and then deinitialize the waitset and the client. - + ```c iox_ws_detach_client_state(waitset, client, ClientState_HAS_RESPONSE); iox_ws_deinit(waitset); @@ -253,7 +257,7 @@ We require the `request` for the loan so that the `response` can be delivered to the corresponding client. When the `iox_server_loan_response` was successful we calculate the sum of the two received fibonacci numbers and send it. - + ```c const struct AddRequest* request = NULL; if (iox_server_take_request(server, (const void**)&request) == ServerRequestResult_SUCCESS) @@ -270,11 +274,15 @@ if (iox_server_take_request(server, (const void**)&request) == ServerRequestResu { response->sum = request->augend + request->addend; printf("%s Send Response: %lu\n", APP_NAME, (unsigned long)response->sum); - iox_server_send(server, response); + enum iox_ServerSendResult sendResult = iox_server_send(server, response); + if (sendResult != ServerSendResult_SUCCESS) + { + printf("Error sending Response! Error code: %d\n", sendResult); + } } else { - printf("%s Could not allocate Response! Return value = %d\n", APP_NAME, loanResult); + printf("%s Could not allocate Response! Error code: %d\n", APP_NAME, loanResult); } iox_server_release_request(server, request); @@ -354,6 +362,7 @@ void onRequestReceived(iox_server_t server) if (loanResult == AllocationResult_SUCCESS) { response->sum = request->augend + request->addend; + printf("%s Send Response: %lu\n", APP_NAME, (unsigned long)response->sum); enum iox_ServerSendResult sendResult = iox_server_send(server, response); if (sendResult != ServerSendResult_SUCCESS) { @@ -362,7 +371,7 @@ void onRequestReceived(iox_server_t server) } else { - printf("Could not allocate Response! Return value = %d\n", loanResult); + printf("Could not allocate Response! Error code: %d\n", loanResult); } iox_server_release_request(server, request); } diff --git a/iceoryx_examples/singleprocess/README.md b/iceoryx_examples/singleprocess/README.md index 954b11a7c2..9d67d7ff72 100644 --- a/iceoryx_examples/singleprocess/README.md +++ b/iceoryx_examples/singleprocess/README.md @@ -42,7 +42,7 @@ iox::roudi::IceOryxRouDiComponents roudiComponents(defaultRouDiConfig); ``` 3. We are starting RouDi, provide the required components and - disable monitoring. The last bool parameter `TERMINATE_APP_IN_ROUDI_DTOR_FLAG` + disable monitoring. The last bool parameter `TERMINATE_APP_IN_ROUDI_DTOR_FLAG` states that RouDi does not terminate all registered processes when RouDi goes out of scope. If we would set it to `true`, our application would self terminate in the destructor of `roudi`. @@ -130,7 +130,7 @@ options.historyRequest = 5U; iox::popo::Subscriber subscriber({"Single", "Process", "Demo"}, options); ``` -Now we can receive the data in a while loop when our `SubscribeState` is `SUBSCRIBED` +Now we can receive the data in a while loop when our `SubscribeState` is `SUBSCRIBED` until someone terminates the application. diff --git a/iceoryx_examples/user_header/README.md b/iceoryx_examples/user_header/README.md index 8cac2f86d7..ecd47e5702 100644 --- a/iceoryx_examples/user_header/README.md +++ b/iceoryx_examples/user_header/README.md @@ -295,3 +295,6 @@ if (iox_sub_take_chunk(subscriber, &userPayload) == ChunkReceiveResult_SUCCESS) iox_sub_release_chunk(subscriber, userPayload); } ``` +
+[Check out User-Header on GitHub :fontawesome-brands-github:](https://github.com/eclipse-iceoryx/iceoryx/tree/master/iceoryx_examples/user_header){ .md-button } +
diff --git a/iceoryx_examples/waitset_in_c/README.md b/iceoryx_examples/waitset_in_c/README.md index 89aaf7ab90..e92899f65c 100644 --- a/iceoryx_examples/waitset_in_c/README.md +++ b/iceoryx_examples/waitset_in_c/README.md @@ -2,7 +2,7 @@ ## Thread Safety -The WaitSet is **not** thread-safe! +The _WaitSet_ is **not** thread-safe! - It is **not** allowed to attach or detach _Triggerable_ classes with `iox_ws_attach_**` or `iox_ws_detach_**` when another thread is currently @@ -41,15 +41,16 @@ To run an example you need a running `iox-roudi` and the waitset publisher Let's say we would like to write a gateway and would like to forward every incoming message from a subscriber with the same callback. For instance we could perform a memcopy of the received data into a specific struct. Additionally, we would -like to count all processed samples therefor we provide an extra void pointer +like to count all processed samples. Therefore we provide an extra void pointer argument called `contextData` which is a pointer to an `uint64_t`. This could be performed by a function that we attach to an event as a callback. In our case, we have the function `subscriberCallback` that prints out the subscriber pointer and the content of the received sample. + ```c -void subscriberCallback(iox_sub_t const subscriber, void * const contextData) +void subscriberCallback(iox_sub_t const subscriber, void* const contextData) { if (contextData == NULL) { @@ -58,7 +59,7 @@ void subscriberCallback(iox_sub_t const subscriber, void * const contextData) } uint64_t* sumOfAllSamples = (uint64_t*)contextData; - const void* userPayload; + const void* userPayload = NULL; while (iox_sub_take_chunk(subscriber, &userPayload) == ChunkReceiveResult_SUCCESS) { printf("subscriber: %p received %u\n", (void*)subscriber, ((struct CounterTopic*)userPayload)->counter); @@ -72,11 +73,12 @@ void subscriberCallback(iox_sub_t const subscriber, void * const contextData) The `shutdownTrigger` gets a simplified callback where it just states that the program will be terminated. For this we do not need any context data. + ```c void shutdownCallback(iox_user_trigger_t userTrigger) { (void)userTrigger; - printf("CTRL+c pressed - exiting now\n"); + printf("CTRL+C pressed - exiting now\n"); fflush(stdout); } ``` @@ -87,8 +89,9 @@ One will never miss chunks since the event notification is reset after a call to `iox_ws_wait` or `iox_ws_timed_wait` which we introduce below. After we registered our runtime we set up some `waitSetStorage`, initialize the _WaitSet_ -and attach a `shutdownTrigger` to handle `CTRL-c`. +and attach a `shutdownTrigger` to handle `CTRL+C`. + ```c iox_runtime_init("iox-c-waitset-gateway"); @@ -96,12 +99,17 @@ iox_ws_storage_t waitSetStorage; iox_ws_t waitSet = iox_ws_init(&waitSetStorage); shutdownTrigger = iox_user_trigger_init(&shutdownTriggerStorage); +// attach shutdownTrigger with no callback to handle CTRL+C iox_ws_attach_user_trigger_event(waitSet, shutdownTrigger, 0U, shutdownCallback); + +// register signal after shutdownTrigger since we are using it in the handler +signal(SIGINT, sigHandler); +signal(SIGTERM, sigHandler); ``` In the next steps, we define `sumOfAllSamples`, create two subscribers with `iox_sub_init`, subscribe them to our topic -and attach the event `SubscriberEvent_DATA_RECEIVED` to the WaitSet with +and attach the event `SubscriberEvent_DATA_RECEIVED` to the _WaitSet_ with the `subscriberCallback`, an event id `1U` and a pointer to our user defined context data `sumOfAllSamples` which is then provided as argument for the callback. @@ -111,12 +119,15 @@ context data `sumOfAllSamples` which is then provided as argument for the callba attachment, with its callback, is attached otherwise the callback context data pointer is dangling. + ```c uint64_t sumOfAllSamples = 0U; +// array where the subscriber are stored iox_sub_storage_t subscriberStorage[NUMBER_OF_SUBSCRIBERS]; iox_sub_t subscriber[NUMBER_OF_SUBSCRIBERS]; +// create subscriber and subscribe them to our service iox_sub_options_t options; iox_sub_options_init(&options); options.historyRequest = 1U; @@ -135,23 +146,27 @@ Now that everything is set up we enter the event loop. It always starts with a call to `iox_ws_wait`, a blocking call which returns us the number of occurred notifications. + ```c uint64_t missedElements = 0U; uint64_t numberOfNotifications = 0U; +// array where all notification infos from iox_ws_wait will be stored iox_notification_info_t notificationArray[NUMBER_OF_NOTIFICATIONS]; bool keepRunning = true; while (keepRunning) { - numberOfNotifications = - iox_ws_wait(waitSet, notificationArray, NUMBER_OF_NOTIFICATIONS, &missedElements); + numberOfNotifications = iox_ws_wait(waitSet, notificationArray, NUMBER_OF_NOTIFICATIONS, &missedElements); + // ... +} ``` The events which have occurred are stored in the `notificationArray`. We iterate through it, if the `shutdownTrigger` was triggered we terminate the program otherwise we call the callback with `iox_notification_info_call(notification)`. + ```c for (uint64_t i = 0U; i < numberOfNotifications; ++i) { @@ -159,23 +174,28 @@ for (uint64_t i = 0U; i < numberOfNotifications; ++i) if (iox_notification_info_does_originate_from_user_trigger(notification, shutdownTrigger)) { + // CTRL+C was pressed -> exit keepRunning = false; } else { + // call the callback which was assigned to the event iox_notification_info_call(notification); - } - printf("sum of all samples: %lu\n", sumOfAllSamples); - fflush(stdout); + printf("sum of all samples: %lu\n", (unsigned long)sumOfAllSamples); + fflush(stdout); + } } ``` Before we can close the program, we cleanup all resources. + ```c for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) { + // not mandatory since iox_sub_deinit will detach the subscriber automatically + // only added to present the full API iox_ws_detach_subscriber_event(waitSet, subscriber[i], SubscriberEvent_DATA_RECEIVED); iox_sub_deinit(subscriber[i]); } @@ -190,9 +210,10 @@ In this scenario, we have two groups of subscribers. We are interested in the data of the first group and would like to print them onto the console and the data of the second group should be discarded. -We start like in every example with creating the WaitSet and attaching the +We start like in every example with creating the _WaitSet_ and attaching the `shutdownTrigger`. + ```c iox_runtime_init("iox-c-waitset-grouping"); @@ -200,15 +221,23 @@ iox_ws_storage_t waitSetStorage; iox_ws_t waitSet = iox_ws_init(&waitSetStorage); shutdownTrigger = iox_user_trigger_init(&shutdownTriggerStorage); +// attach shutdownTrigger with no callback to handle CTRL+C iox_ws_attach_user_trigger_event(waitSet, shutdownTrigger, 0U, NULL); + +// register signal after shutdownTrigger since we are using it in the handler +signal(SIGINT, sigHandler); +signal(SIGTERM, sigHandler); ``` After that we can create a list of subscribers and subscribe them to our topic. + ```c +// array where the subscribers are stored iox_sub_storage_t subscriberStorage[NUMBER_OF_SUBSCRIBERS]; iox_sub_t subscriber[NUMBER_OF_SUBSCRIBERS]; +// create subscriber and subscribe them to our service iox_sub_options_t options; iox_sub_options_init(&options); options.historyRequest = 1U; @@ -217,7 +246,6 @@ options.nodeName = "iox-c-waitset-grouping-node"; for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) { subscriber[i] = iox_sub_init(&(subscriberStorage[i]), "Radar", "FrontLeft", "Counter", &options); - } ``` @@ -227,15 +255,18 @@ the `SubscriberState_HAS_DATA` state and the event id of the first group to our The third and forth subscriber are attached to the same waitset under the second group id. + ```c -const uint64_t FIRST_GROUP_ID = 123; -const uint64_t SECOND_GROUP_ID = 456; +const uint64_t FIRST_GROUP_ID = 123U; +const uint64_t SECOND_GROUP_ID = 456U; +// attach the first two subscribers to the waitset with a triggerid of FIRST_GROUP_ID for (uint64_t i = 0U; i < 2U; ++i) { iox_ws_attach_subscriber_state(waitSet, subscriber[i], SubscriberState_HAS_DATA, FIRST_GROUP_ID, NULL); } +// attach the remaining subscribers to the waitset with a triggerid of SECOND_GROUP_ID for (uint64_t i = 2U; i < 4U; ++i) { iox_ws_attach_subscriber_state(waitSet, subscriber[i], SubscriberState_HAS_DATA, SECOND_GROUP_ID, NULL); @@ -245,12 +276,14 @@ for (uint64_t i = 2U; i < 4U; ++i) We are again ready for our event loop. We start as usual by setting the array of notifications by calling `iox_ws_wait`. + ```c bool keepRunning = true; while (keepRunning) { - numberOfNotifications = - iox_ws_wait(waitSet, notificationArray, NUMBER_OF_NOTIFICATIONS, &missedElements); + numberOfNotifications = iox_ws_wait(waitSet, notificationArray, NUMBER_OF_NOTIFICATIONS, &missedElements); + // ... +} ``` When we iterate through the array we handle the `shutdownTrigger` first. @@ -262,30 +295,38 @@ sample and to print the result to the console. The second group is handled in the same way. But we do not print the new samples to screen, we just discard them. + ```c for (uint64_t i = 0U; i < numberOfNotifications; ++i) { - iox_notification_info_t event = notificationArray[i]; + iox_notification_info_t notification = notificationArray[i]; - if (iox_notification_info_does_originate_from_user_trigger(event, shutdownTrigger)) + if (iox_notification_info_does_originate_from_user_trigger(notification, shutdownTrigger)) { + // CTRL+C was pressed -> exit keepRunning = false; } - else if (iox_notification_info_get_event_id(event) == FIRST_GROUP_ID) + // we print the received data for the first group + else if (iox_notification_info_get_notification_id(notification) == FIRST_GROUP_ID) { - iox_sub_t subscriber = iox_notification_info_get_subscriber_origin(event); + iox_sub_t subscriber = iox_notification_info_get_subscriber_origin(notification); const void* userPayload; if (iox_sub_take_chunk(subscriber, &userPayload)) { printf("received: %u\n", ((struct CounterTopic*)userPayload)->counter); + fflush(stdout); iox_sub_release_chunk(subscriber, userPayload); } } - else if (iox_notification_info_get_event_id(event) == SECOND_GROUP_ID) + // dismiss the received data for the second group + else if (iox_notification_info_get_notification_id(notification) == SECOND_GROUP_ID) { printf("dismiss data\n"); - iox_sub_t subscriber = iox_notification_info_get_subscriber_origin(event); + iox_sub_t subscriber = iox_notification_info_get_subscriber_origin(notification); + // We need to release the samples to reset the event hasSamples + // otherwise the WaitSet would notify us in `iox_ws_wait()` again + // instantly. iox_sub_release_queued_chunks(subscriber); } } @@ -297,6 +338,7 @@ persists. The last thing we have to do is to cleanup all the acquired resources. + ```c for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) { @@ -315,8 +357,9 @@ One way would be to assign every subscriber a different callback, here we look at a different approach. We check if the event originated from a specific subscriber and then perform the calls on that subscriber directly. -We start as usual by creating a WaitSet and attach the `shutdownTrigger` to it. +We start as usual by creating a _WaitSet_ and attach the `shutdownTrigger` to it. + ```c iox_runtime_init("iox-c-waitset-individual"); @@ -324,13 +367,24 @@ iox_ws_storage_t waitSetStorage; iox_ws_t waitSet = iox_ws_init(&waitSetStorage); shutdownTrigger = iox_user_trigger_init(&shutdownTriggerStorage); +// attach shutdownTrigger with no callback to handle CTRL+C iox_ws_attach_user_trigger_event(waitSet, shutdownTrigger, 0U, NULL); + +// register signal after shutdownTrigger since we are using it in the handler +signal(SIGINT, sigHandler); +signal(SIGTERM, sigHandler); ``` Now we create two subscribers, subscribe them to our topic and attach them to the waitset without a callback and with the same trigger id. + ```c +// array where the subscriber are stored +iox_sub_storage_t subscriberStorage[NUMBER_OF_SUBSCRIBERS]; +iox_sub_t subscriber[NUMBER_OF_SUBSCRIBERS]; + +// create two subscribers, subscribe to the service and attach them to the waitset iox_sub_options_t options; iox_sub_options_init(&options); options.historyRequest = 1U; @@ -338,6 +392,7 @@ options.queueCapacity = 256U; options.nodeName = "iox-c-waitset-individual-node1"; subscriber[0] = iox_sub_init(&(subscriberStorage[0]), "Radar", "FrontLeft", "Counter", &options); + options.nodeName = "iox-c-waitset-individual-node2"; subscriber[1] = iox_sub_init(&(subscriberStorage[1]), "Radar", "FrontLeft", "Counter", &options); @@ -348,12 +403,20 @@ iox_ws_attach_subscriber_state(waitSet, subscriber[1U], SubscriberState_HAS_DATA We are ready to start the event loop. We begin by acquiring the array of all the triggered triggers. + ```c +uint64_t missedElements = 0U; +uint64_t numberOfNotifications = 0U; + +// array where all notification infos from iox_ws_wait will be stored +iox_notification_info_t notificationArray[NUMBER_OF_NOTIFICATIONS]; + bool keepRunning = true; while (keepRunning) { - numberOfNotifications = - iox_ws_wait(waitSet, notificationArray, NUMBER_OF_NOTIFICATIONS, &missedElements); + numberOfNotifications = iox_ws_wait(waitSet, notificationArray, NUMBER_OF_NOTIFICATIONS, &missedElements); + // ... +} ``` The `shutdownTrigger` is handled as usual and @@ -362,35 +425,45 @@ to identify the event that originated from a specific subscriber. If it originat from the first subscriber we print the received data to the console, if it originated from the second subscriber we discard the data. + ```c - for (uint64_t i = 0U; i < numberOfNotifications; ++i) - { - iox_notification_info_t event = notificationArray[i]; +for (uint64_t i = 0U; i < numberOfNotifications; ++i) +{ + iox_notification_info_t notification = notificationArray[i]; - if (iox_notification_info_does_originate_from_user_trigger(event, shutdownTrigger)) - { - keepRunning = false; - } - else if (iox_notification_info_does_originate_from_subscriber(event, subscriber[0])) + if (iox_notification_info_does_originate_from_user_trigger(notification, shutdownTrigger)) + { + // CTRL+C was pressed -> exit + keepRunning = false; + } + // process sample received by subscriber1 + else if (iox_notification_info_does_originate_from_subscriber(notification, subscriber[0U])) + { + const void* userPayload; + if (iox_sub_take_chunk(subscriber[0U], &userPayload)) { - const void* userPayload; - if (iox_sub_take_chunk(subscriber[0], &userPayload)) - { - printf("subscriber 1 received: %u\n", ((struct CounterTopic*)userPayload)->counter); + printf("subscriber 1 received: %u\n", ((struct CounterTopic*)userPayload)->counter); + fflush(stdout); - iox_sub_release_chunk(subscriber[0], userPayload); - } - } - else if (iox_notification_info_does_originate_from_subscriber(event, subscriber[1])) - { - iox_sub_release_queued_chunks(subscriber[1]); - printf("subscriber 2 received something - dont care\n"); + iox_sub_release_chunk(subscriber[0U], userPayload); } } + // dismiss sample received by subscriber2 + else if (iox_notification_info_does_originate_from_subscriber(notification, subscriber[1])) + { + // We need to release the samples to reset the event hasSamples + // otherwise the WaitSet would notify us in `iox_ws_wait()` again + // instantly. + iox_sub_release_queued_chunks(subscriber[1U]); + printf("subscriber 2 received something - dont care\n"); + fflush(stdout); + } +} ``` We conclude the example as always, by cleaning up the resources. + ```c for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) { @@ -402,14 +475,19 @@ iox_user_trigger_deinit(shutdownTrigger); ``` ### Timer Driven Execution -In this example, we demonstrate how you can use the WaitSet to trigger a cyclic + +In this example, we demonstrate how you can use the _WaitSet_ to trigger a cyclic call every second. We use a user trigger which will be triggered in a separate -thread every second to signal the WaitSet that it's time for the next run. +thread every second to signal the _WaitSet_ that it's time for the next run. Additionally, we attach a callback (`cyclicRun`) to this user trigger so that the event can directly call the cyclic call. -We begin by creating the waitset and attach the `shutdownTrigger`. +!!! note + This example does not run on Windows due to direct usage of the `pthread` API. +We begin by creating the _WaitSet_ and attach the `shutdownTrigger`. + + ```c iox_runtime_init("iox-c-waitset-timer-driven-execution"); @@ -417,12 +495,18 @@ iox_ws_storage_t waitSetStorage; iox_ws_t waitSet = iox_ws_init(&waitSetStorage); shutdownTrigger = iox_user_trigger_init(&shutdownTriggerStorage); +// attach shutdownTrigger with no callback to handle CTRL+C iox_ws_attach_user_trigger_event(waitSet, shutdownTrigger, 0, NULL); + +// register signal after shutdownTrigger since we are using it in the handler +signal(SIGINT, sigHandler); +signal(SIGTERM, sigHandler); ``` Now we create our cyclic trigger and attach it to our waitset with an eventId of `0` and the callback `cyclicRun`. + ```c cyclicTrigger = iox_user_trigger_init(&cyclicTriggerStorage); iox_ws_attach_user_trigger_event(waitSet, cyclicTrigger, 0, cyclicRun); @@ -431,9 +515,10 @@ iox_ws_attach_user_trigger_event(waitSet, cyclicTrigger, 0, cyclicRun); The thread which will trigger the `cyclicTrigger` every second is started in the next lines. + ```c pthread_t cyclicTriggerThread; -if (pthread_create(&cyclicTriggerThread, NULL, cyclicTriggerCallback, NULL)) +if (createThread(&cyclicTriggerThread, cyclicTriggerCallback)) { printf("failed to create thread\n"); return -1; @@ -443,40 +528,50 @@ if (pthread_create(&cyclicTriggerThread, NULL, cyclicTriggerCallback, NULL)) Everything is prepared and we enter the event loop. We start by gathering all notifications in an array. + ```c +uint64_t missedElements = 0U; +uint64_t numberOfNotifications = 0U; + +// array where all notifications from iox_ws_wait will be stored +iox_notification_info_t notificationArray[NUMBER_OF_NOTIFICATIONS]; + while (keepRunning) { - numberOfNotifications = - iox_ws_wait(waitSet, notificationArray, NUMBER_OF_NOTIFICATIONS, &missedElements); + numberOfNotifications = iox_ws_wait(waitSet, notificationArray, NUMBER_OF_NOTIFICATIONS, &missedElements); + // ... +} ``` The `shutdownTrigger` is handled as usual and the `cyclicTrigger` is handled by just calling the attached callback with `iox_notification_info_call(notification)`. + ```c - for (uint64_t i = 0U; i < numberOfNotifications; ++i) - { - iox_notification_info_t notification = notificationArray[i]; +for (uint64_t i = 0U; i < numberOfNotifications; ++i) +{ + iox_notification_info_t notification = notificationArray[i]; - if (iox_notification_info_does_originate_from_user_trigger(notification, shutdownTrigger)) - { - // CTRL+c was pressed -> exit - keepRunning = false; - } - else - { - // call myCyclicRun - iox_notification_info_call(notification); - } + if (iox_notification_info_does_originate_from_user_trigger(notification, shutdownTrigger)) + { + // CTRL+C was pressed -> exit + keepRunning = false; + } + else + { + // call myCyclicRun + iox_notification_info_call(notification); } +} ``` The last thing we have to do is to cleanup all the used resources. + ```c - pthread_join(cyclicTriggerThread, NULL); - iox_ws_deinit(waitSet); - iox_user_trigger_deinit(shutdownTrigger); +joinThread(cyclicTriggerThread); +iox_ws_deinit(waitSet); +iox_user_trigger_deinit(shutdownTrigger); ```
diff --git a/iceoryx_examples/waitset_in_c/ice_c_waitset_gateway.c b/iceoryx_examples/waitset_in_c/ice_c_waitset_gateway.c index 2f2f52d376..86b6187d2e 100644 --- a/iceoryx_examples/waitset_in_c/ice_c_waitset_gateway.c +++ b/iceoryx_examples/waitset_in_c/ice_c_waitset_gateway.c @@ -42,16 +42,19 @@ static void sigHandler(int signalValue) iox_user_trigger_trigger(shutdownTrigger); } +//! [shutdown callback] void shutdownCallback(iox_user_trigger_t userTrigger) { (void)userTrigger; - printf("CTRL+c pressed - exiting now\n"); + printf("CTRL+C pressed - exiting now\n"); fflush(stdout); } +//! [shutdown callback] // The callback of the trigger. Every callback must have an argument which is // a pointer to the origin of the Trigger. In our case the trigger origin is // an iox_sub_t. +//! [subscriber callback] void subscriberCallback(iox_sub_t const subscriber, void* const contextData) { if (contextData == NULL) @@ -71,9 +74,11 @@ void subscriberCallback(iox_sub_t const subscriber, void* const contextData) ++(*sumOfAllSamples); } } +//! [subscriber callback] int main() { + //! [initialization and shutdown handling] iox_runtime_init("iox-c-waitset-gateway"); iox_ws_storage_t waitSetStorage; @@ -83,10 +88,12 @@ int main() // attach shutdownTrigger with no callback to handle CTRL+C iox_ws_attach_user_trigger_event(waitSet, shutdownTrigger, 0U, shutdownCallback); - //// register signal after shutdownTrigger since we are using it in the handler + // register signal after shutdownTrigger since we are using it in the handler signal(SIGINT, sigHandler); signal(SIGTERM, sigHandler); + //! [initialization and shutdown handling] + //! [create and attach subscriber] uint64_t sumOfAllSamples = 0U; // array where the subscriber are stored @@ -106,41 +113,44 @@ int main() iox_ws_attach_subscriber_event_with_context_data( waitSet, subscriber[i], SubscriberEvent_DATA_RECEIVED, 1U, subscriberCallback, &sumOfAllSamples); } + //! [create and attach subscriber] - + //! [event loop] uint64_t missedElements = 0U; uint64_t numberOfNotifications = 0U; // array where all notification infos from iox_ws_wait will be stored iox_notification_info_t notificationArray[NUMBER_OF_NOTIFICATIONS]; - // event loop bool keepRunning = true; while (keepRunning) { numberOfNotifications = iox_ws_wait(waitSet, notificationArray, NUMBER_OF_NOTIFICATIONS, &missedElements); + //! [handle events] for (uint64_t i = 0U; i < numberOfNotifications; ++i) { iox_notification_info_t notification = notificationArray[i]; if (iox_notification_info_does_originate_from_user_trigger(notification, shutdownTrigger)) { - // CTRL+c was pressed -> exit + // CTRL+C was pressed -> exit keepRunning = false; } else { // call the callback which was assigned to the event iox_notification_info_call(notification); + + printf("sum of all samples: %lu\n", (unsigned long)sumOfAllSamples); + fflush(stdout); } } - - printf("sum of all samples: %lu\n", (unsigned long)sumOfAllSamples); - fflush(stdout); + //! [handle events] } + //! [event loop] - // cleanup all resources + //! [cleanup all resources] for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) { // not mandatory since iox_sub_deinit will detach the subscriber automatically @@ -151,7 +161,7 @@ int main() iox_ws_deinit(waitSet); iox_user_trigger_deinit(shutdownTrigger); - + //! [cleanup all resources] return 0; } diff --git a/iceoryx_examples/waitset_in_c/ice_c_waitset_grouping.c b/iceoryx_examples/waitset_in_c/ice_c_waitset_grouping.c index 22087b587f..f844dbd775 100644 --- a/iceoryx_examples/waitset_in_c/ice_c_waitset_grouping.c +++ b/iceoryx_examples/waitset_in_c/ice_c_waitset_grouping.c @@ -44,6 +44,7 @@ static void sigHandler(int signalValue) int main() { + //! [initialization and shutdown handling] iox_runtime_init("iox-c-waitset-grouping"); iox_ws_storage_t waitSetStorage; @@ -53,11 +54,13 @@ int main() // attach shutdownTrigger with no callback to handle CTRL+C iox_ws_attach_user_trigger_event(waitSet, shutdownTrigger, 0U, NULL); - //// register signal after shutdownTrigger since we are using it in the handler + // register signal after shutdownTrigger since we are using it in the handler signal(SIGINT, sigHandler); signal(SIGTERM, sigHandler); + //! [initialization and shutdown handling] - // array where the subscriber are stored + //! [create subscriber] + // array where the subscribers are stored iox_sub_storage_t subscriberStorage[NUMBER_OF_SUBSCRIBERS]; iox_sub_t subscriber[NUMBER_OF_SUBSCRIBERS]; @@ -71,22 +74,24 @@ int main() { subscriber[i] = iox_sub_init(&(subscriberStorage[i]), "Radar", "FrontLeft", "Counter", &options); } + //! [create subscriber] + //! [attach subscriber] const uint64_t FIRST_GROUP_ID = 123U; const uint64_t SECOND_GROUP_ID = 456U; - // attach the first two subscriber to waitset with a triggerid of FIRST_GROUP_ID + // attach the first two subscribers to the waitset with a triggerid of FIRST_GROUP_ID for (uint64_t i = 0U; i < 2U; ++i) { iox_ws_attach_subscriber_state(waitSet, subscriber[i], SubscriberState_HAS_DATA, FIRST_GROUP_ID, NULL); } - // attach the remaining subscribers to waitset with a triggerid of SECOND_GROUP_ID + // attach the remaining subscribers to the waitset with a triggerid of SECOND_GROUP_ID for (uint64_t i = 2U; i < 4U; ++i) { iox_ws_attach_subscriber_state(waitSet, subscriber[i], SubscriberState_HAS_DATA, SECOND_GROUP_ID, NULL); } - + //! [attach subscriber] uint64_t missedElements = 0U; uint64_t numberOfNotifications = 0U; @@ -94,19 +99,20 @@ int main() // array where all notification infos from iox_ws_wait will be stored iox_notification_info_t notificationArray[NUMBER_OF_NOTIFICATIONS]; - // event loop + //! [event loop] bool keepRunning = true; while (keepRunning) { numberOfNotifications = iox_ws_wait(waitSet, notificationArray, NUMBER_OF_NOTIFICATIONS, &missedElements); + //! [handle events] for (uint64_t i = 0U; i < numberOfNotifications; ++i) { iox_notification_info_t notification = notificationArray[i]; if (iox_notification_info_does_originate_from_user_trigger(notification, shutdownTrigger)) { - // CTRL+c was pressed -> exit + // CTRL+C was pressed -> exit keepRunning = false; } // we print the received data for the first group @@ -133,9 +139,11 @@ int main() iox_sub_release_queued_chunks(subscriber); } } + //! [handle events] } + //! [event loop] - // cleanup all resources + //! [cleanup all resources] for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) { iox_sub_deinit(subscriber[i]); @@ -143,7 +151,7 @@ int main() iox_ws_deinit(waitSet); iox_user_trigger_deinit(shutdownTrigger); - + //! [cleanup all resources] return 0; } diff --git a/iceoryx_examples/waitset_in_c/ice_c_waitset_individual.c b/iceoryx_examples/waitset_in_c/ice_c_waitset_individual.c index 462a58e5a7..d420ee79b0 100644 --- a/iceoryx_examples/waitset_in_c/ice_c_waitset_individual.c +++ b/iceoryx_examples/waitset_in_c/ice_c_waitset_individual.c @@ -44,6 +44,7 @@ static void sigHandler(int signalValue) int main() { + //! [initialization and shutdown handling] iox_runtime_init("iox-c-waitset-individual"); iox_ws_storage_t waitSetStorage; @@ -53,10 +54,12 @@ int main() // attach shutdownTrigger with no callback to handle CTRL+C iox_ws_attach_user_trigger_event(waitSet, shutdownTrigger, 0U, NULL); - //// register signal after shutdownTrigger since we are using it in the handler + // register signal after shutdownTrigger since we are using it in the handler signal(SIGINT, sigHandler); signal(SIGTERM, sigHandler); + //! [initialization and shutdown handling] + //! [create and attach subscriber] // array where the subscriber are stored iox_sub_storage_t subscriberStorage[NUMBER_OF_SUBSCRIBERS]; iox_sub_t subscriber[NUMBER_OF_SUBSCRIBERS]; @@ -75,27 +78,28 @@ int main() iox_ws_attach_subscriber_state(waitSet, subscriber[0U], SubscriberState_HAS_DATA, 0U, NULL); iox_ws_attach_subscriber_state(waitSet, subscriber[1U], SubscriberState_HAS_DATA, 0U, NULL); + //! [create and attach subscriber] - + //! [event loop] uint64_t missedElements = 0U; uint64_t numberOfNotifications = 0U; // array where all notification infos from iox_ws_wait will be stored iox_notification_info_t notificationArray[NUMBER_OF_NOTIFICATIONS]; - // event loop bool keepRunning = true; while (keepRunning) { numberOfNotifications = iox_ws_wait(waitSet, notificationArray, NUMBER_OF_NOTIFICATIONS, &missedElements); + //! [handle events] for (uint64_t i = 0U; i < numberOfNotifications; ++i) { iox_notification_info_t notification = notificationArray[i]; if (iox_notification_info_does_originate_from_user_trigger(notification, shutdownTrigger)) { - // CTRL+c was pressed -> exit + // CTRL+C was pressed -> exit keepRunning = false; } // process sample received by subscriber1 @@ -121,9 +125,11 @@ int main() fflush(stdout); } } + //! [handle events] } + //! [event loop] - // cleanup all resources + //! [cleanup all resources] for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) { iox_sub_deinit(subscriber[i]); @@ -131,7 +137,7 @@ int main() iox_ws_deinit(waitSet); iox_user_trigger_deinit(shutdownTrigger); - + //! [cleanup all resources] return 0; } diff --git a/iceoryx_examples/waitset_in_c/ice_c_waitset_timer_driven_execution.c b/iceoryx_examples/waitset_in_c/ice_c_waitset_timer_driven_execution.c index dbed53432c..6cf61955cf 100644 --- a/iceoryx_examples/waitset_in_c/ice_c_waitset_timer_driven_execution.c +++ b/iceoryx_examples/waitset_in_c/ice_c_waitset_timer_driven_execution.c @@ -23,7 +23,9 @@ #include "sleep_for.h" #include "topic_data.h" -#if !defined(_WIN32) +#if defined(_WIN32) +typedef long unsigned int pthread_t; +#else #include #endif #include @@ -67,14 +69,34 @@ void* cyclicTriggerCallback(void* dontCare) return NULL; } +bool createThread(pthread_t* threadHandle, void* (*callback)(void*)) +{ +#if defined(_WIN32) + return -1; +#else + return pthread_create(threadHandle, NULL, callback, NULL); +#endif +} + +int joinThread(pthread_t threadHandle) +{ +#if defined(_WIN32) + return -1; +#else + return pthread_join(threadHandle, NULL); +#endif +} + int main() { #if defined(_WIN32) printf("This example does not work on Windows. But you can easily adapt it for now by starting a windows thread " "which triggers the cyclicTrigger every second.\n"); + return -1; #endif - iox_runtime_init("iox-c-waitset-sync"); + //! [initialization and shutdown handling] + iox_runtime_init("iox-c-waitset-timer-driven-execution"); iox_ws_storage_t waitSetStorage; iox_ws_t waitSet = iox_ws_init(&waitSetStorage); @@ -83,44 +105,47 @@ int main() // attach shutdownTrigger with no callback to handle CTRL+C iox_ws_attach_user_trigger_event(waitSet, shutdownTrigger, 0, NULL); - //// register signal after shutdownTrigger since we are using it in the handler + // register signal after shutdownTrigger since we are using it in the handler signal(SIGINT, sigHandler); signal(SIGTERM, sigHandler); - + //! [initialization and shutdown handling] // create and attach the cyclicTrigger with a callback to // myCyclicRun + //! [cyclic trigger] cyclicTrigger = iox_user_trigger_init(&cyclicTriggerStorage); iox_ws_attach_user_trigger_event(waitSet, cyclicTrigger, 0, cyclicRun); + //! [cyclic trigger] // start a thread which triggers cyclicTrigger every second -#if !defined(_WIN32) + //! [cyclic trigger thread] pthread_t cyclicTriggerThread; - if (pthread_create(&cyclicTriggerThread, NULL, cyclicTriggerCallback, NULL)) + if (createThread(&cyclicTriggerThread, cyclicTriggerCallback)) { printf("failed to create thread\n"); return -1; } -#endif + //! [cyclic trigger thread] + //! [event loop] uint64_t missedElements = 0U; uint64_t numberOfNotifications = 0U; // array where all notifications from iox_ws_wait will be stored iox_notification_info_t notificationArray[NUMBER_OF_NOTIFICATIONS]; - // event loop while (keepRunning) { numberOfNotifications = iox_ws_wait(waitSet, notificationArray, NUMBER_OF_NOTIFICATIONS, &missedElements); + //! [handle events] for (uint64_t i = 0U; i < numberOfNotifications; ++i) { iox_notification_info_t notification = notificationArray[i]; if (iox_notification_info_does_originate_from_user_trigger(notification, shutdownTrigger)) { - // CTRL+c was pressed -> exit + // CTRL+C was pressed -> exit keepRunning = false; } else @@ -129,15 +154,15 @@ int main() iox_notification_info_call(notification); } } + //! [handle events] } + //! [event loop] - // cleanup all resources -#if !defined(_WIN32) - pthread_join(cyclicTriggerThread, NULL); -#endif + //! [cleanup all resources] + joinThread(cyclicTriggerThread); iox_ws_deinit(waitSet); iox_user_trigger_deinit(shutdownTrigger); - + //! [cleanup all resources] return 0; } diff --git a/iceoryx_hoofs/CMakeLists.txt b/iceoryx_hoofs/CMakeLists.txt index f675022e67..a0ba120e74 100644 --- a/iceoryx_hoofs/CMakeLists.txt +++ b/iceoryx_hoofs/CMakeLists.txt @@ -17,7 +17,7 @@ cmake_minimum_required(VERSION 3.16) -set(IOX_VERSION_STRING "1.91.0") +set(IOX_VERSION_STRING "2.0.0") project(iceoryx_hoofs VERSION ${IOX_VERSION_STRING}) diff --git a/iceoryx_hoofs/package.xml b/iceoryx_hoofs/package.xml index c29c0c88a1..59f7876eb9 100644 --- a/iceoryx_hoofs/package.xml +++ b/iceoryx_hoofs/package.xml @@ -2,9 +2,9 @@ iceoryx_hoofs - 1.91.0 + 2.0.0 Eclipse iceoryx inter-process-communication (IPC) middleware basic building blocks - Eclipse Foundation, Inc. + Eclipse Foundation, Inc. Apache 2.0 https://iceoryx.io https://github.com/eclipse-iceoryx/iceoryx/issues @@ -15,6 +15,9 @@ acl libatomic + iceoryx_utils + iceoryx_utils + doxygen diff --git a/iceoryx_hoofs/platform/qnx/include/iceoryx_hoofs/platform/platform_settings.hpp b/iceoryx_hoofs/platform/qnx/include/iceoryx_hoofs/platform/platform_settings.hpp index a506bc5431..5eebde93d6 100644 --- a/iceoryx_hoofs/platform/qnx/include/iceoryx_hoofs/platform/platform_settings.hpp +++ b/iceoryx_hoofs/platform/qnx/include/iceoryx_hoofs/platform/platform_settings.hpp @@ -32,7 +32,7 @@ constexpr uint64_t IOX_MAX_PATH_LENGTH = 1023U; constexpr bool IOX_SHM_WRITE_ZEROS_ON_CREATION = true; constexpr uint64_t IOX_MAX_SHM_NAME_LENGTH = 1024U; constexpr const char IOX_PATH_SEPARATORS[] = "/"; -constexpr uint64_t IOX_UDS_SOCKET_MAX_MESSAGE_SIZE = 4096; +constexpr uint64_t IOX_UDS_SOCKET_MAX_MESSAGE_SIZE = 2048; constexpr const char IOX_UDS_SOCKET_PATH_PREFIX[] = "/tmp/"; constexpr const char IOX_LOCK_FILE_PATH_PREFIX[] = "/var/lock/"; using IoxIpcChannelType = iox::posix::UnixDomainSocket; diff --git a/iceoryx_hoofs/platform/win/include/iceoryx_hoofs/platform/handle_translator.hpp b/iceoryx_hoofs/platform/win/include/iceoryx_hoofs/platform/handle_translator.hpp index e2227ed3cf..ac9df2230b 100644 --- a/iceoryx_hoofs/platform/win/include/iceoryx_hoofs/platform/handle_translator.hpp +++ b/iceoryx_hoofs/platform/win/include/iceoryx_hoofs/platform/handle_translator.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. +// Copyright (c) 2021 - 2022 by Apex.AI Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,7 +17,9 @@ #define IOX_HOOFS_WIN_PLATFORM_HANDLE_TRANSLATOR_HPP #include "iceoryx_hoofs/platform/windows.hpp" -#include +#include +#include +#include /// @brief In windows file handles have the type HANDLE (void*) in linux it is /// usually an int. To establish the portability we keep track of the @@ -28,14 +30,26 @@ class HandleTranslator { public: + static constexpr int INVALID_LINUX_FD = -1; + + HandleTranslator(const HandleTranslator&) = delete; + HandleTranslator(HandleTranslator&&) = delete; + HandleTranslator& operator=(const HandleTranslator&) = delete; + HandleTranslator& operator=(HandleTranslator&&) = delete; + ~HandleTranslator() = default; + static HandleTranslator& getInstance() noexcept; - HANDLE get(const int handle) const noexcept; - int add(HANDLE handle) noexcept; - void remove(const int handle) noexcept; + HANDLE get(const int linuxFd) const noexcept; + int add(HANDLE windowsHandle) noexcept; + void remove(const int linuxFd) noexcept; private: HandleTranslator() noexcept = default; - std::vector m_handleList; + + int m_currentLinuxFileHandle = 0; + mutable std::mutex m_mtx; + std::map m_linuxToWindows; + std::queue m_freeFileDescriptors; }; #endif diff --git a/iceoryx_hoofs/platform/win/include/iceoryx_hoofs/platform/ipc_handle_manager.hpp b/iceoryx_hoofs/platform/win/include/iceoryx_hoofs/platform/ipc_handle_manager.hpp index 765d75702a..7aeb512fa2 100644 --- a/iceoryx_hoofs/platform/win/include/iceoryx_hoofs/platform/ipc_handle_manager.hpp +++ b/iceoryx_hoofs/platform/win/include/iceoryx_hoofs/platform/ipc_handle_manager.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. +// Copyright (c) 2021 - 2022 by Apex.AI Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,8 +18,9 @@ #include "iceoryx_hoofs/platform/unique_system_id.hpp" #include "iceoryx_hoofs/platform/windows.hpp" -#include +#include +#include enum class OwnerShip { @@ -36,13 +37,23 @@ struct IpcHandle_t class IpcHandleManager { public: + IpcHandleManager(const IpcHandleManager&) = delete; + IpcHandleManager(IpcHandleManager&&) = delete; ~IpcHandleManager() noexcept; - bool getHandle(const UniqueSystemId& id, HANDLE& handle) noexcept; + IpcHandleManager& operator=(const IpcHandleManager&) = delete; + IpcHandleManager& operator=(IpcHandleManager&&) = delete; + + static IpcHandleManager& getInstance() noexcept; + + bool getHandle(const UniqueSystemId& id, HANDLE& handle) const noexcept; void addHandle(const UniqueSystemId& id, const OwnerShip ownerShip, HANDLE handle) noexcept; void removeHandle(const UniqueSystemId& id) noexcept; private: + IpcHandleManager() = default; + + mutable std::mutex mtx; std::map ipcHandles; }; diff --git a/iceoryx_hoofs/platform/win/source/handle_translator.cpp b/iceoryx_hoofs/platform/win/source/handle_translator.cpp index 526a82279f..35b35526a0 100644 --- a/iceoryx_hoofs/platform/win/source/handle_translator.cpp +++ b/iceoryx_hoofs/platform/win/source/handle_translator.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. +// Copyright (c) 2021 - 2022 by Apex.AI Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,33 +16,78 @@ #include "iceoryx_hoofs/platform/handle_translator.hpp" +#include + +constexpr int HandleTranslator::INVALID_LINUX_FD; + HandleTranslator& HandleTranslator::getInstance() noexcept { static HandleTranslator globalHandleTranslator; return globalHandleTranslator; } -HANDLE HandleTranslator::get(const int handle) const noexcept +HANDLE HandleTranslator::get(const int linuxFd) const noexcept { - return m_handleList[static_cast(handle)]; + if (linuxFd == INVALID_LINUX_FD) + { + return INVALID_HANDLE_VALUE; + } + + std::lock_guard lock(m_mtx); + + auto iter = m_linuxToWindows.find(linuxFd); + if (iter == m_linuxToWindows.end()) + { + std::cerr << "Cannot acquire windows file handle for not registered linux file descriptor " << linuxFd + << std::endl; + return INVALID_HANDLE_VALUE; + } + + return iter->second; } -int HandleTranslator::add(HANDLE handle) noexcept +int HandleTranslator::add(HANDLE windowsHandle) noexcept { - for (int64_t limit = m_handleList.size(), k = 0; k < limit; ++k) + if (windowsHandle == INVALID_HANDLE_VALUE) + { + return INVALID_LINUX_FD; + } + + std::lock_guard lock(m_mtx); + + int linuxFd = INVALID_LINUX_FD; + if (!m_freeFileDescriptors.empty()) + { + linuxFd = m_freeFileDescriptors.front(); + m_freeFileDescriptors.pop(); + } + else { - if (m_handleList[k] == nullptr) - { - m_handleList[k] = handle; - return k; - } + linuxFd = m_currentLinuxFileHandle; + ++m_currentLinuxFileHandle; } - m_handleList.emplace_back(handle); - return m_handleList.size() - 1; + m_linuxToWindows[linuxFd] = windowsHandle; + return linuxFd; } -void HandleTranslator::remove(const int handle) noexcept +void HandleTranslator::remove(const int linuxFd) noexcept { - m_handleList[static_cast(handle)] = nullptr; + if (linuxFd == INVALID_LINUX_FD) + { + return; + } + + std::lock_guard lock(m_mtx); + + auto iter = m_linuxToWindows.find(linuxFd); + if (iter == m_linuxToWindows.end()) + { + std::cerr << "Unable to release not registered file handle " << linuxFd << " since it was not acquired" + << std::endl; + return; + } + + m_linuxToWindows.erase(iter); + m_freeFileDescriptors.emplace(linuxFd); } diff --git a/iceoryx_hoofs/platform/win/source/ipc_handle_manager.cpp b/iceoryx_hoofs/platform/win/source/ipc_handle_manager.cpp index 7ffd1f90e0..9834280107 100644 --- a/iceoryx_hoofs/platform/win/source/ipc_handle_manager.cpp +++ b/iceoryx_hoofs/platform/win/source/ipc_handle_manager.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. +// Copyright (c) 2021 - 2022 by Apex.AI Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,6 +16,12 @@ #include "iceoryx_hoofs/platform/ipc_handle_manager.hpp" +IpcHandleManager& IpcHandleManager::getInstance() noexcept +{ + static IpcHandleManager instance; + return instance; +} + IpcHandleManager::~IpcHandleManager() noexcept { for (auto& handle : ipcHandles) @@ -27,8 +33,10 @@ IpcHandleManager::~IpcHandleManager() noexcept } } -bool IpcHandleManager::getHandle(const UniqueSystemId& id, HANDLE& handle) noexcept +bool IpcHandleManager::getHandle(const UniqueSystemId& id, HANDLE& handle) const noexcept { + std::lock_guard lock(mtx); + auto iter = ipcHandles.find(id); if (iter != ipcHandles.end()) { @@ -40,11 +48,15 @@ bool IpcHandleManager::getHandle(const UniqueSystemId& id, HANDLE& handle) noexc void IpcHandleManager::addHandle(const UniqueSystemId& id, const OwnerShip ownerShip, HANDLE handle) noexcept { + std::lock_guard lock(mtx); + ipcHandles[id] = IpcHandle_t{ownerShip, handle}; } void IpcHandleManager::removeHandle(const UniqueSystemId& id) noexcept { + std::lock_guard lock(mtx); + auto iter = ipcHandles.find(id); if (iter != ipcHandles.end()) { diff --git a/iceoryx_hoofs/platform/win/source/mman.cpp b/iceoryx_hoofs/platform/win/source/mman.cpp index 470b47cd04..ef4f47e97c 100644 --- a/iceoryx_hoofs/platform/win/source/mman.cpp +++ b/iceoryx_hoofs/platform/win/source/mman.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. +// Copyright (c) 2021 - 2022 by Apex.AI Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,10 +19,13 @@ #include "iceoryx_hoofs/platform/platform_settings.hpp" #include "iceoryx_hoofs/platform/win32_errorHandling.hpp" +#include +#include #include #include static std::set openedSharedMemorySegments; +static std::mutex openedSharedMemorySegmentsMutex; void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset) { @@ -31,6 +34,13 @@ void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset) DWORD fileOffsetLow = 0; DWORD numberOfBytesToMap = length; + auto printErrorMessage = [&] { + std::cerr << "Failed to map file mapping into process space with mmap( addr = " << std::hex << addr << std::dec + << ", length = " << length << ", [always assume PROT_READ | PROT_WRITE] prot = " << prot + << ", [always assume MAP_SHARED] flags = " << flags << ", fd = " << fd + << ", [always assume 0] offset = " << offset << ")" << std::endl; + }; + void* mappedObject = Win32Call(MapViewOfFile, HandleTranslator::getInstance().get(fd), desiredAccess, @@ -38,9 +48,21 @@ void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset) fileOffsetLow, numberOfBytesToMap) .value; + + if (mappedObject == nullptr) + { + printErrorMessage(); + return nullptr; + } + // windows only reserves memory but does not allocate it right away (see SEC_RESERVE in iox_shm_open) // this call actually allocates the right amount of bytes mappedObject = Win32Call(VirtualAlloc, mappedObject, numberOfBytesToMap, MEM_COMMIT, PAGE_READWRITE).value; + if (mappedObject == nullptr) + { + printErrorMessage(); + return nullptr; + } return mappedObject; } @@ -51,6 +73,11 @@ int munmap(void* addr, size_t length) { return 0; } + else + { + std::cerr << "Failed to unmap memory region with munmap( addr = " << std::hex << addr << std::dec + << ", length = " << length << ")" << std::endl; + } return -1; } @@ -59,6 +86,13 @@ int iox_shm_open(const char* name, int oflag, mode_t mode) { HANDLE sharedMemoryHandle{nullptr}; + auto printErrorMessage = [&] { + std::cerr << "Failed to create shared memory with iox_shm_open( name = " << name + << ", [only consider O_CREAT and O_EXCL] oflag = " << oflag + << ", [always assume read, write, execute for everyone] mode = " << mode << ")" << std::endl; + }; + + if (oflag & O_CREAT) { // we do not yet support ACL and rights for data partitions in windows @@ -77,6 +111,13 @@ int iox_shm_open(const char* name, int oflag, mode_t mode) static_cast(name)); sharedMemoryHandle = result.value; + if (sharedMemoryHandle == nullptr) + { + errno = EACCES; + printErrorMessage(); + return HandleTranslator::INVALID_LINUX_FD; + } + if (oflag & O_EXCL && result.error == ERROR_ALREADY_EXISTS) { errno = EEXIST; @@ -84,7 +125,7 @@ int iox_shm_open(const char* name, int oflag, mode_t mode) { Win32Call(CloseHandle, sharedMemoryHandle).value; } - return -1; + return HandleTranslator::INVALID_LINUX_FD; } } else @@ -96,22 +137,31 @@ int iox_shm_open(const char* name, int oflag, mode_t mode) sharedMemoryHandle = result.value; + if (sharedMemoryHandle == nullptr) + { + errno = ENOENT; + return HandleTranslator::INVALID_LINUX_FD; + } + if (result.error != 0) { - if (sharedMemoryHandle != nullptr) - { - Win32Call(CloseHandle, sharedMemoryHandle); - } - return -1; + printErrorMessage(); + errno = EACCES; + Win32Call(CloseHandle, sharedMemoryHandle); + return HandleTranslator::INVALID_LINUX_FD; } } - openedSharedMemorySegments.insert(name); + { + std::lock_guard lock(openedSharedMemorySegmentsMutex); + openedSharedMemorySegments.insert(name); + } return HandleTranslator::getInstance().add(sharedMemoryHandle); } int iox_shm_unlink(const char* name) { + std::lock_guard lock(openedSharedMemorySegmentsMutex); auto iter = openedSharedMemorySegments.find(name); if (iter != openedSharedMemorySegments.end()) { diff --git a/iceoryx_hoofs/platform/win/source/pthread.cpp b/iceoryx_hoofs/platform/win/source/pthread.cpp index 257fef06ea..c56c26afa2 100644 --- a/iceoryx_hoofs/platform/win/source/pthread.cpp +++ b/iceoryx_hoofs/platform/win/source/pthread.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. +// Copyright (c) 2021 - 2022 by Apex.AI Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,8 +22,6 @@ #include #include -static IpcHandleManager ipcMutexHandleManager; - int iox_pthread_setname_np(pthread_t thread, const char* name) { DWORD threadId = Win32Call(GetThreadId, static_cast(thread)).value; @@ -107,7 +105,7 @@ static HANDLE acquireMutexHandle(pthread_mutex_t* mutex) } HANDLE newHandle; - if (ipcMutexHandleManager.getHandle(mutex->uniqueId, newHandle)) + if (IpcHandleManager::getInstance().getHandle(mutex->uniqueId, newHandle)) { return newHandle; } @@ -121,7 +119,7 @@ static HANDLE acquireMutexHandle(pthread_mutex_t* mutex) return nullptr; } - ipcMutexHandleManager.addHandle(mutex->uniqueId, OwnerShip::LOAN, newHandle); + IpcHandleManager::getInstance().addHandle(mutex->uniqueId, OwnerShip::LOAN, newHandle); return newHandle; } @@ -138,7 +136,7 @@ int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr) mutex->handle = createWin32Mutex(NULL, FALSE, generateMutexName(mutex->uniqueId).c_str()); if (mutex->handle != nullptr) { - ipcMutexHandleManager.addHandle(mutex->uniqueId, OwnerShip::OWN, mutex->handle); + IpcHandleManager::getInstance().addHandle(mutex->uniqueId, OwnerShip::OWN, mutex->handle); } } diff --git a/iceoryx_hoofs/platform/win/source/semaphore.cpp b/iceoryx_hoofs/platform/win/source/semaphore.cpp index c9b264d608..feea04616e 100644 --- a/iceoryx_hoofs/platform/win/source/semaphore.cpp +++ b/iceoryx_hoofs/platform/win/source/semaphore.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved. -// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. +// Copyright (c) 2021 - 2022 by Apex.AI Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,8 +20,6 @@ #include -static IpcHandleManager ipcSemaphoreHandleManager; - static std::string generateSemaphoreName(const UniqueSystemId& id) { return "iox_semaphore_" + static_cast(id); @@ -35,7 +33,7 @@ static HANDLE acquireSemaphoreHandle(iox_sem_t* sem) } HANDLE newHandle; - if (ipcSemaphoreHandleManager.getHandle(sem->uniqueId, newHandle)) + if (IpcHandleManager::getInstance().getHandle(sem->uniqueId, newHandle)) { return newHandle; } @@ -50,7 +48,7 @@ static HANDLE acquireSemaphoreHandle(iox_sem_t* sem) return nullptr; } - ipcSemaphoreHandleManager.addHandle(sem->uniqueId, OwnerShip::LOAN, newHandle); + IpcHandleManager::getInstance().addHandle(sem->uniqueId, OwnerShip::LOAN, newHandle); return newHandle; } @@ -148,7 +146,7 @@ int iox_sem_destroy(iox_sem_t* sem) CloseHandle(acquireSemaphoreHandle(sem)); if (sem->isInterprocessSemaphore) { - ipcSemaphoreHandleManager.removeHandle(sem->uniqueId); + IpcHandleManager::getInstance().removeHandle(sem->uniqueId); } return 0; } @@ -185,7 +183,7 @@ int iox_sem_init(iox_sem_t* sem, int pshared, unsigned int value) sem->handle = sem_create_win32_semaphore(value, generateSemaphoreName(sem->uniqueId).c_str()); if (sem->handle != nullptr) { - ipcSemaphoreHandleManager.addHandle(sem->uniqueId, OwnerShip::OWN, sem->handle); + IpcHandleManager::getInstance().addHandle(sem->uniqueId, OwnerShip::OWN, sem->handle); } } else diff --git a/iceoryx_hoofs/test/integrationtests/test_resizeable_lockfree_queue_stresstest.cpp b/iceoryx_hoofs/test/integrationtests/test_resizeable_lockfree_queue_stresstest.cpp index d082010e8d..83c6675f07 100644 --- a/iceoryx_hoofs/test/integrationtests/test_resizeable_lockfree_queue_stresstest.cpp +++ b/iceoryx_hoofs/test/integrationtests/test_resizeable_lockfree_queue_stresstest.cpp @@ -399,7 +399,7 @@ TYPED_TEST_SUITE(ResizeableLockFreeQueueStressTest, TestConfigs); ///@brief Tests concurrent operation of multiple producers and consumers /// with respect to completeness of the data, i.e. nothing is lost. -TYPED_TEST(ResizeableLockFreeQueueStressTest, multiProducerMultiConsumerCompleteness) +TYPED_TEST(ResizeableLockFreeQueueStressTest, DISABLED_multiProducerMultiConsumerCompleteness) { ::testing::Test::RecordProperty("TEST_ID", "9640d068-5c9f-4bc4-b4a0-c0a2225c15ed"); using Queue = typename TestFixture::Queue; @@ -471,7 +471,7 @@ TYPED_TEST(ResizeableLockFreeQueueStressTest, multiProducerMultiConsumerComplete /// @brief Tests concurrent operation of multiple producers and consumers /// with respect to order of the data (monotonic, FIFO). /// @note this cannot be done easily together with completeness and limited memory -TYPED_TEST(ResizeableLockFreeQueueStressTest, multiProducerMultiConsumerOrder) +TYPED_TEST(ResizeableLockFreeQueueStressTest, DISABLED_multiProducerMultiConsumerOrder) { ::testing::Test::RecordProperty("TEST_ID", "5a6e3e6b-7cd9-4079-a9e8-7a849ea3dfe9"); using Queue = typename TestFixture::Queue; @@ -695,7 +695,7 @@ TYPED_TEST(ResizeableLockFreeQueueStressTest, DISABLED_hybridMultiProducerMultiC /// again it is checked that nothing is lost or created by accident. /// @note the tests are getting quite complicated but the complex setup is unavoidable /// in order to test the general case under load. -TYPED_TEST(ResizeableLockFreeQueueStressTest, hybridMultiProducerMultiConsumer0verflowWithCapacityChange) +TYPED_TEST(ResizeableLockFreeQueueStressTest, DISABLED_hybridMultiProducerMultiConsumer0verflowWithCapacityChange) { ::testing::Test::RecordProperty("TEST_ID", "6421f32a-a1f7-4fe2-978f-6ef2005e0cc9"); using Queue = typename TestFixture::Queue; diff --git a/iceoryx_hoofs/test/moduletests/test_concurrent_smart_lock.cpp b/iceoryx_hoofs/test/moduletests/test_concurrent_smart_lock.cpp index 856bf9776e..4710d04454 100644 --- a/iceoryx_hoofs/test/moduletests/test_concurrent_smart_lock.cpp +++ b/iceoryx_hoofs/test/moduletests/test_concurrent_smart_lock.cpp @@ -131,7 +131,7 @@ class smart_lock_test : public Test } } - Watchdog m_watchdog{iox::units::Duration::fromSeconds(2)}; + Watchdog m_watchdog{iox::units::Duration::fromSeconds(60U)}; using SutType_t = smart_lock; optional m_sut; std::atomic m_numberOfThreadWaiter{0U}; diff --git a/iceoryx_hoofs/test/moduletests/test_semaphore_module.cpp b/iceoryx_hoofs/test/moduletests/test_semaphore_module.cpp index f1d36040ba..6fb6acda2b 100644 --- a/iceoryx_hoofs/test/moduletests/test_semaphore_module.cpp +++ b/iceoryx_hoofs/test/moduletests/test_semaphore_module.cpp @@ -15,14 +15,12 @@ // // SPDX-License-Identifier: Apache-2.0 -#include "iceoryx_hoofs/testing/timing_test.hpp" -#if !(defined(QNX) || defined(QNX__) || defined(__QNX__)) - #include "iceoryx_hoofs/cxx/convert.hpp" #include "iceoryx_hoofs/internal/units/duration.hpp" #include "iceoryx_hoofs/platform/time.hpp" #include "iceoryx_hoofs/posix_wrapper/semaphore.hpp" #include "iceoryx_hoofs/testing/test.hpp" +#include "iceoryx_hoofs/testing/timing_test.hpp" #include #include @@ -381,4 +379,3 @@ TIMING_TEST_P(Semaphore_test, TimedWaitWithoutTimeout, Repeat(3), [&] { t.join(); }); } // namespace -#endif // not defined QNX diff --git a/iceoryx_hoofs/test/moduletests/test_shared_memory.cpp b/iceoryx_hoofs/test/moduletests/test_shared_memory.cpp index f64e064b2e..131ec6cb8f 100644 --- a/iceoryx_hoofs/test/moduletests/test_shared_memory.cpp +++ b/iceoryx_hoofs/test/moduletests/test_shared_memory.cpp @@ -20,6 +20,7 @@ #include "iceoryx_hoofs/internal/posix_wrapper/shared_memory_object/shared_memory.hpp" #include "iceoryx_hoofs/platform/mman.hpp" #include "iceoryx_hoofs/platform/stat.hpp" +#include "iceoryx_hoofs/platform/unistd.hpp" #include "iceoryx_hoofs/posix_wrapper/posix_call.hpp" #include @@ -63,13 +64,23 @@ class SharedMemory_Test : public Test .create(); } - bool createRawSharedMemory(const iox::posix::SharedMemory::Name_t& name) + std::unique_ptr> createRawSharedMemory(const iox::posix::SharedMemory::Name_t& name) { - return !iox::posix::posixCall(iox_shm_open)( - (std::string("/") + name.c_str()).c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) - .failureReturnValue(SharedMemory::INVALID_HANDLE) - .evaluate() - .has_error(); + auto result = iox::posix::posixCall(iox_shm_open)((std::string("/") + name.c_str()).c_str(), + O_RDWR | O_CREAT, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) + .failureReturnValue(SharedMemory::INVALID_HANDLE) + .evaluate(); + if (result.has_error()) + { + return std::unique_ptr>(); + } + + return std::unique_ptr>(new int(result->value), [=](int* fd) { + this->cleanupSharedMemory(name); + iox_close(*fd); + delete fd; + }); } bool cleanupSharedMemory(const iox::posix::SharedMemory::Name_t& name) @@ -141,57 +152,55 @@ TEST_F(SharedMemory_Test, UnlinkExistingShmWorks) { ::testing::Test::RecordProperty("TEST_ID", "11f0b2f2-b891-41e4-bb82-648a9541582f"); constexpr const char SHM_NAME[] = "its_a_mee_monukulius"; - ASSERT_TRUE(createRawSharedMemory(SHM_NAME)); + auto rawSharedMemory = createRawSharedMemory(SHM_NAME); + ASSERT_TRUE(static_cast(rawSharedMemory)); auto result = iox::posix::SharedMemory::unlinkIfExist(SHM_NAME); ASSERT_FALSE(result.has_error()); EXPECT_TRUE(*result); + + // delete the underyling fd pointer but do not cleanup raw shared memory since + // is is already deleted with unlinkIfExist in the test + delete rawSharedMemory.release(); } TEST_F(SharedMemory_Test, ExclusiveCreateWorksWhenShmDoesNotExist) { ::testing::Test::RecordProperty("TEST_ID", "bfc44656-ef23-49ef-be96-0d4bfb592030"); - IOX_DISCARD_RESULT(iox::posix::SharedMemory::unlinkIfExist(SUT_SHM_NAME)); - { - auto sut = createSut(SUT_SHM_NAME, OpenMode::EXCLUSIVE_CREATE); - ASSERT_FALSE(sut.has_error()); - EXPECT_TRUE(sut->hasOwnership()); - EXPECT_THAT(sut->getHandle(), Ne(SharedMemory::INVALID_HANDLE)); - } - EXPECT_FALSE(cleanupSharedMemory(SUT_SHM_NAME)); + auto sut = createSut(SUT_SHM_NAME, OpenMode::EXCLUSIVE_CREATE); + ASSERT_FALSE(sut.has_error()); + EXPECT_TRUE(sut->hasOwnership()); + EXPECT_THAT(sut->getHandle(), Ne(SharedMemory::INVALID_HANDLE)); } TEST_F(SharedMemory_Test, ExclusiveCreateFailsWhenShmExists) { ::testing::Test::RecordProperty("TEST_ID", "19eca662-4f01-453b-9ae3-5cb2090e46ce"); - ASSERT_TRUE(createRawSharedMemory(SUT_SHM_NAME)); + auto rawSharedMemory = createRawSharedMemory(SUT_SHM_NAME); + ASSERT_TRUE(static_cast(rawSharedMemory)); + auto sut = createSut(SUT_SHM_NAME, OpenMode::EXCLUSIVE_CREATE); ASSERT_TRUE(sut.has_error()); - IOX_DISCARD_RESULT(iox::posix::SharedMemory::unlinkIfExist(SUT_SHM_NAME)); } TEST_F(SharedMemory_Test, PurgeAndCreateWorksWhenShmDoesNotExist) { ::testing::Test::RecordProperty("TEST_ID", "611694b6-d877-43a1-a6e3-dfef3f8a174b"); - { - auto sut = createSut(SUT_SHM_NAME, OpenMode::PURGE_AND_CREATE); - ASSERT_FALSE(sut.has_error()); - EXPECT_TRUE(sut->hasOwnership()); - EXPECT_THAT(sut->getHandle(), Ne(SharedMemory::INVALID_HANDLE)); - } - EXPECT_FALSE(cleanupSharedMemory(SUT_SHM_NAME)); + auto sut = createSut(SUT_SHM_NAME, OpenMode::PURGE_AND_CREATE); + ASSERT_FALSE(sut.has_error()); + EXPECT_TRUE(sut->hasOwnership()); + EXPECT_THAT(sut->getHandle(), Ne(SharedMemory::INVALID_HANDLE)); } TEST_F(SharedMemory_Test, PurgeAndCreateWorksWhenShmExists) { ::testing::Test::RecordProperty("TEST_ID", "21d620f0-af45-46ad-a5b7-1c18026fb9a8"); - { - ASSERT_TRUE(createRawSharedMemory(SUT_SHM_NAME)); - auto sut = createSut(SUT_SHM_NAME, OpenMode::PURGE_AND_CREATE); - ASSERT_FALSE(sut.has_error()); - EXPECT_TRUE(sut->hasOwnership()); - EXPECT_THAT(sut->getHandle(), Ne(SharedMemory::INVALID_HANDLE)); - } - EXPECT_FALSE(cleanupSharedMemory(SUT_SHM_NAME)); + auto rawSharedMemory = createRawSharedMemory(SUT_SHM_NAME); + ASSERT_TRUE(static_cast(rawSharedMemory)); + + auto sut = createSut(SUT_SHM_NAME, OpenMode::PURGE_AND_CREATE); + ASSERT_FALSE(sut.has_error()); + EXPECT_TRUE(sut->hasOwnership()); + EXPECT_THAT(sut->getHandle(), Ne(SharedMemory::INVALID_HANDLE)); } TEST_F(SharedMemory_Test, CreateOrOpenCreatesShmWhenShmDoesNotExist) @@ -203,33 +212,32 @@ TEST_F(SharedMemory_Test, CreateOrOpenCreatesShmWhenShmDoesNotExist) EXPECT_TRUE(sut->hasOwnership()); EXPECT_THAT(sut->getHandle(), Ne(SharedMemory::INVALID_HANDLE)); } - EXPECT_FALSE(cleanupSharedMemory(SUT_SHM_NAME)); } TEST_F(SharedMemory_Test, CreateOrOpenOpensShmWhenShmDoesExist) { ::testing::Test::RecordProperty("TEST_ID", "2413ddda-9d2e-4429-adba-81fe848a6a06"); - ASSERT_TRUE(createRawSharedMemory(SUT_SHM_NAME)); + auto rawSharedMemory = createRawSharedMemory(SUT_SHM_NAME); + ASSERT_TRUE(static_cast(rawSharedMemory)); { auto sut = createSut(SUT_SHM_NAME, OpenMode::OPEN_OR_CREATE); ASSERT_FALSE(sut.has_error()); EXPECT_FALSE(sut->hasOwnership()); EXPECT_THAT(sut->getHandle(), Ne(SharedMemory::INVALID_HANDLE)); } - EXPECT_TRUE(cleanupSharedMemory(SUT_SHM_NAME)); } TEST_F(SharedMemory_Test, OpenWorksWhenShmExist) { ::testing::Test::RecordProperty("TEST_ID", "59ba1e1c-ec1c-45fb-bc85-c6256f9176fd"); - ASSERT_TRUE(createRawSharedMemory(SUT_SHM_NAME)); + auto rawSharedMemory = createRawSharedMemory(SUT_SHM_NAME); + ASSERT_TRUE(static_cast(rawSharedMemory)); { auto sut = createSut(SUT_SHM_NAME, OpenMode::OPEN_EXISTING); ASSERT_FALSE(sut.has_error()); EXPECT_FALSE(sut->hasOwnership()); EXPECT_THAT(sut->getHandle(), Ne(SharedMemory::INVALID_HANDLE)); } - EXPECT_TRUE(cleanupSharedMemory(SUT_SHM_NAME)); } TEST_F(SharedMemory_Test, OpenFailsWhenShmDoesNotExist) diff --git a/iceoryx_integrationtest/package.xml b/iceoryx_integrationtest/package.xml index 9f85be4491..81fe28f080 100644 --- a/iceoryx_integrationtest/package.xml +++ b/iceoryx_integrationtest/package.xml @@ -2,9 +2,9 @@ iceoryx_integrationtest - 1.91.0 + 2.0.0 iceoryx Software Integrationtest - Eclipse Foundation, Inc. + Eclipse Foundation, Inc. Apache 2.0 https://iceoryx.io https://github.com/eclipse-iceoryx/iceoryx/issues diff --git a/iceoryx_posh/CMakeLists.txt b/iceoryx_posh/CMakeLists.txt index e7ac817738..8f28ac07bf 100644 --- a/iceoryx_posh/CMakeLists.txt +++ b/iceoryx_posh/CMakeLists.txt @@ -16,7 +16,7 @@ # SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.16) -set(IOX_VERSION_STRING "1.91.0") +set(IOX_VERSION_STRING "2.0.0") project(iceoryx_posh VERSION ${IOX_VERSION_STRING}) diff --git a/iceoryx_posh/include/iceoryx_posh/popo/subscriber_options.hpp b/iceoryx_posh/include/iceoryx_posh/popo/subscriber_options.hpp index abc542b539..62c2890ef9 100644 --- a/iceoryx_posh/include/iceoryx_posh/popo/subscriber_options.hpp +++ b/iceoryx_posh/include/iceoryx_posh/popo/subscriber_options.hpp @@ -47,8 +47,8 @@ struct SubscriberOptions /// @brief The option whether the publisher should block when the subscriber queue is full QueueFullPolicy queueFullPolicy{QueueFullPolicy::DISCARD_OLDEST_DATA}; - /// @brief Indicates whether to enforce sufficient history support of the publisher, - /// i.e. require historyCapacity >= historyRequest to be eligible to be connected + /// @brief Indicates whether to enforce history support of the publisher, + /// i.e. require historyCapacity > 0 to be eligible to be connected bool requiresPublisherHistorySupport{false}; /// @brief serialization of the SubscriberOptions diff --git a/iceoryx_posh/package.xml b/iceoryx_posh/package.xml index eaac83152b..d71ed6102c 100644 --- a/iceoryx_posh/package.xml +++ b/iceoryx_posh/package.xml @@ -2,9 +2,9 @@ iceoryx_posh - 1.91.0 + 2.0.0 Eclipse iceoryx inter-process-communication (IPC) middleware Posix Shared Memory Library and middleware daemon (RouDi) - Eclipse Foundation, Inc. + Eclipse Foundation, Inc. Apache 2.0 https://iceoryx.io https://github.com/eclipse-iceoryx/iceoryx/issues diff --git a/iceoryx_posh/source/roudi/port_manager.cpp b/iceoryx_posh/source/roudi/port_manager.cpp index 0ac78e9522..8829709525 100644 --- a/iceoryx_posh/source/roudi/port_manager.cpp +++ b/iceoryx_posh/source/roudi/port_manager.cpp @@ -492,8 +492,7 @@ bool PortManager::isCompatiblePubSub(const PublisherPortRouDiType& publisher, !(pubOpts.subscriberTooSlowPolicy == popo::ConsumerTooSlowPolicy::DISCARD_OLDEST_DATA && subOpts.queueFullPolicy == popo::QueueFullPolicy::BLOCK_PRODUCER); - const bool historyRequestIsCompatible = - !subOpts.requiresPublisherHistorySupport || subOpts.historyRequest <= pubOpts.historyCapacity; + const bool historyRequestIsCompatible = !subOpts.requiresPublisherHistorySupport || pubOpts.historyCapacity > 0; return blockingPoliciesAreCompatible && historyRequestIsCompatible; } diff --git a/iceoryx_posh/test/integrationtests/test_publisher_subscriber_communication.cpp b/iceoryx_posh/test/integrationtests/test_publisher_subscriber_communication.cpp index d6a284b612..57b6508c71 100644 --- a/iceoryx_posh/test/integrationtests/test_publisher_subscriber_communication.cpp +++ b/iceoryx_posh/test/integrationtests/test_publisher_subscriber_communication.cpp @@ -211,7 +211,7 @@ TEST_F(PublisherSubscriberCommunication_test, } TEST_F(PublisherSubscriberCommunication_test, - SubscriberRequiringHistorySupportDoesConnectToPublisherWithSufficientHistorySupport) + SubscriberRequiringHistorySupportDoesConnectToPublisherWithEqualHistorySupport) { ::testing::Test::RecordProperty("TEST_ID", "0ca391fe-c4f6-48b5-bd36-96854513c6bb"); @@ -229,23 +229,25 @@ TEST_F(PublisherSubscriberCommunication_test, } TEST_F(PublisherSubscriberCommunication_test, - SubscriberRequiringHistorySupportDoesNotConnectToPublisherWithInSufficientHistorySupport) + SubscriberRequiringHistorySupportDoesConnectToPublisherWithLowerHistorySupport) { ::testing::Test::RecordProperty("TEST_ID", "46b917e6-75f1-4cd2-8ffa-1c254f3423a7"); - constexpr uint64_t historyRequest = 3; - constexpr uint64_t historyCapacity = 2; + constexpr uint64_t historyRequest = 6; + constexpr uint64_t historyCapacity = 5; constexpr bool requiresHistorySupport = true; auto publisher = createPublisher(historyCapacity); auto subscriber = createSubscriber(historyRequest, requiresHistorySupport); ASSERT_TRUE(publisher); - EXPECT_FALSE(publisher->hasSubscribers()); + EXPECT_TRUE(publisher->hasSubscribers()); + + publishAndExpectReceivedData(publisher, subscriber, 75); } TEST_F(PublisherSubscriberCommunication_test, - SubscriberNotRequiringHistorySupportDoesConnectToPublisherWithInsufficientHistorySupport) + SubscriberNotRequiringHistorySupportDoesConnectToPublisherWithLowerHistorySupport) { ::testing::Test::RecordProperty("TEST_ID", "b672c382-f81b-4cd4-8049-36d2691bb532"); @@ -259,7 +261,7 @@ TEST_F(PublisherSubscriberCommunication_test, ASSERT_TRUE(publisher); EXPECT_TRUE(publisher->hasSubscribers()); - publishAndExpectReceivedData(publisher, subscriber, 75); + publishAndExpectReceivedData(publisher, subscriber, 76); } TEST_F(PublisherSubscriberCommunication_test, SubscriberCanOnlyBeSubscribedWhenInterfaceDiffersFromPublisher) diff --git a/iceoryx_posh/test/moduletests/test_gw_gateway_generic.cpp b/iceoryx_posh/test/moduletests/test_gw_gateway_generic.cpp index 615e2da720..c720a73a21 100644 --- a/iceoryx_posh/test/moduletests/test_gw_gateway_generic.cpp +++ b/iceoryx_posh/test/moduletests/test_gw_gateway_generic.cpp @@ -58,6 +58,8 @@ class GatewayGenericTest : public Test public: void SetUp(){}; void TearDown(){}; + + std::unique_ptr sut{new TestGatewayGeneric{}}; }; // ======================================== Tests ======================================== // @@ -67,12 +69,10 @@ TEST_F(GatewayGenericTest, AddedChannelsAreStored) // ===== Setup auto testService = iox::capro::ServiceDescription("service", "instance", "event"); - TestGatewayGeneric gw{}; - // ===== Test - ASSERT_FALSE(gw.addChannel(testService, StubbedIceoryxTerminal::Options()).has_error()); + ASSERT_FALSE(sut->addChannel(testService, StubbedIceoryxTerminal::Options()).has_error()); - EXPECT_EQ(1U, gw.getNumberOfChannels()); + EXPECT_EQ(1U, sut->getNumberOfChannels()); } TEST_F(GatewayGenericTest, DoesNotAddDuplicateChannels) @@ -81,13 +81,11 @@ TEST_F(GatewayGenericTest, DoesNotAddDuplicateChannels) // ===== Setup auto testService = iox::capro::ServiceDescription("service", "instance", "event"); - TestGatewayGeneric gw{}; - // ===== Test - ASSERT_FALSE(gw.addChannel(testService, StubbedIceoryxTerminal::Options()).has_error()); - ASSERT_FALSE(gw.addChannel(testService, StubbedIceoryxTerminal::Options()).has_error()); + ASSERT_FALSE(sut->addChannel(testService, StubbedIceoryxTerminal::Options()).has_error()); + ASSERT_FALSE(sut->addChannel(testService, StubbedIceoryxTerminal::Options()).has_error()); - EXPECT_EQ(1U, gw.getNumberOfChannels()); + EXPECT_EQ(1U, sut->getNumberOfChannels()); } TEST_F(GatewayGenericTest, IgnoresWildcardServices) @@ -99,20 +97,18 @@ TEST_F(GatewayGenericTest, IgnoresWildcardServices) auto wildcardInstanceService = iox::capro::ServiceDescription("service", "*", "event"); auto wildcardEventService = iox::capro::ServiceDescription("service", "instance", "*"); - TestGatewayGeneric gw{}; - // ===== Test - auto resultOne = gw.addChannel(completeWildcardService, StubbedIceoryxTerminal::Options()); - auto resultTwo = gw.addChannel(wildcardServiceService, StubbedIceoryxTerminal::Options()); - auto resultThree = gw.addChannel(wildcardInstanceService, StubbedIceoryxTerminal::Options()); - auto resultFour = gw.addChannel(wildcardEventService, StubbedIceoryxTerminal::Options()); + auto resultOne = sut->addChannel(completeWildcardService, StubbedIceoryxTerminal::Options()); + auto resultTwo = sut->addChannel(wildcardServiceService, StubbedIceoryxTerminal::Options()); + auto resultThree = sut->addChannel(wildcardInstanceService, StubbedIceoryxTerminal::Options()); + auto resultFour = sut->addChannel(wildcardEventService, StubbedIceoryxTerminal::Options()); EXPECT_EQ(iox::gw::GatewayError::UNSUPPORTED_SERVICE_TYPE, resultOne.get_error()); EXPECT_EQ(iox::gw::GatewayError::UNSUPPORTED_SERVICE_TYPE, resultTwo.get_error()); EXPECT_EQ(iox::gw::GatewayError::UNSUPPORTED_SERVICE_TYPE, resultThree.get_error()); EXPECT_EQ(iox::gw::GatewayError::UNSUPPORTED_SERVICE_TYPE, resultFour.get_error()); - EXPECT_EQ(0U, gw.getNumberOfChannels()); + EXPECT_EQ(0U, sut->getNumberOfChannels()); } TEST_F(GatewayGenericTest, ProperlyManagesMultipleChannels) @@ -124,62 +120,58 @@ TEST_F(GatewayGenericTest, ProperlyManagesMultipleChannels) auto serviceThree = iox::capro::ServiceDescription("serviceThree", "instanceThree", "eventThree"); auto serviceFour = iox::capro::ServiceDescription("serviceFour", "instanceFour", "eventFour"); - TestGatewayGeneric gw{}; - // ===== Test - ASSERT_FALSE(gw.addChannel(serviceOne, StubbedIceoryxTerminal::Options()).has_error()); - ASSERT_FALSE(gw.addChannel(serviceTwo, StubbedIceoryxTerminal::Options()).has_error()); - ASSERT_FALSE(gw.addChannel(serviceThree, StubbedIceoryxTerminal::Options()).has_error()); - ASSERT_FALSE(gw.addChannel(serviceFour, StubbedIceoryxTerminal::Options()).has_error()); + ASSERT_FALSE(sut->addChannel(serviceOne, StubbedIceoryxTerminal::Options()).has_error()); + ASSERT_FALSE(sut->addChannel(serviceTwo, StubbedIceoryxTerminal::Options()).has_error()); + ASSERT_FALSE(sut->addChannel(serviceThree, StubbedIceoryxTerminal::Options()).has_error()); + ASSERT_FALSE(sut->addChannel(serviceFour, StubbedIceoryxTerminal::Options()).has_error()); - EXPECT_EQ(4U, gw.getNumberOfChannels()); - EXPECT_EQ(true, gw.findChannel(serviceOne).has_value()); - EXPECT_EQ(true, gw.findChannel(serviceTwo).has_value()); - EXPECT_EQ(true, gw.findChannel(serviceThree).has_value()); - EXPECT_EQ(true, gw.findChannel(serviceFour).has_value()); + EXPECT_EQ(4U, sut->getNumberOfChannels()); + EXPECT_EQ(true, sut->findChannel(serviceOne).has_value()); + EXPECT_EQ(true, sut->findChannel(serviceTwo).has_value()); + EXPECT_EQ(true, sut->findChannel(serviceThree).has_value()); + EXPECT_EQ(true, sut->findChannel(serviceFour).has_value()); } TEST_F(GatewayGenericTest, HandlesMaxmimumChannelCapacity) { ::testing::Test::RecordProperty("TEST_ID", "5b4385e8-c717-4368-8121-b7d526fd22ac"); // ===== Setup - TestGatewayGeneric gw{}; // ===== Test for (auto i = 0U; i < iox::MAX_CHANNEL_NUMBER; i++) { auto result = - gw.addChannel(iox::capro::ServiceDescription( - iox::capro::IdString_t(iox::cxx::TruncateToCapacity, iox::cxx::convert::toString(i)), - iox::capro::IdString_t(iox::cxx::TruncateToCapacity, iox::cxx::convert::toString(i)), - iox::capro::IdString_t(iox::cxx::TruncateToCapacity, iox::cxx::convert::toString(i))), - StubbedIceoryxTerminal::Options()); + sut->addChannel(iox::capro::ServiceDescription( + iox::capro::IdString_t(iox::cxx::TruncateToCapacity, iox::cxx::convert::toString(i)), + iox::capro::IdString_t(iox::cxx::TruncateToCapacity, iox::cxx::convert::toString(i)), + iox::capro::IdString_t(iox::cxx::TruncateToCapacity, iox::cxx::convert::toString(i))), + StubbedIceoryxTerminal::Options()); EXPECT_EQ(false, result.has_error()); } - EXPECT_EQ(iox::MAX_CHANNEL_NUMBER, gw.getNumberOfChannels()); + EXPECT_EQ(iox::MAX_CHANNEL_NUMBER, sut->getNumberOfChannels()); } TEST_F(GatewayGenericTest, ThrowsErrorWhenExceedingMaximumChannelCapaicity) { ::testing::Test::RecordProperty("TEST_ID", "f73c1fe0-d5d3-4527-9acb-29692c5fd19f"); // ===== Setup - TestGatewayGeneric gw{}; // ===== Test for (auto i = 0U; i < iox::MAX_CHANNEL_NUMBER; i++) { auto result = - gw.addChannel(iox::capro::ServiceDescription( - iox::capro::IdString_t(iox::cxx::TruncateToCapacity, iox::cxx::convert::toString(i)), - iox::capro::IdString_t(iox::cxx::TruncateToCapacity, iox::cxx::convert::toString(i)), - iox::capro::IdString_t(iox::cxx::TruncateToCapacity, iox::cxx::convert::toString(i))), - StubbedIceoryxTerminal::Options()); + sut->addChannel(iox::capro::ServiceDescription( + iox::capro::IdString_t(iox::cxx::TruncateToCapacity, iox::cxx::convert::toString(i)), + iox::capro::IdString_t(iox::cxx::TruncateToCapacity, iox::cxx::convert::toString(i)), + iox::capro::IdString_t(iox::cxx::TruncateToCapacity, iox::cxx::convert::toString(i))), + StubbedIceoryxTerminal::Options()); EXPECT_EQ(false, result.has_error()); } - auto result = gw.addChannel({"oneTooMany", "oneTooMany", "oneTooMany"}, StubbedIceoryxTerminal::Options()); + auto result = sut->addChannel({"oneTooMany", "oneTooMany", "oneTooMany"}, StubbedIceoryxTerminal::Options()); EXPECT_EQ(true, result.has_error()); EXPECT_EQ(iox::gw::GatewayError::UNSUCCESSFUL_CHANNEL_CREATION, result.get_error()); } @@ -192,16 +184,14 @@ TEST_F(GatewayGenericTest, ThrowsErrorWhenAttemptingToRemoveNonexistantChannel) auto testServiceB = iox::capro::ServiceDescription("serviceB", "instanceB", "eventB"); auto testServiceC = iox::capro::ServiceDescription("serviceC", "instanceC", "eventC"); - TestGatewayGeneric gw{}; - // ===== Test - ASSERT_FALSE(gw.addChannel(testServiceA, StubbedIceoryxTerminal::Options()).has_error()); - ASSERT_FALSE(gw.addChannel(testServiceB, StubbedIceoryxTerminal::Options()).has_error()); - EXPECT_EQ(2U, gw.getNumberOfChannels()); + ASSERT_FALSE(sut->addChannel(testServiceA, StubbedIceoryxTerminal::Options()).has_error()); + ASSERT_FALSE(sut->addChannel(testServiceB, StubbedIceoryxTerminal::Options()).has_error()); + EXPECT_EQ(2U, sut->getNumberOfChannels()); - auto result = gw.discardChannel(testServiceC); + auto result = sut->discardChannel(testServiceC); EXPECT_EQ(true, result.has_error()); - EXPECT_EQ(2U, gw.getNumberOfChannels()); + EXPECT_EQ(2U, sut->getNumberOfChannels()); } TEST_F(GatewayGenericTest, DiscardedChannelsAreNotStored) @@ -210,14 +200,12 @@ TEST_F(GatewayGenericTest, DiscardedChannelsAreNotStored) // ===== Setup auto testService = iox::capro::ServiceDescription("service", "instance", "event"); - TestGatewayGeneric gw{}; - // ===== Test - ASSERT_FALSE(gw.addChannel(testService, StubbedIceoryxTerminal::Options()).has_error()); - EXPECT_EQ(1U, gw.getNumberOfChannels()); - auto result = gw.discardChannel(testService); + ASSERT_FALSE(sut->addChannel(testService, StubbedIceoryxTerminal::Options()).has_error()); + EXPECT_EQ(1U, sut->getNumberOfChannels()); + auto result = sut->discardChannel(testService); EXPECT_EQ(false, result.has_error()); - EXPECT_EQ(0U, gw.getNumberOfChannels()); + EXPECT_EQ(0U, sut->getNumberOfChannels()); } TEST_F(GatewayGenericTest, FindChannelReturnsCopyOfFoundChannel) @@ -226,11 +214,9 @@ TEST_F(GatewayGenericTest, FindChannelReturnsCopyOfFoundChannel) // ===== Setup auto testService = iox::capro::ServiceDescription("service", "instance", "event"); - TestGatewayGeneric gw{}; - // ===== Test - ASSERT_FALSE(gw.addChannel(testService, StubbedIceoryxTerminal::Options()).has_error()); - auto foundChannel = gw.findChannel(testService); + ASSERT_FALSE(sut->addChannel(testService, StubbedIceoryxTerminal::Options()).has_error()); + auto foundChannel = sut->findChannel(testService); EXPECT_EQ(true, foundChannel.has_value()); if (foundChannel.has_value()) { @@ -245,11 +231,9 @@ TEST_F(GatewayGenericTest, FindChannelGivesEmptyOptionalIfNoneFound) auto storedChannelService = iox::capro::ServiceDescription("service", "instance", "event"); auto notStoredChannelService = iox::capro::ServiceDescription("otherService", "otherInstance", "otherEvent"); - TestGatewayGeneric gw{}; - // ===== Test - ASSERT_FALSE(gw.addChannel(storedChannelService, StubbedIceoryxTerminal::Options()).has_error()); - auto foundChannel = gw.findChannel(notStoredChannelService); + ASSERT_FALSE(sut->addChannel(storedChannelService, StubbedIceoryxTerminal::Options()).has_error()); + auto foundChannel = sut->findChannel(notStoredChannelService); EXPECT_EQ(false, foundChannel.has_value()); } @@ -264,13 +248,11 @@ TEST_F(GatewayGenericTest, ForEachChannelExecutesGivenFunctionForAllStoredChanne auto count = 0U; auto f = [&count](TestChannel&) { count++; }; - TestGatewayGeneric gw{}; - // ===== Test - ASSERT_FALSE(gw.addChannel(testServiceA, StubbedIceoryxTerminal::Options()).has_error()); - ASSERT_FALSE(gw.addChannel(testServiceB, StubbedIceoryxTerminal::Options()).has_error()); - ASSERT_FALSE(gw.addChannel(testServiceC, StubbedIceoryxTerminal::Options()).has_error()); - gw.forEachChannel(f); + ASSERT_FALSE(sut->addChannel(testServiceA, StubbedIceoryxTerminal::Options()).has_error()); + ASSERT_FALSE(sut->addChannel(testServiceB, StubbedIceoryxTerminal::Options()).has_error()); + ASSERT_FALSE(sut->addChannel(testServiceC, StubbedIceoryxTerminal::Options()).has_error()); + sut->forEachChannel(f); EXPECT_EQ(3U, count); } diff --git a/iceoryx_posh/test/moduletests/test_popo_toml_gateway_config_parser.cpp b/iceoryx_posh/test/moduletests/test_popo_toml_gateway_config_parser.cpp index dc6b6d5722..ef87afdaef 100644 --- a/iceoryx_posh/test/moduletests/test_popo_toml_gateway_config_parser.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_toml_gateway_config_parser.cpp @@ -15,6 +15,9 @@ // // SPDX-License-Identifier: Apache-2.0 +// iox::config::GatewayConfig uses 1MB on the stack which is way too much for QNX +#if !(defined(QNX) || defined(QNX__) || defined(__QNX__)) + #include "iceoryx/tests/posh/moduletests/test_input_path.hpp" #include "iceoryx_posh/gateway/toml_gateway_config_parser.hpp" #include "stubs/stub_toml_gateway_config_parser.hpp" @@ -414,3 +417,5 @@ TEST_P(TomlGatewayConfigParserTest, ParseMalformedInputFileCausesError) ASSERT_TRUE(result.has_error()); EXPECT_EQ(parseErrorInputFile.first, result.get_error()); } + +#endif // not defined QNX diff --git a/iceoryx_posh/test/moduletests/test_posh_runtime.cpp b/iceoryx_posh/test/moduletests/test_posh_runtime.cpp index 4075339185..7972c82003 100644 --- a/iceoryx_posh/test/moduletests/test_posh_runtime.cpp +++ b/iceoryx_posh/test/moduletests/test_posh_runtime.cpp @@ -143,8 +143,7 @@ TEST_F(PoshRuntime_test, NoAppName) ::testing::Test::RecordProperty("TEST_ID", "e053d114-c79c-4391-91e1-8fcfe90ee8e4"); const iox::RuntimeName_t invalidAppName(""); - EXPECT_DEATH({ PoshRuntime::initRuntime(invalidAppName); }, - "Cannot initialize runtime. Application name must not be empty!"); + EXPECT_DEATH({ PoshRuntime::initRuntime(invalidAppName); }, ""); } // To be able to test the singleton and avoid return the exisiting instance, we don't use the test fixture diff --git a/iceoryx_posh/test/moduletests/test_posh_runtime_single_process.cpp b/iceoryx_posh/test/moduletests/test_posh_runtime_single_process.cpp index 91ee3e74f0..452a299c65 100644 --- a/iceoryx_posh/test/moduletests/test_posh_runtime_single_process.cpp +++ b/iceoryx_posh/test/moduletests/test_posh_runtime_single_process.cpp @@ -49,23 +49,24 @@ TEST_F(PoshRuntimeSingleProcess_test, ConstructorPoshRuntimeSingleProcessIsSucce { ::testing::Test::RecordProperty("TEST_ID", "9faf7053-86af-4d26-b3a7-fb3c6319ab86"); iox::RouDiConfig_t defaultRouDiConfig = iox::RouDiConfig_t().setDefaults(); - IceOryxRouDiComponents roudiComponents(defaultRouDiConfig); + std::unique_ptr roudiComponents{new IceOryxRouDiComponents(defaultRouDiConfig)}; - RouDi roudi(roudiComponents.rouDiMemoryManager, - roudiComponents.portManager, - RouDi::RoudiStartupParameters{iox::roudi::MonitoringMode::OFF, false}); + std::unique_ptr roudi{new RouDi(roudiComponents->rouDiMemoryManager, + roudiComponents->portManager, + RouDi::RoudiStartupParameters{iox::roudi::MonitoringMode::OFF, false})}; - const RuntimeName_t m_runtimeName{"App"}; + const RuntimeName_t runtimeName{"App"}; - EXPECT_NO_FATAL_FAILURE({ PoshRuntimeSingleProcess m_runtimeSingleProcess(m_runtimeName); }); + EXPECT_NO_FATAL_FAILURE( + { std::unique_ptr sut{new PoshRuntimeSingleProcess(runtimeName)}; }); } TEST_F(PoshRuntimeSingleProcess_test, ConstructorPoshRuntimeSingleProcessMultipleProcessIsFound) { ::testing::Test::RecordProperty("TEST_ID", "1cc7ad5d-5878-454a-94ba-5cf412c22682"); - RouDiEnvironment m_roudiEnv{iox::RouDiConfig_t().setDefaults()}; + RouDiEnvironment roudiEnv{iox::RouDiConfig_t().setDefaults()}; - const RuntimeName_t m_runtimeName{"App"}; + const RuntimeName_t runtimeName{"App"}; iox::cxx::optional detectedError; auto errorHandlerGuard = iox::ErrorHandler::setTemporaryErrorHandler( @@ -74,7 +75,7 @@ TEST_F(PoshRuntimeSingleProcess_test, ConstructorPoshRuntimeSingleProcessMultipl EXPECT_THAT(errorLevel, Eq(iox::ErrorLevel::FATAL)); }); - PoshRuntimeSingleProcess m_runtimeSingleProcess(m_runtimeName); + std::unique_ptr sut{new PoshRuntimeSingleProcess(runtimeName)}; ASSERT_THAT(detectedError.has_value(), Eq(true)); EXPECT_THAT(detectedError.value(), Eq(iox::Error::kPOSH__RUNTIME_IS_CREATED_MULTIPLE_TIMES)); diff --git a/iceoryx_posh/test/moduletests/test_roudi_portmanager.cpp b/iceoryx_posh/test/moduletests/test_roudi_portmanager.cpp index 567023dc63..acd162b7a5 100644 --- a/iceoryx_posh/test/moduletests/test_roudi_portmanager.cpp +++ b/iceoryx_posh/test/moduletests/test_roudi_portmanager.cpp @@ -528,9 +528,9 @@ TEST_F(PortManager_test, SubscriberNotRequiringHistorySupportDoesConnectToPublis EXPECT_TRUE(publisher.hasSubscribers()); } -TEST_F(PortManager_test, SubscriberRequiringHistorySupportDoesConnectToPublisherWithSufficientHistorySupport) +TEST_F(PortManager_test, SubscriberRequiringHistorySupportDoesConnectToPublisherWithEqualHistorySupport) { - ::testing::Test::RecordProperty("TEST_ID", "e2567667-4583-482b-9999-029f91c0cb71"); + ::testing::Test::RecordProperty("TEST_ID", "20749a22-2771-4ec3-92f8-81bbdbd4aab6"); auto publisherOptions = createTestPubOptions(); auto subscriberOptions = createTestSubOptions(); @@ -547,15 +547,15 @@ TEST_F(PortManager_test, SubscriberRequiringHistorySupportDoesConnectToPublisher EXPECT_TRUE(publisher.hasSubscribers()); } -TEST_F(PortManager_test, SubscriberRequiringHistorySupportDoesNotConnectToPublisherWithInsufficientHistorySupport) +TEST_F(PortManager_test, SubscriberRequiringHistorySupportDoesConnectToPublisherWithLowerHistorySupport) { - ::testing::Test::RecordProperty("TEST_ID", "20749a22-2771-4ec3-92f8-81bbdbd4aab6"); + ::testing::Test::RecordProperty("TEST_ID", "e2567667-4583-482b-9999-029f91c0cb71"); auto publisherOptions = createTestPubOptions(); auto subscriberOptions = createTestSubOptions(); - publisherOptions.historyCapacity = 2; - subscriberOptions.historyRequest = 3; + publisherOptions.historyCapacity = 5; + subscriberOptions.historyRequest = 6; subscriberOptions.requiresPublisherHistorySupport = true; auto publisher = createPublisher(publisherOptions); @@ -563,10 +563,10 @@ TEST_F(PortManager_test, SubscriberRequiringHistorySupportDoesNotConnectToPublis ASSERT_TRUE(publisher); ASSERT_TRUE(subscriber); - EXPECT_FALSE(publisher.hasSubscribers()); + EXPECT_TRUE(publisher.hasSubscribers()); } -TEST_F(PortManager_test, SubscriberNotRequiringHistorySupportDoesConnectToPublisherWithInsufficientHistorySupport) +TEST_F(PortManager_test, SubscriberNotRequiringHistorySupportDoesConnectToPublisherWithLowerHistorySupport) { ::testing::Test::RecordProperty("TEST_ID", "e6c7cee4-cb4a-4a14-8790-4dbfce7d8584"); diff --git a/iceoryx_posh/test/moduletests/test_roudi_process_introspection.cpp b/iceoryx_posh/test/moduletests/test_roudi_process_introspection.cpp index 8c38edfe6e..656adddf29 100644 --- a/iceoryx_posh/test/moduletests/test_roudi_process_introspection.cpp +++ b/iceoryx_posh/test/moduletests/test_roudi_process_introspection.cpp @@ -65,17 +65,18 @@ class ProcessIntrospection_test : public Test } } - ChunkMock* createMemoryChunkAndSend(ProcessIntrospectionAccess& introspectionAccess) + ChunkMock* createMemoryChunkAndSend(ProcessIntrospectionAccess& sut) { - EXPECT_CALL(introspectionAccess.getPublisherPort().value(), tryAllocateChunk(_, _, _, _)) + EXPECT_CALL(sut.getPublisherPort().value(), tryAllocateChunk(_, _, _, _)) .WillOnce(Return(iox::cxx::expected::create_value( m_chunk.get()->chunkHeader()))); bool chunkWasSent = false; - EXPECT_CALL(introspectionAccess.getPublisherPort().value(), sendChunk(_)) - .WillOnce(Invoke([&](iox::mepoo::ChunkHeader* const) { chunkWasSent = true; })); + EXPECT_CALL(sut.getPublisherPort().value(), sendChunk(_)).WillOnce(Invoke([&](iox::mepoo::ChunkHeader* const) { + chunkWasSent = true; + })); - introspectionAccess.send(); + sut.send(); return chunkWasSent ? m_chunk.get() : nullptr; } @@ -88,8 +89,8 @@ TEST_F(ProcessIntrospection_test, CTOR) { ::testing::Test::RecordProperty("TEST_ID", "74c1c79f-3c99-406b-8ccf-9e85defbd71b"); { - ProcessIntrospectionAccess introspectionAccess; - EXPECT_THAT(introspectionAccess.getPublisherPort().has_value(), Eq(false)); + std::unique_ptr introspectionAccess{new ProcessIntrospectionAccess()}; + EXPECT_THAT(introspectionAccess->getPublisherPort().has_value(), Eq(false)); } } @@ -97,9 +98,9 @@ TEST_F(ProcessIntrospection_test, registerPublisherPort) { ::testing::Test::RecordProperty("TEST_ID", "fcacaa4a-7883-43d6-850f-04b78558e45b"); { - ProcessIntrospectionAccess introspectionAccess; - introspectionAccess.registerPublisherPort(std::move(m_mockPublisherPortUserIntrospection)); - EXPECT_CALL(introspectionAccess.getPublisherPort().value(), stopOffer()).Times(1); + std::unique_ptr introspectionAccess{new ProcessIntrospectionAccess()}; + introspectionAccess->registerPublisherPort(std::move(m_mockPublisherPortUserIntrospection)); + EXPECT_CALL(introspectionAccess->getPublisherPort().value(), stopOffer()).Times(1); } } @@ -107,13 +108,13 @@ TEST_F(ProcessIntrospection_test, send) { ::testing::Test::RecordProperty("TEST_ID", "7faf7880-c9be-4893-8f68-15cc77a4583c"); { - ProcessIntrospectionAccess introspectionAccess; - introspectionAccess.registerPublisherPort(std::move(m_mockPublisherPortUserIntrospection)); + std::unique_ptr introspectionAccess{new ProcessIntrospectionAccess()}; + introspectionAccess->registerPublisherPort(std::move(m_mockPublisherPortUserIntrospection)); - auto chunk = createMemoryChunkAndSend(introspectionAccess); + auto chunk = createMemoryChunkAndSend(*introspectionAccess); ASSERT_THAT(chunk, Ne(nullptr)); EXPECT_THAT(chunk->sample()->m_processList.size(), Eq(0U)); - EXPECT_CALL(introspectionAccess.getPublisherPort().value(), stopOffer()).Times(1); + EXPECT_CALL(introspectionAccess->getPublisherPort().value(), stopOffer()).Times(1); } } @@ -121,36 +122,36 @@ TEST_F(ProcessIntrospection_test, addRemoveProcess) { ::testing::Test::RecordProperty("TEST_ID", "50d5090f-c89e-400f-b400-313df15d4193"); { - ProcessIntrospectionAccess introspectionAccess; - introspectionAccess.registerPublisherPort(std::move(m_mockPublisherPortUserIntrospection)); + std::unique_ptr introspectionAccess{new ProcessIntrospectionAccess()}; + introspectionAccess->registerPublisherPort(std::move(m_mockPublisherPortUserIntrospection)); const int PID = 42; const char PROCESS_NAME[] = "/chuck_norris"; // invalid removal doesn't cause problems - introspectionAccess.removeProcess(PID); - auto chunk1 = createMemoryChunkAndSend(introspectionAccess); + introspectionAccess->removeProcess(PID); + auto chunk1 = createMemoryChunkAndSend(*introspectionAccess); ASSERT_THAT(chunk1, Ne(nullptr)); EXPECT_THAT(chunk1->sample()->m_processList.size(), Eq(0U)); // a new process should be sent - introspectionAccess.addProcess(PID, iox::RuntimeName_t(PROCESS_NAME)); - auto chunk2 = createMemoryChunkAndSend(introspectionAccess); + introspectionAccess->addProcess(PID, iox::RuntimeName_t(PROCESS_NAME)); + auto chunk2 = createMemoryChunkAndSend(*introspectionAccess); ASSERT_THAT(chunk2, Ne(nullptr)); EXPECT_THAT(chunk2->sample()->m_processList.size(), Eq(1U)); EXPECT_THAT(chunk2->sample()->m_processList[0].m_pid, Eq(PID)); EXPECT_THAT(iox::RuntimeName_t(PROCESS_NAME) == chunk2->sample()->m_processList[0].m_name, Eq(true)); // list should be empty after removal - introspectionAccess.removeProcess(PID); - auto chunk3 = createMemoryChunkAndSend(introspectionAccess); + introspectionAccess->removeProcess(PID); + auto chunk3 = createMemoryChunkAndSend(*introspectionAccess); ASSERT_THAT(chunk3, Ne(nullptr)); EXPECT_THAT(chunk3->sample()->m_processList.size(), Eq(0U)); // if there isn't any change, no data are deliverd - EXPECT_CALL(introspectionAccess.getPublisherPort().value(), sendChunk(_)).Times(0); - EXPECT_CALL(introspectionAccess.getPublisherPort().value(), stopOffer()).Times(1); - introspectionAccess.send(); + EXPECT_CALL(introspectionAccess->getPublisherPort().value(), sendChunk(_)).Times(0); + EXPECT_CALL(introspectionAccess->getPublisherPort().value(), stopOffer()).Times(1); + introspectionAccess->send(); } } @@ -161,40 +162,40 @@ TEST_F(ProcessIntrospection_test, thread) const int PID = 42; const char PROCESS_NAME[] = "/chuck_norris"; - ProcessIntrospectionAccess introspectionAccess; + std::unique_ptr introspectionAccess{new ProcessIntrospectionAccess()}; - introspectionAccess.registerPublisherPort(std::move(m_mockPublisherPortUserIntrospection)); + introspectionAccess->registerPublisherPort(std::move(m_mockPublisherPortUserIntrospection)); - EXPECT_CALL(introspectionAccess.getPublisherPort().value(), tryAllocateChunk(_, _, _, _)) + EXPECT_CALL(introspectionAccess->getPublisherPort().value(), tryAllocateChunk(_, _, _, _)) .WillRepeatedly( Return(iox::cxx::expected::create_value( m_chunk.get()->chunkHeader()))); - EXPECT_CALL(introspectionAccess.getPublisherPort().value(), hasSubscribers()).WillRepeatedly(Return(true)); - EXPECT_CALL(introspectionAccess.getPublisherPort().value(), offer()).Times(1); - EXPECT_CALL(introspectionAccess.getPublisherPort().value(), sendChunk(_)).Times(Between(2, 8)); + EXPECT_CALL(introspectionAccess->getPublisherPort().value(), hasSubscribers()).WillRepeatedly(Return(true)); + EXPECT_CALL(introspectionAccess->getPublisherPort().value(), offer()).Times(1); + EXPECT_CALL(introspectionAccess->getPublisherPort().value(), sendChunk(_)).Times(Between(2, 8)); using namespace iox::units::duration_literals; - introspectionAccess.setSendInterval(10_ms); - introspectionAccess.run(); + introspectionAccess->setSendInterval(10_ms); + introspectionAccess->run(); for (size_t i = 0; i < 3; ++i) { - introspectionAccess.addProcess(PID, iox::RuntimeName_t(PROCESS_NAME)); + introspectionAccess->addProcess(PID, iox::RuntimeName_t(PROCESS_NAME)); std::this_thread::sleep_for(std::chrono::milliseconds(15)); - introspectionAccess.removeProcess(PID); + introspectionAccess->removeProcess(PID); std::this_thread::sleep_for(std::chrono::milliseconds(15)); } - introspectionAccess.stop(); + introspectionAccess->stop(); for (size_t i = 0; i < 3; ++i) // within this time, the thread should have sent the 6 updates { - introspectionAccess.stop(); + introspectionAccess->stop(); std::this_thread::sleep_for(std::chrono::milliseconds(15)); - introspectionAccess.addProcess(PID, iox::RuntimeName_t(PROCESS_NAME)); + introspectionAccess->addProcess(PID, iox::RuntimeName_t(PROCESS_NAME)); std::this_thread::sleep_for(std::chrono::milliseconds(15)); - introspectionAccess.removeProcess(PID); + introspectionAccess->removeProcess(PID); } } } @@ -203,9 +204,9 @@ TEST_F(ProcessIntrospection_test, addRemoveNode) { ::testing::Test::RecordProperty("TEST_ID", "edae5283-400b-44c5-882c-60ad9bfb7957"); { - ProcessIntrospectionAccess introspectionAccess; + std::unique_ptr introspectionAccess{new ProcessIntrospectionAccess()}; - introspectionAccess.registerPublisherPort(std::move(m_mockPublisherPortUserIntrospection)); + introspectionAccess->registerPublisherPort(std::move(m_mockPublisherPortUserIntrospection)); const int PID = 42; const char PROCESS_NAME[] = "/chuck_norris"; @@ -214,60 +215,60 @@ TEST_F(ProcessIntrospection_test, addRemoveNode) const char NODE_3[] = "the_hitman"; // invalid removal of unknown node of unknown process - introspectionAccess.removeNode(iox::RuntimeName_t(PROCESS_NAME), iox::NodeName_t(NODE_1)); - auto chunk1 = createMemoryChunkAndSend(introspectionAccess); + introspectionAccess->removeNode(iox::RuntimeName_t(PROCESS_NAME), iox::NodeName_t(NODE_1)); + auto chunk1 = createMemoryChunkAndSend(*introspectionAccess); ASSERT_THAT(chunk1, Ne(nullptr)); EXPECT_THAT(chunk1->sample()->m_processList.size(), Eq(0U)); // a new process - introspectionAccess.addProcess(PID, iox::RuntimeName_t(PROCESS_NAME)); + introspectionAccess->addProcess(PID, iox::RuntimeName_t(PROCESS_NAME)); // invalid removal of unknown node of known process - introspectionAccess.removeNode(iox::RuntimeName_t(PROCESS_NAME), iox::NodeName_t(NODE_1)); - auto chunk2 = createMemoryChunkAndSend(introspectionAccess); + introspectionAccess->removeNode(iox::RuntimeName_t(PROCESS_NAME), iox::NodeName_t(NODE_1)); + auto chunk2 = createMemoryChunkAndSend(*introspectionAccess); ASSERT_THAT(chunk2, Ne(nullptr)); EXPECT_THAT(chunk2->sample()->m_processList.size(), Eq(1U)); EXPECT_THAT(chunk2->sample()->m_processList[0].m_nodes.size(), Eq(0U)); // add a node - introspectionAccess.addNode(iox::RuntimeName_t(PROCESS_NAME), iox::NodeName_t(NODE_1)); - auto chunk3 = createMemoryChunkAndSend(introspectionAccess); + introspectionAccess->addNode(iox::RuntimeName_t(PROCESS_NAME), iox::NodeName_t(NODE_1)); + auto chunk3 = createMemoryChunkAndSend(*introspectionAccess); ASSERT_THAT(chunk3, Ne(nullptr)); EXPECT_THAT(chunk3->sample()->m_processList.size(), Eq(1U)); EXPECT_THAT(chunk3->sample()->m_processList[0].m_nodes.size(), Eq(1U)); // add it again, must be ignored - introspectionAccess.addNode(iox::RuntimeName_t(PROCESS_NAME), iox::NodeName_t(NODE_1)); - auto chunk4 = createMemoryChunkAndSend(introspectionAccess); + introspectionAccess->addNode(iox::RuntimeName_t(PROCESS_NAME), iox::NodeName_t(NODE_1)); + auto chunk4 = createMemoryChunkAndSend(*introspectionAccess); ASSERT_THAT(chunk4, Ne(nullptr)); EXPECT_THAT(chunk4->sample()->m_processList.size(), Eq(1U)); EXPECT_THAT(chunk4->sample()->m_processList[0].m_nodes.size(), Eq(1U)); // add some more - introspectionAccess.addNode(iox::RuntimeName_t(PROCESS_NAME), iox::NodeName_t(NODE_2)); - introspectionAccess.addNode(iox::RuntimeName_t(PROCESS_NAME), iox::NodeName_t(NODE_3)); - auto chunk5 = createMemoryChunkAndSend(introspectionAccess); + introspectionAccess->addNode(iox::RuntimeName_t(PROCESS_NAME), iox::NodeName_t(NODE_2)); + introspectionAccess->addNode(iox::RuntimeName_t(PROCESS_NAME), iox::NodeName_t(NODE_3)); + auto chunk5 = createMemoryChunkAndSend(*introspectionAccess); ASSERT_THAT(chunk5, Ne(nullptr)); EXPECT_THAT(chunk5->sample()->m_processList.size(), Eq(1U)); EXPECT_THAT(chunk5->sample()->m_processList[0].m_nodes.size(), Eq(3U)); // remove some nodes - introspectionAccess.removeNode(iox::RuntimeName_t(PROCESS_NAME), iox::NodeName_t(NODE_1)); - introspectionAccess.removeNode(iox::RuntimeName_t(PROCESS_NAME), iox::NodeName_t(NODE_3)); - auto chunk6 = createMemoryChunkAndSend(introspectionAccess); + introspectionAccess->removeNode(iox::RuntimeName_t(PROCESS_NAME), iox::NodeName_t(NODE_1)); + introspectionAccess->removeNode(iox::RuntimeName_t(PROCESS_NAME), iox::NodeName_t(NODE_3)); + auto chunk6 = createMemoryChunkAndSend(*introspectionAccess); ASSERT_THAT(chunk6, Ne(nullptr)); EXPECT_THAT(chunk6->sample()->m_processList.size(), Eq(1U)); EXPECT_THAT(chunk6->sample()->m_processList[0].m_nodes.size(), Eq(1U)); EXPECT_THAT(strcmp(NODE_2, chunk6->sample()->m_processList[0].m_nodes[0].c_str()), Eq(0)); // remove last node, list empty again - introspectionAccess.removeNode(iox::RuntimeName_t(PROCESS_NAME), iox::NodeName_t(NODE_2)); - auto chunk7 = createMemoryChunkAndSend(introspectionAccess); + introspectionAccess->removeNode(iox::RuntimeName_t(PROCESS_NAME), iox::NodeName_t(NODE_2)); + auto chunk7 = createMemoryChunkAndSend(*introspectionAccess); ASSERT_THAT(chunk7, Ne(nullptr)); EXPECT_THAT(chunk7->sample()->m_processList.size(), Eq(1U)); EXPECT_THAT(chunk7->sample()->m_processList[0].m_nodes.size(), Eq(0U)); - EXPECT_CALL(introspectionAccess.getPublisherPort().value(), stopOffer()).Times(1); + EXPECT_CALL(introspectionAccess->getPublisherPort().value(), stopOffer()).Times(1); } } diff --git a/tools/ci/build-test-windows.ps1 b/tools/ci/build-test-windows.ps1 index a280ea8712..4992457ab2 100755 --- a/tools/ci/build-test-windows.ps1 +++ b/tools/ci/build-test-windows.ps1 @@ -27,10 +27,10 @@ if ($?) { Write-Host "running tests (excluding timing_tests)" } # until the windows support is fully implemented and we can use the windows cmake targets # we have to exclude the tests explicitly until everyone is running -if ($?) { build\hoofs\test\Debug\hoofs_moduletests.exe --gtest_filter="-SharedMemory_Test.CreateOrOpenCreatesShmWhenShmDoesNotExist:SharedMemory_Test.OpenFailsWhenShmDoesNotExist:*TimingTest*" } +if ($?) { build\hoofs\test\Debug\hoofs_moduletests.exe --gtest_filter="-*TimingTest*" } if ($?) { build\hoofs\test\Debug\hoofs_integrationtests.exe } if ($?) { build\binding_c\test\Debug\binding_c_moduletests.exe --gtest_filter="-BindingC_Runtime_test.RuntimeNameLengthIsOutOfLimit:BindingC_Runtime_test.RuntimeNameIsNullptr:*TimingTest*" } -if ($?) { build\posh\test\Debug\posh_moduletests.exe --gtest_filter="-ChunkHeader_test.ChunkHeaderBinaryCompatibilityCheck:TomlGatewayConfigParserSuiteTest*:IceoryxRoudiApp_test*:PoshRuntime_test.NoAppName:ValidTest*:ParseAllMalformedInput*:*TimingTest*" } +if ($?) { build\posh\test\Debug\posh_moduletests.exe --gtest_filter="-ChunkHeader_test.ChunkHeaderBinaryCompatibilityCheck:TomlGatewayConfigParserSuiteTest*:IceoryxRoudiApp_test.ConstructorCalledWithArgUniqueIdTwoTimesReturnError:IceoryxRoudiApp_test.ConstructorCalledWithArgVersionSetRunVariableToFalse:ValidTest*:ParseAllMalformedInput*:*TimingTest*" } if ($?) { build\posh\test\Debug\posh_integrationtests.exe --gtest_filter="-ChunkBuildingBlocks_IntegrationTest.TwoHopsThreeThreadsNoSoFi:*TimingTest*" } exit $LASTEXITCODE diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile index 19e7278242..403363939b 100644 --- a/tools/docker/Dockerfile +++ b/tools/docker/Dockerfile @@ -40,7 +40,7 @@ RUN apt-get update && apt-get install -y \ ADD . /iceoryx WORKDIR /iceoryx -RUN ./tools/iceoryx_build_test.sh build-all +RUN ./tools/iceoryx_build_test.sh clean build-all # ======================================================================================================================== # diff --git a/tools/docker/README.md b/tools/docker/README.md index 3cc30cd993..3fd4d43411 100644 --- a/tools/docker/README.md +++ b/tools/docker/README.md @@ -1,37 +1,52 @@ # Overview -This directory contains files related to building and running [Eclipse iceoryx](https://github.com/eclipse-iceoryx/iceoryx) using [Docker](https://www.docker.com/). +This directory contains files related to building and running +[Eclipse iceoryx](https://github.com/eclipse-iceoryx/iceoryx) using +[Docker](https://www.docker.com/). # Building the iceoryx Docker Image -To create a Docker image with the built iceoryx libraries and examples from your current sources, run the following script from the root of the repository: `./tools/docker/build_iceoryx_docker.sh`. +To create a Docker image with pre built iceoryx libraries and examples from your +current sources, run the script +```sh +./tools/docker/build_iceoryx_docker.sh +``` +from the root of the repository. + -After the Docker image is built, you can run it in interactive mode then use one of the below options to connect to it and run the example applications. +After the Docker image is built, you can run it in interactive mode and use one +of the options below to connect to the container and run the example applications. # Running an iceoryx Docker container -A helper script is provided to launch containers for ready-built containers. +A helper script is provided to launch containers for pre-built containers. Simply run the following script from any location: -``` +```sh ./tools/docker/run_iceoryx_docker.sh ``` # Connecting to the Docker container and running the example applications -With the iceoryx Docker container running RouDi, you can connect to it to play with the example applications. -There are a couple of methods available to connect to do this. +When the iceoryx Docker container is up and running with a RouDi instance, you can +connect to it and start various example applications. +We have a couple of methods available. ## Using `docker exec` -It is possible to bind a shell to running containers via `docker exec`. A helper script is provided with the command for convenience. -To bind a shell to the container, simply run the following script from any location: -``` +It is possible to bind a shell to a running container via `docker exec`. A helper +script is provided with the command for convenience. +To bind a shell to the container simply run the following script from any location: +```sh ./tools/docker/bind_iceoryx_docker.sh ``` You will then be dropped into a bash shell where you can access all iceoryx binaries. -All iceoryx binaries are directly available in the $PATH of the shell. -For example, to start a sender application, you can run the following from the bound bash shell: +All iceoryx binaries are installed in `/usr/bin/` and can be used directly. If you +are unsure which iceoryx commands are available type `ls /usr/bin/iox-*` to acquire +a complete list. + +For example, to start a sender application, you can run the following command from +the bound bash shell: ``` root@b10b3630f6d3:/# iox-cpp-publisher-untyped @@ -39,7 +54,8 @@ root@b10b3630f6d3:/# iox-cpp-publisher-untyped 2020-12-18 09:04:12.813 [ Info ]: Application registered payload segment 0x7fa19b98a000 with size 149134400 to id 2 ``` -To run the corresponding receiver application, bind another shell to the container and run the following: +To run the corresponding receiver application bind another shell to the container +with `./tools/docker/bind_iceoryx_docker.sh` and run the following: ``` root@b10b3630f6d3:/# iox-cpp-subscriber-untyped @@ -55,13 +71,19 @@ Got value: 12 The complete communication flow should now be observable. -There are a lot more binaries to test, detailed explanations for each can be found in [iceoryx_examples](./../../iceoryx_examples). +There are a lot more binaries to test, detailed explanations for each can be +found in [iceoryx_examples](./../../iceoryx_examples). ## Using `screen` -Another way of interacting with the daemon and examples is to use [screen](https://www.gnu.org/software/screen/) (also included in the Docker image) to start executables in different virtual screens that you attach to and detach from as your experiments may require. +Another way of interacting with the daemon and examples is to use +[screen](https://www.gnu.org/software/screen/) (also included in the Docker image). +`screen` allows you to start executables in different virtual screens that you +attach to and detach from as your experiments may require. -This may be a better option if you are working on a remote machine and don't want to have to open multiple connections to it in separate terminals and/or if you're comfortable with using `screen`. +This may be a better option if you are working on a remote machine and don't +want to open multiple connections to it in separate terminals and/or if you're +comfortable with using `screen`. To start a session with screen, run the following from a bound bash shell: @@ -72,9 +94,9 @@ root@b10b3630f6d3:/# screen and press `[Enter]`. Inside the virtual screen shell, you can then launch the example applications. -Again, all iceoryx applications are directly available in the $PATH. +Again, all iceoryx applications are directly available in `/usr/bin/`. -To launch an example sender application, run the corresponding binary: +To launch an example sender application simply run the corresponding binary: ``` root@b10b3630f6d3 /# iox-cpp-publisher-untyped @@ -105,8 +127,9 @@ You can now detach from this virtual screen with `[Ctrl]+A D`. ### Exercises with iceoryx examples using screen -As an exercise, you can return to previous screen sessions to observe their current output. -For examples, here we list all screens and return to the one running the sender: +As an exercise, you can return to previous screen sessions to observe their current +output. For examples, here we list all screens and return to the one running the +sender: ``` root@b10b3630f6d3:/# screen -r @@ -116,22 +139,27 @@ There are several suitable screens on: Type "screen [-d] -r [pid.]tty.host" to resume one of them. ``` -Try the following to return to the sender application (adjust the PID as per your exact `screen -r` output above): +To attach a screen session use the PID listed as first number in the output above. +In our case we would like to attach to +`35.pts-0.b10b3630f6d3> (12/18/20 09:28:17)>(Detached)` our sender application. -``` +```sh screen -r 35 ``` -You should see again the virtual screen of the sender application. You can stop the sender with `[Ctrl]+C`, and then detach -from this screen with `[Ctrl]+A D`, and return to the receiver screen with (adjusting the PID as per your `screen -r` output above): +You should see again the virtual screen of the sender application. You can stop +the sender with `[Ctrl]+C`, then detach from this screen with `[Ctrl]+A D` +and return to the receiver screen with: -``` +```sh screen -r 66 ``` and you should see the receiver output again. -As an exercise, try return to the sender screen and relaunch the sender, then detach from the sender screen and attach to the receiver screen and see the output based on the new sender: +As an exercise, try return to the sender screen and relaunch the sender, then +detach from the sender screen and attach to the receiver screen and see the +output based on the new sender: ``` Got value: 104 diff --git a/tools/iceoryx_build_test.sh b/tools/iceoryx_build_test.sh index 5a09d02815..cc52e815e3 100755 --- a/tools/iceoryx_build_test.sh +++ b/tools/iceoryx_build_test.sh @@ -261,7 +261,7 @@ if [ "$PACKAGE" == "ON" ]; then fi # clean build folders -if [ $CLEAN_BUILD == true ] && [ -d "$BUILD_DIR" ]; then +if [ "$CLEAN_BUILD" == true ] && [ -d "$BUILD_DIR" ]; then echo " [i] Cleaning build directory" cd "$WORKSPACE" rm -rf "${BUILD_DIR:?}/"* diff --git a/tools/introspection/CMakeLists.txt b/tools/introspection/CMakeLists.txt index 576b9272bf..8946e16c94 100644 --- a/tools/introspection/CMakeLists.txt +++ b/tools/introspection/CMakeLists.txt @@ -17,7 +17,7 @@ cmake_minimum_required(VERSION 3.16) -set(IOX_VERSION_STRING "1.91.0") +set(IOX_VERSION_STRING "2.0.0") project(iceoryx_introspection VERSION ${IOX_VERSION_STRING})