diff --git a/.clang-tidy b/.clang-tidy index b1d2bfb93a..481e134407 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,4 +1,4 @@ -Checks: '-*,readability-*,performance-*,hicpp-*,cppcoreguidelines-*,concurrency-*,clang-analyzer-*,cert-*,bugprone-*' +Checks: '-*,readability-*,-readability-named-parameter,-readability-avoid-const-params-in-decls,performance-*,hicpp-*,-hicpp-named-parameter,-hicpp-avoid-c-arrays,cppcoreguidelines-*,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-pro-bounds-constant-array-index,concurrency-*,clang-analyzer-*,cert-*,bugprone-*' CheckOptions: - { key: readability-identifier-naming.ClassCase, value: CamelCase } - { key: readability-identifier-naming.EnumCase, value: CamelCase } diff --git a/.codecov.yml b/.codecov.yml index 96fe738553..67c32ab7f4 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,13 +1,13 @@ # codecov yaml -comment: yes # do not comment PR with the result +comment: layout: "reach, diff, flags, files" behavior: default - require_changes: yes # only post the comment if coverage changes + require_changes: yes require_base: no require_head: yes coverage: - range: 70..100 + range: 70..100 status: project: @@ -19,8 +19,9 @@ coverage: default: enabled: yes target: 70% - threshold: 2% - changes: yes + threshold: 2% + base: auto + changes: no parsers: gcov: diff --git a/.github/workflows/colcon.yml b/.github/workflows/integrationtest.yml similarity index 79% rename from .github/workflows/colcon.yml rename to .github/workflows/integrationtest.yml index f25afdef62..37ce36f73b 100644 --- a/.github/workflows/colcon.yml +++ b/.github/workflows/integrationtest.yml @@ -1,6 +1,6 @@ # This is a basic workflow to help you get started with Actions -name: Colcon build +name: Iceoryx Integrationtests # Controls when the action will run. Triggers the workflow on push or pull request # events but only for the master branch @@ -32,7 +32,7 @@ jobs: - name: Install Iceoryx Dependencies run: | sudo apt install -y apt-transport-https - sudo apt update && sudo apt install -y cmake libacl1-dev libncurses5-dev pkg-config + sudo apt update && sudo apt install -y cmake libacl1-dev libncurses5-dev pkg-config ros-foxy-ros-testing mkdir -p src/iceoryx cd $GITHUB_WORKSPACE/src/iceoryx @@ -48,7 +48,16 @@ jobs: sudo sed -i '41 c\"'c'", "'cc'", "'cpp'", "'cxx'", "'h'", "'hh'", "'hpp'", "'hxx'", "'inl'", "'sh'"' /opt/ros/foxy/lib/python3.8/site-packages/ament_copyright/main.py ament_copyright --exclude LICENSE CONTRIBUTING.md tools/apache2_header.txt - - name: Build & Test + - name: Build run: | + source /opt/ros/foxy/setup.bash cd $GITHUB_WORKSPACE colcon build + + - name: Test Execution + run: | + source /opt/ros/foxy/setup.bash + source $GITHUB_WORKSPACE/install/setup.bash + cd $GITHUB_WORKSPACE + colcon test --packages-select iceoryx_integrationtest + colcon test-result --all --verbose diff --git a/.gitignore b/.gitignore index 43d276167c..2a718ec7d3 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ clangd/ site/ doc/website/API-reference/ *.project +__pycache__ diff --git a/README.md b/README.md index bdede717da..43f8a234cc 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

[![Build & Test](https://github.com/eclipse-iceoryx/iceoryx/workflows/Build%20&%20Test/badge.svg?branch=master)](https://github.com/eclipse-iceoryx/iceoryx/actions) -[![Colcon build](https://github.com/eclipse-iceoryx/iceoryx/workflows/Colcon%20build/badge.svg?branch=master)](https://github.com/eclipse-iceoryx/iceoryx/actions) +[![Integrationtests](https://github.com/eclipse-iceoryx/iceoryx/workflows/Iceoryx%20Integrationtests/badge.svg?branch=master)](https://github.com/eclipse-iceoryx/iceoryx/actions) [![Gitter](https://badges.gitter.im/eclipse-iceoryx/iceoryx.svg)](https://gitter.im/eclipse/iceoryx) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Codecov](https://codecov.io/gh/eclipse-iceoryx/iceoryx/branch/master/graph/badge.svg?branch=master)](https://codecov.io/gh/eclipse-iceoryx/iceoryx?branch=master) diff --git a/cmake/cpptoml/CMakeLists.txt b/cmake/cpptoml/CMakeLists.txt index 9b01b0920b..2e950a86ea 100644 --- a/cmake/cpptoml/CMakeLists.txt +++ b/cmake/cpptoml/CMakeLists.txt @@ -33,6 +33,12 @@ else() set(CREATE_PATH_COMMAND mkdir -p) endif() +if(DEFINED CMAKE_TOOLCHAIN_FILE) + set(TOOLCHAIN_FILE "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}") + set(cpptoml_DIR ${CMAKE_BINARY_DIR}/dependencies/install/lib/cmake/cpptoml) + set(cpptoml_DIR ${cpptoml_DIR} CACHE PATH "" FORCE) +endif() + # set download config, source and build paths set(DOWNLOAD_CONFIG_DIR ${CMAKE_BINARY_DIR}/dependencies/cpptoml/download) set(SOURCE_DIR ${CMAKE_BINARY_DIR}/dependencies/cpptoml/src) @@ -57,7 +63,7 @@ endif() file(MAKE_DIRECTORY ${BUILD_DIR}) -execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" "-DENABLE_LIBCXX=off" "-DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}" "${SOURCE_DIR}" +execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" "-DENABLE_LIBCXX=off" "-DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}" "${TOOLCHAIN_FILE}" "${SOURCE_DIR}" RESULT_VARIABLE result WORKING_DIRECTORY ${BUILD_DIR} ) if(result) diff --git a/cmake/cyclonedds/CMakeLists.txt b/cmake/cyclonedds/CMakeLists.txt index 24f439fbaa..1cb6bbcb1b 100644 --- a/cmake/cyclonedds/CMakeLists.txt +++ b/cmake/cyclonedds/CMakeLists.txt @@ -33,6 +33,10 @@ else() set(CREATE_PATH_COMMAND mkdir -p) endif(WIN32) +if(DEFINED CMAKE_TOOLCHAIN_FILE) + set(TOOLCHAIN_FILE "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}") +endif() + # ===== Helpers function(fetch_and_install name) set(DOWNLOAD_CONFIG_DIR ${CMAKE_BINARY_DIR}/dependencies/${name}/download) @@ -42,7 +46,7 @@ function(fetch_and_install name) # Fetch source configure_file(${name}.cmake.in ${DOWNLOAD_CONFIG_DIR}/CMakeLists.txt) - execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" "${DOWNLOAD_CONFIG_DIR}" + execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" "${TOOLCHAIN_FILE}" "${DOWNLOAD_CONFIG_DIR}" RESULT_VARIABLE result WORKING_DIRECTORY ${DOWNLOAD_CONFIG_DIR} ) if(result) @@ -71,7 +75,7 @@ function(fetch_and_install name) endforeach() string( REPLACE ";" " " ADDITIONAL_CMAKE_FLAGS "${ADDITIONAL_CMAKE_FLAGS}") - execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" "-DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}" "${ADDITIONAL_CMAKE_FLAGS}" "${SOURCE_DIR}" + execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" "-DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}" "${ADDITIONAL_CMAKE_FLAGS}" "${TOOLCHAIN_FILE}" "${SOURCE_DIR}" RESULT_VARIABLE result WORKING_DIRECTORY ${BUILD_DIR} ) if(result) diff --git a/cmake/googletest/CMakeLists.txt b/cmake/googletest/CMakeLists.txt index 59e70a4fb2..94247fef30 100644 --- a/cmake/googletest/CMakeLists.txt +++ b/cmake/googletest/CMakeLists.txt @@ -17,7 +17,7 @@ cmake_minimum_required(VERSION 3.5) project(googletest-build CXX) if(BUILD_TEST) - + include(ProcessorCount) ProcessorCount(N) @@ -33,6 +33,12 @@ if(BUILD_TEST) set(ENABLE_STATIC_DEBUG "-Dgtest_force_shared_crt=ON") endif(WIN32) + if(DEFINED CMAKE_TOOLCHAIN_FILE) + set(TOOLCHAIN_FILE "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}") + set(GTest_DIR ${CMAKE_BINARY_DIR}/dependencies/install/lib/cmake/GTest) + set(GTest_DIR ${GTest_DIR} CACHE PATH "" FORCE) + endif() + # set download confi, source and build paths set(DOWNLOAD_CONFIG_DIR ${CMAKE_BINARY_DIR}/dependencies/googletest/download) set(SOURCE_DIR ${CMAKE_BINARY_DIR}/dependencies/googletest/src) @@ -41,7 +47,7 @@ if(BUILD_TEST) # Download and unpack googletest at configure time configure_file(googletest.cmake.in ${DOWNLOAD_CONFIG_DIR}/CMakeLists.txt) - execute_process(COMMAND ${CMAKE_COMMAND} ${ENABLE_STATIC_DEBUG} -G "${CMAKE_GENERATOR}" "${DOWNLOAD_CONFIG_DIR}" + execute_process(COMMAND ${CMAKE_COMMAND} ${ENABLE_STATIC_DEBUG} -G "${CMAKE_GENERATOR}" "${TOOLCHAIN_FILE}" "${DOWNLOAD_CONFIG_DIR}" RESULT_VARIABLE result WORKING_DIRECTORY ${DOWNLOAD_CONFIG_DIR} ) if(result) @@ -57,7 +63,7 @@ if(BUILD_TEST) file(MAKE_DIRECTORY ${BUILD_DIR}) - execute_process(COMMAND ${CMAKE_COMMAND} ${ENABLE_STATIC_DEBUG} -G "${CMAKE_GENERATOR}" "-DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}" "${SOURCE_DIR}" + execute_process(COMMAND ${CMAKE_COMMAND} ${ENABLE_STATIC_DEBUG} -G "${CMAKE_GENERATOR}" "-DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}" "${TOOLCHAIN_FILE}" "${SOURCE_DIR}" RESULT_VARIABLE result WORKING_DIRECTORY ${BUILD_DIR} ) if(result) diff --git a/doc/website/getting-started/examples/intermediate-examples.md b/doc/website/getting-started/examples/intermediate-examples.md index d87c629de3..48d7849d43 100644 --- a/doc/website/getting-started/examples/intermediate-examples.md +++ b/doc/website/getting-started/examples/intermediate-examples.md @@ -1,6 +1,7 @@ # Intermediate examples {! ./../iceoryx_examples/callbacks/README.md !} +{! ./../iceoryx_examples/callbacks_in_c/README.md !} {! ./../iceoryx_examples/waitset/README.md !} {! ./../iceoryx_examples/waitset_in_c/README.md !} {! ./../iceoryx_examples/singleprocess/README.md !} diff --git a/doc/website/getting-started/installation.md b/doc/website/getting-started/installation.md index d8ca0002fe..32f7487306 100644 --- a/doc/website/getting-started/installation.md +++ b/doc/website/getting-started/installation.md @@ -9,26 +9,25 @@ iceoryx_utils and iceoryx_posh are deployed as independent cmake packages. Posh - 64-bit hardware (e.g. x86_64 or aarch64; 32-bit hardware might work, but is not supported) - [cmake](https://cmake.org), 3.5 or later - One of the following compilers: - - [gcc](https://gcc.gnu.org), 7.4 or later + - [gcc](https://gcc.gnu.org), 7.4 or later (5.4 currently supported too) - [clang](https://clang.llvm.org), 9.0 or later - [msvc](https://visualstudio.microsoft.com/de/), part of Visual Studio 2019 or later - [libacl](http://download.savannah.gnu.org/releases/acl/), 2.2 or later. Only for Linux & QNX. - - optional, [ncurses](https://invisible-island.net/ncurses/), 6.2 or later. Required by introspectiont tool. + - optional, [ncurses](https://invisible-island.net/ncurses/), 6.2 or later. Required by introspection tool. #### Optional, Cyclone DDS Gateway -If you would like to use our Cyclone DDS Gateway you have to install -[Cyclone DDS](https://github.com/eclipse-cyclonedds/cyclonedds) first. Furthermore -you have to install: +If you would like to use our Cyclone DDS Gateway you have to install [Cyclone DDS](https://github.com/eclipse-cyclonedds/cyclonedds) first.
+Furthermore you have to install: - [Apache Maven](http://maven.apache.org/download.cgi), 3.5 or later - - [OpenJDK](http://jdk.java.net/11/), 11.0 or later. Alternatively Java JDK, version 8 or later + - [OpenJDK](http://jdk.java.net/11/), 11.0 or later. Alternatively, Java JDK version 8 or later **Hint:** If you are behind a corporate firewall you may have to adjust the proxy settings of maven in `/etc/maven/settings.xml`. See: [Maven Proxy Configuration](https://maven.apache.org/settings.html#proxies) ### Mac OS -Before installing iceoryx you need a XCode installation, git and optional an installed ncurses library for +Before installing iceoryx you need to install XCode and git. Optionally, ncurses library is required for the introspection client. To install ncurses locally into your build folder follow these steps ``` cd iceoryx @@ -54,6 +53,16 @@ sudo apt install gcc g++ cmake libacl1-dev libncurses5-dev pkg-config Additionally, there is an optional dependency to the MIT licensed [cpptoml](https://github.com/skystrife/cpptoml) library, which is used to parse a RouDi config file for the mempool config. +### QNX + +QNX SDP 7.0 and 7.1 are supported (shipping with gcc 5.4 and gcc 8.3 respectively). + +Easiest way to build iceoryx on QNX is using the build script and providing a toolchain file.
+Example: +``` +./tools/iceoryx_build_test.sh -t /home/user/toolchains/qnx/qcc_x86_64_toolchain.cmake +``` + ## Build with CMake diff --git a/iceoryx_binding_c/CMakeLists.txt b/iceoryx_binding_c/CMakeLists.txt index cd417a8a59..575fc6101a 100644 --- a/iceoryx_binding_c/CMakeLists.txt +++ b/iceoryx_binding_c/CMakeLists.txt @@ -59,6 +59,7 @@ add_library(${PROJECT_NAME} source/cpp2c_enum_translation.cpp source/cpp2c_publisher.cpp source/cpp2c_subscriber.cpp + source/cpp2c_service_description_translation.cpp ) add_library(${PROJECT_NAMESPACE}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) @@ -74,7 +75,7 @@ target_include_directories(${PROJECT_NAME} $ ) -if(NOT WIN32) +if(NOT (WIN32 OR QNX)) target_link_libraries(${PROJECT_NAME} PUBLIC stdc++ diff --git a/iceoryx_binding_c/include/iceoryx_binding_c/internal/cpp2c_service_description_translation.hpp b/iceoryx_binding_c/include/iceoryx_binding_c/internal/cpp2c_service_description_translation.hpp new file mode 100644 index 0000000000..89624d17b1 --- /dev/null +++ b/iceoryx_binding_c/include/iceoryx_binding_c/internal/cpp2c_service_description_translation.hpp @@ -0,0 +1,27 @@ +// Copyright (c) 2021 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef IOX_BINDING_C_CPP2C_SERVICE_DESCRIPTION_TRANSLATION_HPP +#define IOX_BINDING_C_CPP2C_SERVICE_DESCRIPTION_TRANSLATION_HPP + +#include "iceoryx_binding_c/service_description.h" +#include "iceoryx_posh/capro/service_description.hpp" + +iox_service_description_t +TranslateServiceDescription(const iox::capro::ServiceDescription& serviceDescription) noexcept; + +#endif + diff --git a/iceoryx_binding_c/include/iceoryx_binding_c/publisher.h b/iceoryx_binding_c/include/iceoryx_binding_c/publisher.h index e35ec594c4..984b008d5e 100644 --- a/iceoryx_binding_c/include/iceoryx_binding_c/publisher.h +++ b/iceoryx_binding_c/include/iceoryx_binding_c/publisher.h @@ -20,6 +20,7 @@ #include "iceoryx_binding_c/enums.h" #include "iceoryx_binding_c/internal/c2cpp_binding.h" +#include "iceoryx_binding_c/service_description.h" #include "iceoryx_binding_c/types.h" /// @brief publisher handle @@ -116,4 +117,8 @@ bool iox_pub_is_offered(iox_pub_t const self); /// @return true if there are subscribers otherwise false bool iox_pub_has_subscribers(iox_pub_t const self); +/// @brief returns the service description of the publisher +/// @param[in] self handle to the publisher +/// @return the service description +iox_service_description_t iox_pub_get_service_description(iox_pub_t const self); #endif diff --git a/iceoryx_binding_c/include/iceoryx_binding_c/service_description.h b/iceoryx_binding_c/include/iceoryx_binding_c/service_description.h new file mode 100644 index 0000000000..c0e0ff6161 --- /dev/null +++ b/iceoryx_binding_c/include/iceoryx_binding_c/service_description.h @@ -0,0 +1,33 @@ +// Copyright (c) 2021 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef IOX_BINDING_C_SERVICE_DESCRIPTION_H +#define IOX_BINDING_C_SERVICE_DESCRIPTION_H + +#include + +typedef struct +{ + uint16_t serviceId; + uint16_t instanceId; + uint16_t eventId; + + char serviceString[100U]; + char instanceString[100U]; + char eventString[100U]; +} iox_service_description_t; + +#endif diff --git a/iceoryx_binding_c/include/iceoryx_binding_c/subscriber.h b/iceoryx_binding_c/include/iceoryx_binding_c/subscriber.h index 81948f2f00..17a1e23201 100644 --- a/iceoryx_binding_c/include/iceoryx_binding_c/subscriber.h +++ b/iceoryx_binding_c/include/iceoryx_binding_c/subscriber.h @@ -20,6 +20,7 @@ #include "iceoryx_binding_c/enums.h" #include "iceoryx_binding_c/internal/c2cpp_binding.h" +#include "iceoryx_binding_c/service_description.h" #include "iceoryx_binding_c/types.h" /// @brief Subscriber handle @@ -117,4 +118,8 @@ bool iox_sub_has_chunks(iox_sub_t const self); /// @return true if there are lost chunks due to overflowing queue, otherwise false bool iox_sub_has_lost_chunks(iox_sub_t const self); +/// @brief returns the service description of the subscriber +/// @param[in] self handle to the subscriber +/// @return the service description +iox_service_description_t iox_sub_get_service_description(iox_sub_t const self); #endif diff --git a/iceoryx_binding_c/include/iceoryx_binding_c/types.h b/iceoryx_binding_c/include/iceoryx_binding_c/types.h index c6535bf476..6b7e5884c3 100644 --- a/iceoryx_binding_c/include/iceoryx_binding_c/types.h +++ b/iceoryx_binding_c/include/iceoryx_binding_c/types.h @@ -31,7 +31,7 @@ struct iox_ws_storage_t_ { // the value of the array size is the result of the following formula: // sizeof(WaitSet) / 8 - uint64_t do_not_touch_me[2567]; + uint64_t do_not_touch_me[2696]; }; typedef struct iox_ws_storage_t_ iox_ws_storage_t; diff --git a/iceoryx_binding_c/source/c_publisher.cpp b/iceoryx_binding_c/source/c_publisher.cpp index d68029e3e7..113cd7c05c 100644 --- a/iceoryx_binding_c/source/c_publisher.cpp +++ b/iceoryx_binding_c/source/c_publisher.cpp @@ -17,6 +17,7 @@ #include "iceoryx_binding_c/internal/cpp2c_enum_translation.hpp" #include "iceoryx_binding_c/internal/cpp2c_publisher.hpp" +#include "iceoryx_binding_c/internal/cpp2c_service_description_translation.hpp" #include "iceoryx_posh/internal/popo/ports/publisher_port_user.hpp" #include "iceoryx_posh/runtime/posh_runtime.hpp" @@ -149,3 +150,9 @@ bool iox_pub_has_subscribers(iox_pub_t const self) { return PublisherPortUser(self->m_portData).hasSubscribers(); } + +iox_service_description_t iox_pub_get_service_description(iox_pub_t const self) +{ + return TranslateServiceDescription(PublisherPortUser(self->m_portData).getCaProServiceDescription()); +} + diff --git a/iceoryx_binding_c/source/c_subscriber.cpp b/iceoryx_binding_c/source/c_subscriber.cpp index 6618ffddb2..7bf4aa67d3 100644 --- a/iceoryx_binding_c/source/c_subscriber.cpp +++ b/iceoryx_binding_c/source/c_subscriber.cpp @@ -18,6 +18,7 @@ #include "iceoryx_binding_c/enums.h" #include "iceoryx_binding_c/internal/cpp2c_enum_translation.hpp" +#include "iceoryx_binding_c/internal/cpp2c_service_description_translation.hpp" #include "iceoryx_binding_c/internal/cpp2c_subscriber.hpp" #include "iceoryx_binding_c/internal/cpp2c_waitset.hpp" #include "iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp" @@ -149,3 +150,8 @@ bool iox_sub_has_lost_chunks(iox_sub_t const self) { return SubscriberPortUser(self->m_portData).hasLostChunksSinceLastCall(); } + +iox_service_description_t iox_sub_get_service_description(iox_sub_t const self) +{ + return TranslateServiceDescription(SubscriberPortUser(self->m_portData).getCaProServiceDescription()); +} diff --git a/iceoryx_binding_c/source/cpp2c_enum_translation.cpp b/iceoryx_binding_c/source/cpp2c_enum_translation.cpp index f557303106..cc98f7dd68 100644 --- a/iceoryx_binding_c/source/cpp2c_enum_translation.cpp +++ b/iceoryx_binding_c/source/cpp2c_enum_translation.cpp @@ -46,9 +46,9 @@ iox_ChunkReceiveResult ChunkReceiveResult(const iox::popo::ChunkReceiveResult va { switch (value) { - case ChunkReceiveResult::NO_CHUNK_AVAILABLE: + case iox::popo::ChunkReceiveResult::NO_CHUNK_AVAILABLE: return ChunkReceiveResult_NO_CHUNK_AVAILABLE; - case ChunkReceiveResult::TOO_MANY_CHUNKS_HELD_IN_PARALLEL: + case iox::popo::ChunkReceiveResult::TOO_MANY_CHUNKS_HELD_IN_PARALLEL: return ChunkReceiveResult_TOO_MANY_CHUNKS_HELD_IN_PARALLEL; default: return ChunkReceiveResult_UNDEFINED_ERROR; diff --git a/iceoryx_binding_c/source/cpp2c_service_description_translation.cpp b/iceoryx_binding_c/source/cpp2c_service_description_translation.cpp new file mode 100644 index 0000000000..30577b9c25 --- /dev/null +++ b/iceoryx_binding_c/source/cpp2c_service_description_translation.cpp @@ -0,0 +1,44 @@ +// Copyright (c) 2021 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "iceoryx_binding_c/internal/cpp2c_service_description_translation.hpp" + +iox_service_description_t TranslateServiceDescription(const iox::capro::ServiceDescription& serviceDescription) noexcept +{ + iox_service_description_t retVal; + retVal.serviceId = serviceDescription.getServiceID(); + retVal.instanceId = serviceDescription.getInstanceID(); + retVal.eventId = serviceDescription.getEventID(); + +#if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic push +// the warning below is available only in gcc >=8.x compilers +// ignore this warning since we already ensure that the string sizes are correct in the test +// test_service_description.cpp (iox_service_description_test.StringSizesAreCorrect) +// therefore a string truncation will never occur. +#pragma GCC diagnostic ignored "-Wstringop-truncation" +#endif + strncpy(retVal.serviceString, serviceDescription.getServiceIDString().c_str(), iox::capro::IdString_t().capacity()); + strncpy( + retVal.instanceString, serviceDescription.getInstanceIDString().c_str(), iox::capro::IdString_t().capacity()); + strncpy(retVal.eventString, serviceDescription.getEventIDString().c_str(), iox::capro::IdString_t().capacity()); +#if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic pop +#endif + + return retVal; +} + diff --git a/iceoryx_binding_c/source/cpp2c_subscriber.cpp b/iceoryx_binding_c/source/cpp2c_subscriber.cpp index d612f4c44e..cfc4fe5612 100644 --- a/iceoryx_binding_c/source/cpp2c_subscriber.cpp +++ b/iceoryx_binding_c/source/cpp2c_subscriber.cpp @@ -34,16 +34,9 @@ void cpp2c_Subscriber::enableEvent(iox::popo::TriggerHandle&& triggerHandle, static_cast(subscriberEvent); m_trigger = std::move(triggerHandle); - if (triggerHandle.doesContainEventVariable()) - { - iox::popo::SubscriberPortUser(m_portData) - .setEventVariable(*static_cast(m_trigger.getConditionVariableData()), - m_trigger.getUniqueId()); - } - else - { - iox::popo::SubscriberPortUser(m_portData).setConditionVariable(m_trigger.getConditionVariableData()); - } + + iox::popo::SubscriberPortUser(m_portData) + .setConditionVariable(*m_trigger.getConditionVariableData(), m_trigger.getUniqueId()); } iox::popo::WaitSetHasTriggeredCallback diff --git a/iceoryx_binding_c/test/mocks/wait_set_mock.hpp b/iceoryx_binding_c/test/mocks/wait_set_mock.hpp index 7917025c37..b3443c1ae2 100644 --- a/iceoryx_binding_c/test/mocks/wait_set_mock.hpp +++ b/iceoryx_binding_c/test/mocks/wait_set_mock.hpp @@ -22,8 +22,8 @@ class WaitSetMock : public cpp2c_WaitSet { public: - WaitSetMock(iox::popo::ConditionVariableData* condVarDataPtr) noexcept - : cpp2c_WaitSet(condVarDataPtr) + WaitSetMock(iox::popo::ConditionVariableData& condVarData) noexcept + : cpp2c_WaitSet(condVarData) { } }; diff --git a/iceoryx_binding_c/test/moduletests/test_cpp2c_service_description_translation.cpp b/iceoryx_binding_c/test/moduletests/test_cpp2c_service_description_translation.cpp new file mode 100644 index 0000000000..f2077ba60f --- /dev/null +++ b/iceoryx_binding_c/test/moduletests/test_cpp2c_service_description_translation.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2021 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "iceoryx_binding_c/internal/cpp2c_service_description_translation.hpp" + +using namespace iox; +using namespace iox::capro; + + +#include "test.hpp" + +using namespace ::testing; + +TEST(iox_service_description_translation_test, TranslatesIntegersCorrectly) +{ + iox::capro::ServiceDescription service(5U, 12U, 89U); + auto cServiceDescription = TranslateServiceDescription(service); + + EXPECT_THAT(cServiceDescription.serviceId, Eq(5U)); + EXPECT_THAT(cServiceDescription.instanceId, Eq(89U)); + EXPECT_THAT(cServiceDescription.eventId, Eq(12U)); + + EXPECT_THAT(std::string(cServiceDescription.serviceString), Eq("5")); + EXPECT_THAT(std::string(cServiceDescription.instanceString), Eq("89")); + EXPECT_THAT(std::string(cServiceDescription.eventString), Eq("12")); +} + +TEST(iox_service_description_translation_test, TranslatesStringCorrectly) +{ + iox::capro::ServiceDescription service( + IdString_t("SomeService"), IdString_t("FunkyInstance"), IdString_t("BumbleBeeSighted")); + auto cServiceDescription = TranslateServiceDescription(service); + + EXPECT_THAT(cServiceDescription.serviceId, Eq(iox::capro::InvalidID)); + EXPECT_THAT(cServiceDescription.instanceId, Eq(iox::capro::InvalidID)); + EXPECT_THAT(cServiceDescription.eventId, Eq(iox::capro::InvalidID)); + + EXPECT_THAT(std::string(cServiceDescription.serviceString), Eq("SomeService")); + EXPECT_THAT(std::string(cServiceDescription.instanceString), Eq("FunkyInstance")); + EXPECT_THAT(std::string(cServiceDescription.eventString), Eq("BumbleBeeSighted")); +} diff --git a/iceoryx_binding_c/test/moduletests/test_event_info.cpp b/iceoryx_binding_c/test/moduletests/test_event_info.cpp index c3ade89c1c..6537725361 100644 --- a/iceoryx_binding_c/test/moduletests/test_event_info.cpp +++ b/iceoryx_binding_c/test/moduletests/test_event_info.cpp @@ -78,7 +78,7 @@ class iox_event_info_test : public Test static UserTrigger* m_lastEventCallbackArgument; ConditionVariableData m_condVar{"myApp"}; - WaitSetMock m_waitSet{&m_condVar}; + WaitSetMock m_waitSet{m_condVar}; UserTrigger m_userTrigger; static constexpr uint32_t NUM_CHUNKS_IN_POOL = MAX_CHUNKS_HELD_PER_SUBSCRIBER_SIMULTANEOUSLY + 2; diff --git a/iceoryx_binding_c/test/moduletests/test_listener.cpp b/iceoryx_binding_c/test/moduletests/test_listener.cpp index 7749ee1561..229f316a15 100644 --- a/iceoryx_binding_c/test/moduletests/test_listener.cpp +++ b/iceoryx_binding_c/test/moduletests/test_listener.cpp @@ -17,7 +17,6 @@ #include "iceoryx_binding_c/internal/cpp2c_enum_translation.hpp" #include "iceoryx_binding_c/internal/cpp2c_subscriber.hpp" #include "iceoryx_posh/iceoryx_posh_types.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/event_variable_data.hpp" #include "iceoryx_posh/internal/popo/ports/subscriber_port_single_producer.hpp" #include "iceoryx_posh/mepoo/mepoo_config.hpp" #include "iceoryx_posh/popo/listener.hpp" @@ -65,8 +64,8 @@ class iox_listener_test : public Test class TestListener : public Listener { public: - TestListener(EventVariableData& eventVar) - : Listener(&eventVar) + TestListener(ConditionVariableData& condVar) + : Listener(condVar) { } }; @@ -132,8 +131,8 @@ class iox_listener_test : public Test } } - EventVariableData m_eventVar{"hypnotoadKnueppeltRetour"}; - TestListener m_sut{m_eventVar}; + ConditionVariableData m_condVar{"hypnotoadKnueppeltRetour"}; + TestListener m_sut{m_condVar}; iox_user_trigger_storage_t m_userTriggerStorage[MAX_NUMBER_OF_EVENTS_PER_LISTENER + 1U]; cxx::vector m_userTrigger; diff --git a/iceoryx_binding_c/test/moduletests/test_publisher.cpp b/iceoryx_binding_c/test/moduletests/test_publisher.cpp index b25853f9f5..68475c0f49 100644 --- a/iceoryx_binding_c/test/moduletests/test_publisher.cpp +++ b/iceoryx_binding_c/test/moduletests/test_publisher.cpp @@ -120,7 +120,7 @@ class iox_pub_test : public Test TEST_F(iox_pub_test, initialStateOfIsOfferedIsAsExpected) { - PublisherOptions iGotOptions; + PublisherOptions iGotOptions; auto expectedIsOffered = iGotOptions.offerOnCreate; EXPECT_EQ(expectedIsOffered, iox_pub_is_offered(&m_sut)); } @@ -256,6 +256,18 @@ TEST_F(iox_pub_test, sendDeliversChunk) EXPECT_TRUE(static_cast(maybeSharedChunk->getPayload())->dummy == 4711); } +TEST_F(iox_pub_test, correctServiceDescriptionReturned) +{ + auto serviceDescription = iox_pub_get_service_description(&m_sut); + + EXPECT_THAT(serviceDescription.serviceId, Eq(iox::capro::InvalidID)); + EXPECT_THAT(serviceDescription.instanceId, Eq(iox::capro::InvalidID)); + EXPECT_THAT(serviceDescription.eventId, Eq(iox::capro::InvalidID)); + EXPECT_THAT(std::string(serviceDescription.serviceString), Eq("a")); + EXPECT_THAT(std::string(serviceDescription.instanceString), Eq("b")); + EXPECT_THAT(std::string(serviceDescription.eventString), Eq("c")); +} + TEST(iox_pub_options_test, publisherOptionsAreInitializedCorrectly) { iox_pub_options_t sut; @@ -305,3 +317,4 @@ TEST(iox_pub_options_test, publisherInitializationTerminatesIfOptionsAreNotIniti EXPECT_DEATH({ iox_pub_init(&storage, "a", "b", "c", &options); }, ".*"); } + diff --git a/iceoryx_binding_c/test/moduletests/test_service_description.cpp b/iceoryx_binding_c/test/moduletests/test_service_description.cpp new file mode 100644 index 0000000000..75fc1eef96 --- /dev/null +++ b/iceoryx_binding_c/test/moduletests/test_service_description.cpp @@ -0,0 +1,42 @@ +// Copyright (c) 2021 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "iceoryx_posh/capro/service_description.hpp" + +using namespace iox; +using namespace iox::capro; + + +extern "C" { +#include "iceoryx_binding_c/service_description.h" +} + +#include "test.hpp" + +#include + +using namespace ::testing; + +TEST(iox_service_description_test, StringSizesAreCorrect) +{ + EXPECT_THAT(sizeof(decltype(std::declval().serviceString)), + Eq(iox::capro::IdString_t().capacity())); + EXPECT_THAT(sizeof(decltype(std::declval().instanceString)), + Eq(iox::capro::IdString_t().capacity())); + EXPECT_THAT(sizeof(decltype(std::declval().eventString)), + Eq(iox::capro::IdString_t().capacity())); +} + diff --git a/iceoryx_binding_c/test/moduletests/test_subscriber.cpp b/iceoryx_binding_c/test/moduletests/test_subscriber.cpp index 68d15fbd58..907115af48 100644 --- a/iceoryx_binding_c/test/moduletests/test_subscriber.cpp +++ b/iceoryx_binding_c/test/moduletests/test_subscriber.cpp @@ -101,7 +101,7 @@ class iox_sub_test : public Test iox_sub_t m_sut = m_subscriber.get(); ConditionVariableData m_condVar{"myApp"}; - std::unique_ptr m_waitSet{new WaitSetMock(&m_condVar)}; + std::unique_ptr m_waitSet{new WaitSetMock(m_condVar)}; }; iox_sub_t iox_sub_test::m_triggerCallbackLatestArgument = nullptr; @@ -283,7 +283,7 @@ TEST_F(iox_sub_test, attachingToWaitSetWorks) TEST_F(iox_sub_test, attachingToAnotherWaitsetCleansupAtOriginalWaitset) { - WaitSetMock m_waitSet2{&m_condVar}; + WaitSetMock m_waitSet2{m_condVar}; iox_ws_attach_subscriber_event(m_waitSet.get(), m_sut, SubscriberEvent_HAS_DATA, 0U, NULL); EXPECT_EQ(iox_ws_attach_subscriber_event(&m_waitSet2, m_sut, SubscriberEvent_HAS_DATA, 0U, NULL), @@ -340,6 +340,18 @@ TEST_F(iox_sub_test, deinitSubscriberDetachesTriggerFromWaitSet) free(subscriber); } +TEST_F(iox_sub_test, correctServiceDescriptionReturned) +{ + auto serviceDescription = iox_sub_get_service_description(m_sut); + + EXPECT_THAT(serviceDescription.serviceId, Eq(iox::capro::InvalidID)); + EXPECT_THAT(serviceDescription.instanceId, Eq(iox::capro::InvalidID)); + EXPECT_THAT(serviceDescription.eventId, Eq(iox::capro::InvalidID)); + EXPECT_THAT(std::string(serviceDescription.serviceString), Eq("a")); + EXPECT_THAT(std::string(serviceDescription.instanceString), Eq("b")); + EXPECT_THAT(std::string(serviceDescription.eventString), Eq("c")); +} + TEST(iox_sub_options_test, subscriberOptionsAreInitializedCorrectly) { iox_sub_options_t sut; diff --git a/iceoryx_binding_c/test/moduletests/test_user_trigger.cpp b/iceoryx_binding_c/test/moduletests/test_user_trigger.cpp index d1e1e32bc3..a4dc089737 100644 --- a/iceoryx_binding_c/test/moduletests/test_user_trigger.cpp +++ b/iceoryx_binding_c/test/moduletests/test_user_trigger.cpp @@ -52,7 +52,7 @@ class iox_user_trigger_test : public Test iox_user_trigger_t m_sut; ConditionVariableData m_condVar{"Horscht"}; - WaitSetMock m_waitSet{&m_condVar}; + WaitSetMock m_waitSet{m_condVar}; static bool wasTriggerCallbackCalled; }; @@ -116,7 +116,7 @@ TEST_F(iox_user_trigger_test, triggeringWaitSetResultsInCorrectCallback) TEST_F(iox_user_trigger_test, attachingToAnotherWaitSetCleansupFirstWaitset) { - WaitSetMock m_waitSet2{&m_condVar}; + WaitSetMock m_waitSet2{m_condVar}; iox_ws_attach_user_trigger_event(&m_waitSet, m_sut, 0U, NULL); iox_ws_attach_user_trigger_event(&m_waitSet2, m_sut, 0U, NULL); @@ -127,7 +127,7 @@ TEST_F(iox_user_trigger_test, attachingToAnotherWaitSetCleansupFirstWaitset) TEST_F(iox_user_trigger_test, disable_trigger_eventingItFromWaitsetCleansup) { - WaitSetMock m_waitSet2{&m_condVar}; + WaitSetMock m_waitSet2{m_condVar}; iox_ws_attach_user_trigger_event(&m_waitSet, m_sut, 0U, NULL); iox_ws_detach_user_trigger_event(&m_waitSet, m_sut); diff --git a/iceoryx_binding_c/test/moduletests/test_wait_set.cpp b/iceoryx_binding_c/test/moduletests/test_wait_set.cpp index 6d5bdab82b..3cea5de5ae 100644 --- a/iceoryx_binding_c/test/moduletests/test_wait_set.cpp +++ b/iceoryx_binding_c/test/moduletests/test_wait_set.cpp @@ -60,7 +60,7 @@ class iox_ws_test : public Test } ConditionVariableData m_condVar{"Horscht"}; - WaitSetMock* m_sut = new WaitSetMock{&m_condVar}; + WaitSetMock* m_sut = new WaitSetMock{m_condVar}; iox_user_trigger_storage_t m_userTriggerStorage[MAX_NUMBER_OF_EVENTS_PER_WAITSET + 1]; cxx::vector m_userTrigger; diff --git a/iceoryx_examples/README.md b/iceoryx_examples/README.md index ef63b1a66a..59da30a4c6 100644 --- a/iceoryx_examples/README.md +++ b/iceoryx_examples/README.md @@ -22,7 +22,8 @@ cd someExample | Example | Description |Level | |:-------------------------------------------------------|:------------|:----:| -|[callbacks](./callbacks/) | Implement callbacks which are triggered by events. | Intermediate | +|[callbacks](./callbacks/) | Implement callbacks which are triggered by events. | Intermediate | +|[callbacks_in_c](./callbacks_in_c/) | The C version of the callbacks example. | Intermediate | |[icedelivery](./icedelivery/) | You are new to iceoryx then take a look at this example which demonstrates the basics of iceoryx by sending data from one process to another process. | Beginner | |[icedelivery_in_c](./icedelivery_in_c/) | Shows the same use case as the ice delivery example but with the iceoryx C API | Beginner | |[ice_multi_publisher](./ice_multi_publisher/) | Shows how multiple publishers can be used to publish on the same topic. | Intermediate | @@ -31,3 +32,11 @@ cd someExample |[singleprocess](./singleprocess/) | Iceoryx can also be used for inter thread communication when you would like to run everything in a single process. | Intermediate | |[iceperf](./iceperf/) | A benchmark application which measures the latency of an IPC transmission between two applications. | Advanced | |[icecrystal](./icecrystal/) | Demonstrates the usage of the iceoryx introspection client. | Advanced | + +## How to add a new example + + 1. Add the example in the "List of all examples" in this markdown file + 2. Add the example folder name into the `EXAMPLES=${EXAMPLES} ...` array in `./tools/iceoryx_build_test.sh` + 3. Add an `add_subdirectory` directive into `iceoryx_meta/CMakeLists.txt` in the `if(EXAMPLES)` section. + 4. Add the example in the file with the corresponding level in `doc/website/getting-started/examples/LEVEL-examples.md` + diff --git a/iceoryx_examples/callbacks/README.md b/iceoryx_examples/callbacks/README.md index 01f23349ac..1a913764e5 100644 --- a/iceoryx_examples/callbacks/README.md +++ b/iceoryx_examples/callbacks/README.md @@ -1,2 +1,146 @@ -# ActiveCallSet - WORK IN PROGRESS - +# Listener (or how to use callbacks with iceoryx) +## Introduction + +For an introduction into the terminology please read the Glossary in the +[WaitSet C++ example](../waitset). + +The Listener is a completely threadsafe construct which reacts to events by +executing registered callbacks in a background thread. Events can be emitted by +_EventOrigins_ like a subscriber or a user trigger. Some of the _EventOrigins_ +like the subscriber can hereby emit more than one event type. + +The interface of a listener consists of two methods: `attachEvent` to attach a +new event with a callback and `detachEvent`. These two methods can be called +concurrently, even from inside a callback which was triggered by an event! + +## Expected output + + + +## Code walkthrough + +Let's say we have an application which offers us two distinct services: +`Radar.FrontLeft.Counter` and `Rader.FrontRight.Counter`. Every time we have +received a sample from left and right we would like to calculate the sum with +the newest values and print it out. If we have received only one of the samples +we store it until we received the other side. + +### ice_callbacks_publisher.cpp + +The publisher of this example does not contain any new features but if you have +some questions take a look at the [icedelivery example](../icedelivery). + +### ice_callbacks_subscriber.cpp +#### int main() +The subscriber main function starts as usual and after registering the runtime +we create the listener which starts a background thread. +```cpp +iox::popo::Listener listener; +``` + +Because it is fun we also create a heartbeat trigger which will be triggered +every 4 seconds so that `heartbeat received` can be printed to the console. +Furthermore, we have to create two subscriber to receive samples for the two +services. +```cpp +iox::popo::UserTrigger heartbeat; +iox::popo::Subscriber subscriberLeft({"Radar", "FrontLeft", "Counter"}); +iox::popo::Subscriber subscriberRight({"Radar", "FrontRight", "Counter"}); +``` + +Next thing is a `heartbeatThread` which will trigger our heartbeat trigger every +4 seconds. +```cpp +std::thread heartbeatThread([&] { + while (keepRunning) + { + heartbeat.trigger(); + std::this_thread::sleep_for(std::chrono::seconds(4)); + } +}); +``` + +Now that everything is setup we can attach the subscribers to the listener so that +everytime a new sample (`iox::popo::SubscriberEvent::HAS_DATA`) is received our callback +(`onSampleReceivedCallback`) will be called. We also attach +our `heartbeat` user trigger to print the hearbeat message to the console via another +callback (`heartbeatCallback`). +```cpp +listener.attachEvent(heartbeat, heartbeatCallback); +listener.attachEvent(subscriberLeft, + iox::popo::SubscriberEvent::HAS_DATA, onSampleReceivedCallback); +listener.attachEvent(subscriberRight, + iox::popo::SubscriberEvent::HAS_DATA, onSampleReceivedCallback); +``` +Since a user trigger has only one event we do not have to specify an event when we attach +it to the listener. +In this example we choose to attach the same callback twice to make things easier +but you are free to attach any callback with the signature `void(iox::popo::Subscriber *)`. + +The setup is complete but it would terminate right away since we have no blocker which +waits until SIGINT or SIGTERM was send. In the other examples we hadn't have that problem +since we pulled all the events in a while true loop but working only with callbacks +requires something like our `shutdownSemaphore`, a semaphore on which we wait until +the signal callback increments it. +```cpp +shutdownSemaphore.wait(); +``` + +When the `shutdownSemaphore` unblocks we clean up all resources and terminate the process +gracefully. +```cpp +listener.detachEvent(heartbeat); +listener.detachEvent(subscriberLeft, iox::popo::SubscriberEvent::HAS_DATA); +listener.detachEvent(subscriberRight, iox::popo::SubscriberEvent::HAS_DATA); + +heartbeatThread.join(); +``` + +Hint: You do not have to detach an _EventOrigin_ like a subscriber or user trigger +before it goes out of scope. This also goes for the _Listener_, the implemented +RAII based design takes care of the resource cleanup. + +#### The Callbacks +The callbacks must have a signature like `void(PointerToEventOrigin*)`. +Our `heartbeatCallback` for instance just prints the message `heartbeat received`. +```cpp +void heartbeatCallback(iox::popo::UserTrigger*) +{ + std::cout << "heartbeat received " << std::endl; +} +``` + +The `onSampleReceivedCallback` is more complex. We first acquire the received +sample and check which subscriber signaled the event by acquiring the subscriber's +service description. If the instance is equal to `FrontLeft` we store the sample +in the `leftCache` otherwise in the `rightCache`. +```cpp +void onSampleReceivedCallback(iox::popo::Subscriber* subscriber) +{ + subscriber->take().and_then([subscriber](auto& sample) { + auto instanceString = subscriber->getServiceDescription().getInstanceIDString(); + + // store the sample in the corresponding cache + if (instanceString == iox::capro::IdString_t("FrontLeft")) + { + leftCache.emplace(*sample); + } + else if (instanceString == iox::capro::IdString_t("FrontRight")) + { + rightCache.emplace(*sample); + } +``` + +In a next step we check if both caches are filled. If this is the case we print +an extra message which states the result of the sum of both received values. +Afterwards we reset both caches to start fresh again. +```cpp + if (leftCache && rightCache) + { + std::cout << "Received samples from FrontLeft and FrontRight. Sum of " << leftCache->counter << " + " + << rightCache->counter << " = " << leftCache->counter + rightCache->counter << std::endl; + leftCache.reset(); + rightCache.reset(); + } +``` diff --git a/iceoryx_examples/callbacks/ice_callbacks_publisher.cpp b/iceoryx_examples/callbacks/ice_callbacks_publisher.cpp index 776eba8a58..b6f63d54ba 100644 --- a/iceoryx_examples/callbacks/ice_callbacks_publisher.cpp +++ b/iceoryx_examples/callbacks/ice_callbacks_publisher.cpp @@ -23,29 +23,35 @@ #include #include -bool killswitch = false; +bool keepRunning = true; +constexpr char APP_NAME[] = "iox-ex-callbacks-publisher"; static void sigHandler(int f_sig [[gnu::unused]]) { - killswitch = true; + keepRunning = false; } void sending() { - iox::runtime::PoshRuntime::initRuntime("iox-ex-callbacks-publisher"); + iox::runtime::PoshRuntime::initRuntime(APP_NAME); - iox::popo::Publisher myPublisher({"Radar", "FrontLeft", "Counter"}); - myPublisher.offer(); + iox::popo::Publisher myPublisherLeft({"Radar", "FrontLeft", "Counter"}); + iox::popo::Publisher myPublisherRight({"Radar", "FrontRight", "Counter"}); - for (uint32_t counter = 0U; !killswitch; ++counter) + for (uint32_t counter = 0U; keepRunning; ++counter) { - myPublisher.publishCopyOf(CounterTopic{counter}); - std::cout << "Sending: " << counter << std::endl; - - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + if (counter % 3 == 0) + { + std::cout << "Radar.FrontLeft.Counter sending : " << counter << std::endl; + myPublisherLeft.publishCopyOf(CounterTopic{counter}); + } + else + { + std::cout << "Radar.FrontRight.Counter sending : " << counter * 2 << std::endl; + myPublisherRight.publishCopyOf(CounterTopic{counter * 2}); + } + std::this_thread::sleep_for(std::chrono::seconds(1)); } - - myPublisher.stopOffer(); } int main() @@ -53,8 +59,7 @@ int main() auto signalIntGuard = iox::posix::registerSignalHandler(iox::posix::Signal::INT, sigHandler); auto signalTermGuard = iox::posix::registerSignalHandler(iox::posix::Signal::TERM, sigHandler); - std::thread tx(sending); - tx.join(); + sending(); return (EXIT_SUCCESS); } diff --git a/iceoryx_examples/callbacks/ice_callbacks_subscriber.cpp b/iceoryx_examples/callbacks/ice_callbacks_subscriber.cpp index ac1a30f749..688866927d 100644 --- a/iceoryx_examples/callbacks/ice_callbacks_subscriber.cpp +++ b/iceoryx_examples/callbacks/ice_callbacks_subscriber.cpp @@ -18,6 +18,7 @@ #include "iceoryx_posh/popo/subscriber.hpp" #include "iceoryx_posh/popo/user_trigger.hpp" #include "iceoryx_posh/runtime/posh_runtime.hpp" +#include "iceoryx_utils/cxx/optional.hpp" #include "iceoryx_utils/posix_wrapper/semaphore.hpp" #include "iceoryx_utils/posix_wrapper/signal_handler.hpp" #include "topic_data.hpp" @@ -26,25 +27,52 @@ #include #include -iox::popo::UserTrigger shutdownTrigger; -iox::posix::Semaphore mainLoopBlocker = +iox::posix::Semaphore shutdownSemaphore = iox::posix::Semaphore::create(iox::posix::CreateUnnamedSingleProcessSemaphore, 0).value(); std::atomic_bool keepRunning{true}; +constexpr char APP_NAME[] = "iox-ex-callbacks-subscriber"; + +iox::cxx::optional leftCache; +iox::cxx::optional rightCache; static void sigHandler(int f_sig [[gnu::unused]]) { - shutdownTrigger.trigger(); + shutdownSemaphore.post(); + keepRunning = false; } -void shutdownTriggerCallback(iox::popo::UserTrigger*) +void heartbeatCallback(iox::popo::UserTrigger*) { - keepRunning.store(false); + std::cout << "heartbeat received " << std::endl; } -void onSampleReceived(iox::popo::Subscriber* subscriber) +void onSampleReceivedCallback(iox::popo::Subscriber* subscriber) { - subscriber->take().and_then([](auto& sample) { printf("received: %d\n", sample->counter); }); + subscriber->take().and_then([subscriber](auto& sample) { + auto instanceString = subscriber->getServiceDescription().getInstanceIDString(); + + // store the sample in the corresponding cache + if (instanceString == iox::capro::IdString_t("FrontLeft")) + { + leftCache.emplace(*sample); + } + else if (instanceString == iox::capro::IdString_t("FrontRight")) + { + rightCache.emplace(*sample); + } + + std::cout << "received: " << sample->counter << std::endl; + }); + + // if both caches are filled we can process them + if (leftCache && rightCache) + { + std::cout << "Received samples from FrontLeft and FrontRight. Sum of " << leftCache->counter << " + " + << rightCache->counter << " = " << leftCache->counter + rightCache->counter << std::endl; + leftCache.reset(); + rightCache.reset(); + } } int main() @@ -52,26 +80,43 @@ int main() auto signalIntGuard = iox::posix::registerSignalHandler(iox::posix::Signal::INT, sigHandler); auto signalTermGuard = iox::posix::registerSignalHandler(iox::posix::Signal::TERM, sigHandler); - iox::runtime::PoshRuntime::initRuntime("iox-ex-callbacks-subscriber"); + iox::runtime::PoshRuntime::initRuntime(APP_NAME); + // the listener starts a background thread and the callbacks of the attached events + // will be called in this background thread when they are triggered iox::popo::Listener listener; - // attach shutdownTrigger to handle CTRL+C - listener.attachEvent(shutdownTrigger, shutdownTriggerCallback); - - iox::popo::Subscriber subscriber({"Radar", "FrontLeft", "Counter"}); - - subscriber.subscribe(); - - listener.attachEvent(subscriber, iox::popo::SubscriberEvent::HAS_DATA, onSampleReceived); - - while (keepRunning) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - - listener.detachEvent(shutdownTrigger); - listener.detachEvent(subscriber, iox::popo::SubscriberEvent::HAS_DATA); + iox::popo::UserTrigger heartbeat; + iox::popo::Subscriber subscriberLeft({"Radar", "FrontLeft", "Counter"}); + iox::popo::Subscriber subscriberRight({"Radar", "FrontRight", "Counter"}); + + // send a heartbeat every 4 seconds + std::thread heartbeatThread([&] { + while (keepRunning) + { + heartbeat.trigger(); + std::this_thread::sleep_for(std::chrono::seconds(4)); + } + }); + + // attach everything to the listener, from here on the callbacks are called when the corresponding event is occuring + listener.attachEvent(heartbeat, heartbeatCallback); + listener.attachEvent(subscriberLeft, iox::popo::SubscriberEvent::HAS_DATA, onSampleReceivedCallback); + // it is possible to attach any callback here with the required signature. to simplify the + // example we attach the same callback onSampleReceivedCallback again + listener.attachEvent(subscriberRight, iox::popo::SubscriberEvent::HAS_DATA, onSampleReceivedCallback); + + // wait until someone presses CTRL+c + shutdownSemaphore.wait(); + + // optional detachEvent, but not required. + // 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 + listener.detachEvent(heartbeat); + listener.detachEvent(subscriberLeft, iox::popo::SubscriberEvent::HAS_DATA); + listener.detachEvent(subscriberRight, iox::popo::SubscriberEvent::HAS_DATA); + + heartbeatThread.join(); return (EXIT_SUCCESS); } diff --git a/iceoryx_examples/callbacks_in_c/CMakeLists.txt b/iceoryx_examples/callbacks_in_c/CMakeLists.txt new file mode 100644 index 0000000000..41de7eb03e --- /dev/null +++ b/iceoryx_examples/callbacks_in_c/CMakeLists.txt @@ -0,0 +1,59 @@ +# Copyright (c) 2021 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.5) +project(example_callbacks_in_c) + +include(GNUInstallDirs) + +find_package(iceoryx_posh CONFIG REQUIRED) +find_package(iceoryx_binding_c CONFIG REQUIRED) + +get_target_property(ICEORYX_CXX_STANDARD iceoryx_posh::iceoryx_posh CXX_STANDARD) +if ( NOT ICEORYX_CXX_STANDARD ) + include(IceoryxPlatform) +endif () + +# compiler flag not supported on C +list(REMOVE_ITEM ICEORYX_WARNINGS "-Wno-noexcept-type") + +add_executable(iox-ex-c-callbacks-publisher ./ice_c_callbacks_publisher.c) +set_source_files_properties(./ice_c_callbacks_publisher.c PROPERTIES LANGUAGE C) +target_link_libraries(iox-ex-c-callbacks-publisher + iceoryx_binding_c::iceoryx_binding_c +) +target_compile_options(iox-ex-c-callbacks-publisher PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) + +add_executable(iox-ex-c-callbacks-subscriber ./ice_c_callbacks_subscriber.c) +set_source_files_properties(./ice_c_callbacks_subscriber.c PROPERTIES LANGUAGE C) +target_link_libraries(iox-ex-c-callbacks-subscriber + iceoryx_binding_c::iceoryx_binding_c +) +target_compile_options(iox-ex-c-callbacks-subscriber PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) + +set_target_properties( + iox-ex-c-callbacks-subscriber + iox-ex-c-callbacks-publisher + PROPERTIES + CXX_STANDARD_REQUIRED ON + CXX_STANDARD ${ICEORYX_CXX_STANDARD} + POSITION_INDEPENDENT_CODE ON +) + +install( + TARGETS iox-ex-c-callbacks-publisher iox-ex-c-callbacks-subscriber + RUNTIME DESTINATION bin +) diff --git a/iceoryx_examples/callbacks_in_c/README.md b/iceoryx_examples/callbacks_in_c/README.md new file mode 100644 index 0000000000..b7509d9c98 --- /dev/null +++ b/iceoryx_examples/callbacks_in_c/README.md @@ -0,0 +1,158 @@ +# Listener in C (or how to use callbacks with iceoryx) + +## Introduction + +For a general introduction into the Listener concept please take a look at +the first part of the [Listener C++ example](../callbacks) and at the +Glossary of the [WaitSet C++ example](../waitset). + +## Expected output + + + +## Code walkthrough + +The C version of the callbacks example performs the identical tasks as the +C++ version. We have again an application which offers two services called +`Radar.FrontLeft.Counter` and `Radar.FrontRight.Counter`. Every time we have +received a sample from each service we calculate the sum of it. + +### ice_c_callbacks_publisher.c + +The publisher contains only already known iceoryx features. If some of them +are not known to you please take a look at the [icedelivery in c example](../icedelivery_in_c). + +### ice_c_callbacks_subscriber.c +#### int main() +The subscriber starts as usual by registering the process at the runtime. +In the next step we setup some `listenerStorage` on the stack where the listener +will be constructed in and initialize the listener which will start a background +thread for the upcoming event triggered callbacks. +```c +iox_listener_storage_t listenerStorage; +iox_listener_t listener = iox_listener_init(&listenerStorage); +``` + +Besides the subscribers we also would like to have an event which will be triggered +by ourselfs - the `heartbeat`. +```c +iox_user_trigger_storage_t heartbeatStorage; +heartbeat = iox_user_trigger_init(&heartbeatStorage); +``` + +Both subscribers are having the same options which we setup with: +```c +iox_sub_options_t options; +iox_sub_options_init(&options); +options.historyRequest = 10U; +options.queueCapacity = 5U; +options.nodeName = "iox-c-callback-subscriber-node"; +``` + +and then we can construct the two subscribers `subscriberLeft` and `subscriberRight`. +```c +iox_sub_t subscriberLeft = iox_sub_init(&subscriberLeftStorage, "Radar", "FrontLeft", "Counter", &options); +iox_sub_t subscriberRight = iox_sub_init(&subscriberRightStorage, "Radar", "FrontRight", "Counter", &options); +``` + +Now that everything is initialized we start our `heartbeatTriggerThread` which +triggers our `heartbeat` every 4 seconds. +```c +pthread_t heartbeatTriggerThread; +if (pthread_create(&heartbeatTriggerThread, NULL, cyclicHeartbeatTrigger, NULL)) +{ + printf("failed to create thread\n"); + return -1; +} +``` + +Attaching the subscribers and the heartbeat allows the Listener to call the callbacks +whenever the event is signaled by the _EventOrigin_. +```c +iox_listener_attach_user_trigger_event(listener, heartbeat, &heartbeatCallback); +iox_listener_attach_subscriber_event(listener, subscriberLeft, SubscriberEvent_HAS_DATA, &onSampleReceivedCallback); +iox_listener_attach_subscriber_event( + listener, subscriberRight, SubscriberEvent_HAS_DATA, &onSampleReceivedCallback); +``` +A user trigger can emit only one event therefore we do not provide the event type as +an argument in the user trigger attach call. + +Since we are following a push based approach - without an event loop which is pulling +the events and processing them, we require a blocker which waits until a signal +signals the process to terminate. +```c +while (keepRunning) +{ + sleep_for(100); +} +``` + +When `keepRunning` is set to false we cleanup all the resources. First we detach +the events from the Listener. This is an optional step since the Listener detaches +all events by itself when it is deinitialized. This goes also for all the _EventOrigins_, +if you for instance deinitialize an attached subscriber it will automatically detach +itself from the Listener. +```c +iox_listener_detach_user_trigger_event(listener, heartbeat); +iox_listener_detach_subscriber_event(listener, subscriberLeft, SubscriberEvent_HAS_DATA); +iox_listener_detach_subscriber_event(listener, subscriberRight, SubscriberEvent_HAS_DATA); +``` + +In a last step we have to release all acquired resources +```c +pthread_join(heartbeatTriggerThread, NULL); + +iox_user_trigger_deinit(heartbeat); +iox_sub_deinit(subscriberLeft); +iox_sub_deinit(subscriberRight); +iox_listener_deinit(listener); +``` + +#### The callbacks +Every callback must have a signature like `void (iox_event_origin_t)`. Our +`heartbeatCallback` just prints the message `heartbeat received` onto the console. +```c +void heartbeatCallback(iox_user_trigger_t userTrigger) +{ + (void)userTrigger; + printf("heartbeat received\n"); +} +``` + +The `onSampleReceivedCallback` is a little bit more complex. First we acquire +the chunk and then we have to find out which subscriber received the chunk. For that +we acquire the service description of the subscriber and if its instance equals +`FrontLeft` we store the chunk value in the `leftCache` otherwise in the `rightCache`. +```c +void onSampleReceivedCallback(iox_sub_t subscriber) +{ + const struct CounterTopic* chunk; + if (iox_sub_take_chunk(subscriber, (const void**)&chunk) == ChunkReceiveResult_SUCCESS) + { + iox_service_description_t serviceDescription = iox_sub_get_service_description(subscriber); + if (strcmp(serviceDescription.instanceString, "FrontLeft") == 0) + { + leftCache.value = *chunk; + leftCache.isSet = true; + } + else if (strcmp(serviceDescription.instanceString, "FrontRight") == 0) + { + rightCache.value = *chunk; + rightCache.isSet = true; + } +``` + +If both caches are set we can calculate the sum of both chunks and print them to +the console. To start fresh in the next cycle we reset the `leftCache` and +the `rightCache` afterwards. +```c + if (leftCache.isSet && rightCache.isSet) + { + printf("Received samples from FrontLeft and FrontRight. Sum of %d + %d = %d\n", + leftCache.value.counter, + rightCache.value.counter, + leftCache.value.counter + rightCache.value.counter); + leftCache.isSet = false; + rightCache.isSet = false; + } +``` diff --git a/iceoryx_examples/callbacks_in_c/ice_c_callbacks_publisher.c b/iceoryx_examples/callbacks_in_c/ice_c_callbacks_publisher.c new file mode 100644 index 0000000000..baadca7be5 --- /dev/null +++ b/iceoryx_examples/callbacks_in_c/ice_c_callbacks_publisher.c @@ -0,0 +1,86 @@ +// Copyright (c) 2021 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "iceoryx_binding_c/publisher.h" +#include "iceoryx_binding_c/runtime.h" +#include "sleep_for.h" +#include "topic_data.h" + +#include +#include +#include + +bool killswitch = false; + +static void sigHandler(int f_sig) +{ + // ignore unused parameter + (void)f_sig; + killswitch = true; +} + +void sending() +{ + iox_runtime_init("iox-ex-callbacks-publisher"); + + iox_pub_options_t options; + iox_pub_options_init(&options); + options.historyCapacity = 10U; + options.nodeName = "iox-c-callbacks-publisher-node"; + + iox_pub_storage_t publisherLeftStorage, publisherRightStorage; + + iox_pub_t publisherLeft = iox_pub_init(&publisherLeftStorage, "Radar", "FrontLeft", "Counter", &options); + iox_pub_t publisherRight = iox_pub_init(&publisherRightStorage, "Radar", "FrontRight", "Counter", &options); + + struct CounterTopic* chunk; + for (uint32_t counter = 0U; !killswitch; ++counter) + { + if (counter % 3 == 0) + { + if (iox_pub_loan_chunk(publisherLeft, (void**)&chunk, sizeof(struct CounterTopic)) + == AllocationResult_SUCCESS) + { + printf("Radar.FrontLeft.Counter sending : %d\n", counter); + fflush(stdout); + chunk->counter = counter; + iox_pub_publish_chunk(publisherLeft, chunk); + } + } + else + { + if (iox_pub_loan_chunk(publisherRight, (void**)&chunk, sizeof(struct CounterTopic)) + == AllocationResult_SUCCESS) + { + printf("Radar.FrontRight.Counter sending : %d\n", counter * 2); + fflush(stdout); + chunk->counter = counter * 2; + iox_pub_publish_chunk(publisherRight, chunk); + } + } + sleep_for(1000); + } +} + +int main() +{ + signal(SIGINT, sigHandler); + signal(SIGTERM, sigHandler); + + sending(); + + return 0; +} diff --git a/iceoryx_examples/callbacks_in_c/ice_c_callbacks_subscriber.c b/iceoryx_examples/callbacks_in_c/ice_c_callbacks_subscriber.c new file mode 100644 index 0000000000..e22cab44c2 --- /dev/null +++ b/iceoryx_examples/callbacks_in_c/ice_c_callbacks_subscriber.c @@ -0,0 +1,168 @@ +// Copyright (c) 2021 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + + +#include "iceoryx_binding_c/listener.h" +#include "iceoryx_binding_c/runtime.h" +#include "iceoryx_binding_c/subscriber.h" +#include "iceoryx_binding_c/types.h" +#include "iceoryx_binding_c/user_trigger.h" +#include "sleep_for.h" +#include "topic_data.h" + +#if !defined(_WIN32) +#include +#endif +#include +#include +#include +#include + +bool keepRunning = true; + +iox_user_trigger_t heartbeat; + +static void sigHandler(int signalValue) +{ + (void)signalValue; + // caught SIGINT or SIGTERM, now exit gracefully + keepRunning = false; +} + +struct cache_t +{ + struct CounterTopic value; + bool isSet; +}; + +struct cache_t leftCache = {.isSet = false}; +struct cache_t rightCache = {.isSet = false}; + +void heartbeatCallback(iox_user_trigger_t userTrigger) +{ + (void)userTrigger; + printf("heartbeat received\n"); + fflush(stdout); +} + +void* cyclicHeartbeatTrigger(void* dontCare) +{ + (void)dontCare; + while (keepRunning) + { + iox_user_trigger_trigger(heartbeat); + sleep_for(4000); + } + return NULL; +} + +void onSampleReceivedCallback(iox_sub_t subscriber) +{ + const struct CounterTopic* chunk; + if (iox_sub_take_chunk(subscriber, (const void**)&chunk) == ChunkReceiveResult_SUCCESS) + { + iox_service_description_t serviceDescription = iox_sub_get_service_description(subscriber); + if (strcmp(serviceDescription.instanceString, "FrontLeft") == 0) + { + leftCache.value = *chunk; + leftCache.isSet = true; + } + else if (strcmp(serviceDescription.instanceString, "FrontRight") == 0) + { + rightCache.value = *chunk; + rightCache.isSet = true; + } + printf("received: %d\n", chunk->counter); + fflush(stdout); + } + + if (leftCache.isSet && rightCache.isSet) + { + printf("Received samples from FrontLeft and FrontRight. Sum of %d + %d = %d\n", + leftCache.value.counter, + rightCache.value.counter, + leftCache.value.counter + rightCache.value.counter); + fflush(stdout); + leftCache.isSet = false; + rightCache.isSet = false; + } +} + +int main() +{ + signal(SIGINT, sigHandler); + signal(SIGTERM, sigHandler); + + iox_runtime_init("iox-c-callback-subscriber"); + + // the listener starts a background thread and the callbacks of the attached events + // will be called in this background thread when they are triggered + iox_listener_storage_t listenerStorage; + iox_listener_t listener = iox_listener_init(&listenerStorage); + + iox_user_trigger_storage_t heartbeatStorage; + heartbeat = iox_user_trigger_init(&heartbeatStorage); + + iox_sub_options_t options; + iox_sub_options_init(&options); + options.historyRequest = 10U; + options.queueCapacity = 5U; + options.nodeName = "iox-c-callback-subscriber-node"; + iox_sub_storage_t subscriberLeftStorage, subscriberRightStorage; + + iox_sub_t subscriberLeft = iox_sub_init(&subscriberLeftStorage, "Radar", "FrontLeft", "Counter", &options); + iox_sub_t subscriberRight = iox_sub_init(&subscriberRightStorage, "Radar", "FrontRight", "Counter", &options); + + // send a heartbeat every 4 seconds +#if !defined(_WIN32) + pthread_t heartbeatTriggerThread; + if (pthread_create(&heartbeatTriggerThread, NULL, cyclicHeartbeatTrigger, NULL)) + { + printf("failed to create thread\n"); + return -1; + } +#endif + + // attach everything to the listener, from here one the callbacks are called when an event occurs + iox_listener_attach_user_trigger_event(listener, heartbeat, &heartbeatCallback); + iox_listener_attach_subscriber_event(listener, subscriberLeft, SubscriberEvent_HAS_DATA, &onSampleReceivedCallback); + iox_listener_attach_subscriber_event( + listener, subscriberRight, SubscriberEvent_HAS_DATA, &onSampleReceivedCallback); + + // wait until someone presses CTRL+c + while (keepRunning) + { + sleep_for(100); + } + + // optional detachEvent, but not required. + // 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 + iox_listener_detach_user_trigger_event(listener, heartbeat); + iox_listener_detach_subscriber_event(listener, subscriberLeft, SubscriberEvent_HAS_DATA); + iox_listener_detach_subscriber_event(listener, subscriberRight, SubscriberEvent_HAS_DATA); + +#if !defined(_WIN32) + pthread_join(heartbeatTriggerThread, NULL); +#endif + + iox_user_trigger_deinit(heartbeat); + iox_sub_deinit(subscriberLeft); + iox_sub_deinit(subscriberRight); + iox_listener_deinit(listener); + + return 0; +} diff --git a/iceoryx_posh/source/popo/building_blocks/event_variable_data.cpp b/iceoryx_examples/callbacks_in_c/sleep_for.h similarity index 56% rename from iceoryx_posh/source/popo/building_blocks/event_variable_data.cpp rename to iceoryx_examples/callbacks_in_c/sleep_for.h index 4cff60df57..789039d068 100644 --- a/iceoryx_posh/source/popo/building_blocks/event_variable_data.cpp +++ b/iceoryx_examples/callbacks_in_c/sleep_for.h @@ -14,29 +14,24 @@ // // SPDX-License-Identifier: Apache-2.0 +#ifndef IOX_BINDING_C_SLEEP_FOR_H +#define IOX_BINDING_C_SLEEP_FOR_H -#include "iceoryx_posh/internal/popo/building_blocks/event_variable_data.hpp" +#ifdef _WIN32 +#include -namespace iox +void sleep_for(uint64_t milliseconds) { -namespace popo -{ -EventVariableData::EventVariableData() noexcept -{ - for (auto& id : m_activeNotifications) - { - id.store(false, std::memory_order_relaxed); - } + Sleep(milliseconds); } -EventVariableData::EventVariableData(const ProcessName_t& process) noexcept - : ConditionVariableData(process) +#else +#include + +void sleep_for(uint32_t milliseconds) { - for (auto& id : m_activeNotifications) - { - id.store(false, std::memory_order_relaxed); - } + usleep(milliseconds * 1000U); } -} // namespace popo -} // namespace iox +#endif +#endif diff --git a/iceoryx_examples/callbacks_in_c/topic_data.h b/iceoryx_examples/callbacks_in_c/topic_data.h new file mode 100644 index 0000000000..81df76f490 --- /dev/null +++ b/iceoryx_examples/callbacks_in_c/topic_data.h @@ -0,0 +1,27 @@ +// Copyright (c) 2021 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef IOX_EXAMPLES_WAITSET_TOPIC_DATA_HPP +#define IOX_EXAMPLES_WAITSET_TOPIC_DATA_HPP + +#include + +struct CounterTopic +{ + uint32_t counter; +}; + +#endif // IOX_EXAMPLES_WAITSET_TOPIC_DATA_HPP diff --git a/iceoryx_examples/icedelivery/iox_publisher.cpp b/iceoryx_examples/icedelivery/iox_publisher.cpp index 24eaf469db..ac906174eb 100644 --- a/iceoryx_examples/icedelivery/iox_publisher.cpp +++ b/iceoryx_examples/icedelivery/iox_publisher.cpp @@ -24,6 +24,7 @@ #include bool killswitch = false; +constexpr char APP_NAME[] = "iox-ex-publisher"; static void sigHandler(int f_sig [[gnu::unused]]) { @@ -42,7 +43,7 @@ int main() auto signalIntGuard = iox::posix::registerSignalHandler(iox::posix::Signal::INT, sigHandler); auto signalTermGuard = iox::posix::registerSignalHandler(iox::posix::Signal::TERM, sigHandler); - iox::runtime::PoshRuntime::initRuntime("iox-ex-publisher"); + iox::runtime::PoshRuntime::initRuntime(APP_NAME); iox::popo::Publisher publisher({"Radar", "FrontLeft", "Object"}); @@ -67,6 +68,8 @@ int main() else { auto error = result.get_error(); + // Ignore unused variable warning + (void)error; // Do something with error } @@ -82,6 +85,8 @@ int main() else { auto error = result.get_error(); + // Ignore unused variable warning + (void)error; // Do something with error } @@ -115,7 +120,7 @@ int main() publisher.publishResultOf(getRadarObject, ct); publisher.publishResultOf([&ct](RadarObject* object) { *object = RadarObject(ct, ct, ct); }); - std::cout << "Sent six times value: " << ct << std::endl; + std::cout << APP_NAME << " sent six times value: " << ct << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); } diff --git a/iceoryx_examples/icedelivery/iox_publisher_untyped.cpp b/iceoryx_examples/icedelivery/iox_publisher_untyped.cpp index f1238def6e..c39de8128b 100644 --- a/iceoryx_examples/icedelivery/iox_publisher_untyped.cpp +++ b/iceoryx_examples/icedelivery/iox_publisher_untyped.cpp @@ -24,6 +24,7 @@ #include bool killswitch = false; +constexpr char APP_NAME[] = "iox-ex-publisher-untyped"; static void sigHandler(int f_sig [[gnu::unused]]) { @@ -37,7 +38,7 @@ int main() auto signalIntGuard = iox::posix::registerSignalHandler(iox::posix::Signal::INT, sigHandler); auto signalTermGuard = iox::posix::registerSignalHandler(iox::posix::Signal::TERM, sigHandler); - iox::runtime::PoshRuntime::initRuntime("iox-ex-publisher-untyped"); + iox::runtime::PoshRuntime::initRuntime(APP_NAME); iox::popo::UntypedPublisher publisher({"Radar", "FrontLeft", "Object"}); @@ -64,6 +65,8 @@ int main() else { auto error = result.get_error(); + // Ignore unused variable warning + (void)error; // Do something with the error } @@ -77,10 +80,12 @@ int main() publisher.publish(chunk); }) .or_else([&](iox::popo::AllocationError error) { + // Ignore unused variable warning + (void)error; // Do something with the error }); - std::cout << "Sent two times value: " << ct << std::endl; + std::cout << APP_NAME << " sent two times value: " << ct << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); } diff --git a/iceoryx_examples/icedelivery/iox_publisher_with_options.cpp b/iceoryx_examples/icedelivery/iox_publisher_with_options.cpp index b93defbb62..e86db36237 100644 --- a/iceoryx_examples/icedelivery/iox_publisher_with_options.cpp +++ b/iceoryx_examples/icedelivery/iox_publisher_with_options.cpp @@ -23,6 +23,7 @@ #include bool killswitch = false; +constexpr char APP_NAME[] = "iox-ex-publisher-with-options"; static void sigHandler(int f_sig [[gnu::unused]]) { @@ -36,7 +37,7 @@ int main() auto signalIntGuard = iox::posix::registerSignalHandler(iox::posix::Signal::INT, sigHandler); auto signalTermGuard = iox::posix::registerSignalHandler(iox::posix::Signal::TERM, sigHandler); - iox::runtime::PoshRuntime::initRuntime("iox-ex-publisher-with-options"); + iox::runtime::PoshRuntime::initRuntime(APP_NAME); // create publisher with some options set iox::popo::PublisherOptions publisherOptions; @@ -63,7 +64,7 @@ int main() // Retrieve a sample, construct it with the given arguments and publish it via a lambda. publisher.loan(ct, ct, ct).and_then([](auto& sample) { sample.publish(); }); - std::cout << "Sent value: " << ct << std::endl; + std::cout << APP_NAME << " sent value: " << ct << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(400)); } diff --git a/iceoryx_examples/icedelivery/iox_subscriber.cpp b/iceoryx_examples/icedelivery/iox_subscriber.cpp index 80ae720e46..3bc3994816 100644 --- a/iceoryx_examples/icedelivery/iox_subscriber.cpp +++ b/iceoryx_examples/icedelivery/iox_subscriber.cpp @@ -24,6 +24,7 @@ #include bool killswitch = false; +constexpr char APP_NAME[] = "iox-ex-subscriber"; static void sigHandler(int f_sig [[gnu::unused]]) { @@ -38,7 +39,7 @@ int main() auto signalTermGuard = iox::posix::registerSignalHandler(iox::posix::Signal::TERM, sigHandler); // initialize runtime - iox::runtime::PoshRuntime::initRuntime("iox-ex-subscriber"); + iox::runtime::PoshRuntime::initRuntime(APP_NAME); // initialized subscriber iox::popo::SubscriberOptions subscriberOptions; @@ -51,7 +52,7 @@ int main() if (subscriber.getSubscriptionState() == iox::SubscribeState::SUBSCRIBED) { subscriber.take() - .and_then([](auto& sample) { std::cout << "Got value: " << sample->x << std::endl; }) + .and_then([](auto& sample) { std::cout << APP_NAME << " got value: " << sample->x << std::endl; }) .or_else([](auto& result) { // only has to be called if the alternative is of interest, // i.e. if nothing has to happen when no data is received and diff --git a/iceoryx_examples/icedelivery/iox_subscriber_untyped.cpp b/iceoryx_examples/icedelivery/iox_subscriber_untyped.cpp index 377d6d2e42..c79f9615b0 100644 --- a/iceoryx_examples/icedelivery/iox_subscriber_untyped.cpp +++ b/iceoryx_examples/icedelivery/iox_subscriber_untyped.cpp @@ -24,6 +24,7 @@ #include bool killswitch = false; +constexpr char APP_NAME[] = "iox-ex-subscriber-untyped"; static void sigHandler(int f_sig [[gnu::unused]]) { @@ -38,7 +39,7 @@ int main() auto signalTermGuard = iox::posix::registerSignalHandler(iox::posix::Signal::TERM, sigHandler); // initialize runtime - iox::runtime::PoshRuntime::initRuntime("iox-ex-subscriber-untyped"); + iox::runtime::PoshRuntime::initRuntime(APP_NAME); // initialized subscriber iox::popo::SubscriberOptions subscriberOptions; @@ -53,7 +54,7 @@ int main() subscriber.take() .and_then([&](const void* data) { auto object = static_cast(data); - std::cout << "Got value: " << object->x << std::endl; + std::cout << APP_NAME << " got value: " << object->x << std::endl; // note that we explicitly have to release the data // and afterwards the pointer access is undefined behavior diff --git a/iceoryx_examples/icedelivery/iox_subscriber_with_options.cpp b/iceoryx_examples/icedelivery/iox_subscriber_with_options.cpp index d7d0eefa92..533223c536 100644 --- a/iceoryx_examples/icedelivery/iox_subscriber_with_options.cpp +++ b/iceoryx_examples/icedelivery/iox_subscriber_with_options.cpp @@ -23,6 +23,7 @@ #include bool killswitch = false; +constexpr char APP_NAME[] = "iox-ex-subscriber-with-options"; static void sigHandler(int f_sig [[gnu::unused]]) { @@ -37,7 +38,7 @@ int main() auto signalTermGuard = iox::posix::registerSignalHandler(iox::posix::Signal::TERM, sigHandler); // initialize runtime - iox::runtime::PoshRuntime::initRuntime("iox-ex-subscriber-with-options"); + iox::runtime::PoshRuntime::initRuntime(APP_NAME); // create subscriber with some options set iox::popo::SubscriberOptions subscriberOptions; @@ -73,7 +74,7 @@ int main() do { subscriber.take() - .and_then([](auto& object) { std::cout << "Got value: " << object->x << std::endl; }) + .and_then([](auto& object) { std::cout << APP_NAME << " got value: " << object->x << std::endl; }) .or_else([&](auto&) { hasMoreSamples = false; }); } while (hasMoreSamples); } diff --git a/iceoryx_examples/icedelivery_in_c/CMakeLists.txt b/iceoryx_examples/icedelivery_in_c/CMakeLists.txt index acb0b410a6..3c006078dc 100644 --- a/iceoryx_examples/icedelivery_in_c/CMakeLists.txt +++ b/iceoryx_examples/icedelivery_in_c/CMakeLists.txt @@ -28,6 +28,9 @@ if ( NOT ICEORYX_CXX_STANDARD ) include(IceoryxPlatform) endif () +# compiler flag not supported on C +list(REMOVE_ITEM ICEORYX_WARNINGS "-Wno-noexcept-type") + add_executable(iox-c-publisher ./ice_c_publisher.c) set_source_files_properties(./ice_c_publisher.c PROPERTIES LANGUAGE C) target_link_libraries(iox-c-publisher diff --git a/iceoryx_examples/icedelivery_in_c/ice_c_publisher.c b/iceoryx_examples/icedelivery_in_c/ice_c_publisher.c index ad08ea0e87..8219fcfd80 100644 --- a/iceoryx_examples/icedelivery_in_c/ice_c_publisher.c +++ b/iceoryx_examples/icedelivery_in_c/ice_c_publisher.c @@ -28,6 +28,7 @@ bool killswitch = false; static void sigHandler(int signalValue) { + // Ignore unused variable warning (void)signalValue; // caught SIGINT or SIGTERM, now exit gracefully killswitch = true; @@ -58,6 +59,7 @@ void sending() sample->z = ct; printf("Sent value: %.0f\n", ct); + fflush(stdout); iox_pub_publish_chunk(publisher, chunk); diff --git a/iceoryx_examples/icedelivery_in_c/ice_c_subscriber.c b/iceoryx_examples/icedelivery_in_c/ice_c_subscriber.c index f8dfad8d56..dd25e557a1 100644 --- a/iceoryx_examples/icedelivery_in_c/ice_c_subscriber.c +++ b/iceoryx_examples/icedelivery_in_c/ice_c_subscriber.c @@ -29,6 +29,7 @@ bool killswitch = false; static void sigHandler(int signalValue) { + // Ignore unused variable warning (void)signalValue; // caught SIGINT or SIGTERM, now exit gracefully killswitch = true; @@ -62,6 +63,7 @@ void receiving() { const struct RadarObject* sample = (const struct RadarObject*)(chunk); printf("Got value: %.0f\n", sample->x); + fflush(stdout); iox_sub_release_chunk(subscriber, chunk); } printf("\n"); diff --git a/iceoryx_examples/iceperf/mq.hpp b/iceoryx_examples/iceperf/mq.hpp index 82b7a8756d..36516625c3 100644 --- a/iceoryx_examples/iceperf/mq.hpp +++ b/iceoryx_examples/iceperf/mq.hpp @@ -31,8 +31,8 @@ class MQ : public IcePerfBase { public: - static constexpr size_t MAX_MESSAGE_SIZE = 4 * IcePerfBase::ONE_KILOBYTE; - static constexpr size_t MAX_MESSAGES = 8; + static constexpr uint32_t MAX_MESSAGE_SIZE = 4 * IcePerfBase::ONE_KILOBYTE; + static constexpr uint32_t MAX_MESSAGES = 8; static constexpr int32_t ERROR_CODE = -1; static constexpr mqd_t INVALID_DESCRIPTOR = -1; diff --git a/iceoryx_examples/singleprocess/single_process.cpp b/iceoryx_examples/singleprocess/single_process.cpp index 9674b03681..c0bb8a3582 100644 --- a/iceoryx_examples/singleprocess/single_process.cpp +++ b/iceoryx_examples/singleprocess/single_process.cpp @@ -23,6 +23,7 @@ #include "iceoryx_posh/roudi/iceoryx_roudi_components.hpp" #include "iceoryx_posh/runtime/posh_runtime_single_process.hpp" #include "iceoryx_utils/log/logmanager.hpp" +#include "iceoryx_utils/posix_wrapper/signal_handler.hpp" #include #include @@ -33,6 +34,12 @@ std::atomic_bool keepRunning{true}; +static void sigHandler(int f_sig [[gnu::unused]]) +{ + // caught SIGINT or SIGTERM, now exit gracefully + keepRunning = false; +} + struct TransmissionData_t { uint64_t counter; @@ -102,6 +109,10 @@ void subscriber() int main() { + // Register sigHandler + auto signalIntGuard = iox::posix::registerSignalHandler(iox::posix::Signal::INT, sigHandler); + auto signalTermGuard = iox::posix::registerSignalHandler(iox::posix::Signal::TERM, sigHandler); + // set the log level to error to see the essence of the example iox::log::LogManager::GetLogManager().SetDefaultLogLevel(iox::log::LogLevel::kError); diff --git a/iceoryx_examples/waitset/README.md b/iceoryx_examples/waitset/README.md index 6c18525933..4d949f5277 100644 --- a/iceoryx_examples/waitset/README.md +++ b/iceoryx_examples/waitset/README.md @@ -1,5 +1,7 @@ # WaitSet +## Introduction + The WaitSet is a set where you can attach objects so that they can signal a wide variety of events to one single notifyable. The typical approach is that one creates a WaitSet attaches multiple subscribers, user trigger or other _Triggerables_ to it and then wait till @@ -11,6 +13,11 @@ till you reset the state. The `HAS_DATA` event of the subscriber for instance will notify you as long as there are samples. But it is also possible that one attaches one shot events. These are events which will trigger the WaitSet only once. +## Expected output + + + + ## Threadsafety The WaitSet is **not** threadsafe! - It is **not** allowed to attach or detach _Triggerable_ diff --git a/iceoryx_examples/waitset/ice_waitset_grouping.cpp b/iceoryx_examples/waitset/ice_waitset_grouping.cpp index 5f4c3e567a..b7388fe26d 100644 --- a/iceoryx_examples/waitset/ice_waitset_grouping.cpp +++ b/iceoryx_examples/waitset/ice_waitset_grouping.cpp @@ -52,6 +52,8 @@ int main() { subscriberVector.emplace_back(iox::capro::ServiceDescription{"Radar", "FrontLeft", "Counter"}); auto& subscriber = subscriberVector.back(); + // Ignore unused variable warning + (void)subscriber; } constexpr uint64_t FIRST_GROUP_ID = 123U; diff --git a/iceoryx_examples/waitset/ice_waitset_trigger.cpp b/iceoryx_examples/waitset/ice_waitset_trigger.cpp index 1c9637cd6d..8922127fe1 100644 --- a/iceoryx_examples/waitset/ice_waitset_trigger.cpp +++ b/iceoryx_examples/waitset/ice_waitset_trigger.cpp @@ -96,6 +96,8 @@ class MyTriggerClass static void callOnAction(MyTriggerClass* const triggerClassPtr) { + // Ignore unused variable warning + (void)triggerClassPtr; std::cout << "action performed" << std::endl; } diff --git a/iceoryx_examples/waitset_in_c/CMakeLists.txt b/iceoryx_examples/waitset_in_c/CMakeLists.txt index 32a822838a..fd95d182c7 100644 --- a/iceoryx_examples/waitset_in_c/CMakeLists.txt +++ b/iceoryx_examples/waitset_in_c/CMakeLists.txt @@ -26,6 +26,9 @@ if ( NOT ICEORYX_CXX_STANDARD ) include(IceoryxPlatform) endif () +# compiler flag not supported on C +list(REMOVE_ITEM ICEORYX_WARNINGS "-Wno-noexcept-type") + add_executable(iox-ex-c-waitset-publisher ./ice_c_waitset_publisher.c) target_link_libraries(iox-ex-c-waitset-publisher iceoryx_binding_c::iceoryx_binding_c 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 208540ea9a..354c59978e 100644 --- a/iceoryx_examples/waitset_in_c/ice_c_waitset_gateway.c +++ b/iceoryx_examples/waitset_in_c/ice_c_waitset_gateway.c @@ -50,7 +50,8 @@ void subscriberCallback(iox_sub_t const subscriber) const void* chunk; if (iox_sub_take_chunk(subscriber, &chunk)) { - printf("subscriber: %p received %u\n", subscriber, ((struct CounterTopic*)chunk)->counter); + printf("subscriber: %p received %u\n", (void*)subscriber, ((struct CounterTopic*)chunk)->counter); + fflush(stdout); iox_sub_release_chunk(subscriber, chunk); } 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 aefee06a29..1c0e69a10a 100644 --- a/iceoryx_examples/waitset_in_c/ice_c_waitset_grouping.c +++ b/iceoryx_examples/waitset_in_c/ice_c_waitset_grouping.c @@ -36,6 +36,7 @@ iox_user_trigger_t shutdownTrigger; static void sigHandler(int signalValue) { + // Ignore unused variable warning (void)signalValue; iox_user_trigger_trigger(shutdownTrigger); @@ -117,6 +118,7 @@ int main() if (iox_sub_take_chunk(subscriber, &chunk)) { printf("received: %u\n", ((struct CounterTopic*)chunk)->counter); + fflush(stdout); iox_sub_release_chunk(subscriber, chunk); } 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 6de6edfe84..516970443d 100644 --- a/iceoryx_examples/waitset_in_c/ice_c_waitset_individual.c +++ b/iceoryx_examples/waitset_in_c/ice_c_waitset_individual.c @@ -36,6 +36,7 @@ iox_user_trigger_t shutdownTrigger; static void sigHandler(int signalValue) { + // Ignore unused variable warning (void)signalValue; iox_user_trigger_trigger(shutdownTrigger); @@ -106,6 +107,7 @@ int main() if (iox_sub_take_chunk(subscriber[0U], &chunk)) { printf("subscriber 1 received: %u\n", ((struct CounterTopic*)chunk)->counter); + fflush(stdout); iox_sub_release_chunk(subscriber[0U], chunk); } @@ -118,6 +120,7 @@ int main() // instantly. iox_sub_release_queued_chunks(subscriber[1U]); printf("subscriber 2 received something - dont care\n"); + fflush(stdout); } } } diff --git a/iceoryx_examples/waitset_in_c/ice_c_waitset_publisher.c b/iceoryx_examples/waitset_in_c/ice_c_waitset_publisher.c index a738c3171a..31b0a9c777 100644 --- a/iceoryx_examples/waitset_in_c/ice_c_waitset_publisher.c +++ b/iceoryx_examples/waitset_in_c/ice_c_waitset_publisher.c @@ -28,6 +28,7 @@ bool killswitch = false; static void sigHandler(int signalValue) { + // Ignore unused variable warning (void)signalValue; // caught SIGINT or SIGTERM, now exit gracefully killswitch = true; @@ -53,6 +54,7 @@ void sending() sample->counter = counter; printf("Sending: %u\n", counter); + fflush(stdout); iox_pub_publish_chunk(publisher, chunk); diff --git a/iceoryx_examples/waitset_in_c/ice_c_waitset_sync.c b/iceoryx_examples/waitset_in_c/ice_c_waitset_sync.c index fba71ee9cf..a20d50d317 100644 --- a/iceoryx_examples/waitset_in_c/ice_c_waitset_sync.c +++ b/iceoryx_examples/waitset_in_c/ice_c_waitset_sync.c @@ -42,6 +42,7 @@ bool keepRunning = true; static void sigHandler(int signalValue) { + // Ignore unused variable warning (void)signalValue; iox_user_trigger_trigger(shutdownTrigger); @@ -50,6 +51,7 @@ static void sigHandler(int signalValue) void cyclicRun(iox_user_trigger_t trigger) { printf("activation callback\n"); + fflush(stdout); // after every call we have to reset the trigger otherwise the waitset // would immediately call us again since we still signal to the waitset that // we have been triggered (waitset is state based) @@ -58,6 +60,7 @@ void cyclicRun(iox_user_trigger_t trigger) void* cyclicTriggerCallback(void* dontCare) { + // Ignore unused variable warning (void)dontCare; while (keepRunning) { diff --git a/iceoryx_integrationtest/CMakeLists.txt b/iceoryx_integrationtest/CMakeLists.txt new file mode 100644 index 0000000000..b72c50dcf3 --- /dev/null +++ b/iceoryx_integrationtest/CMakeLists.txt @@ -0,0 +1,39 @@ +# Copyright 2021 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. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.5) +project(iceoryx_integrationtest) + +find_package(ament_cmake REQUIRED) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() + find_package(ros_testing) + + add_ros_test(iceoryx_integrationtest/test_roudi_startup_shutdown.py) + add_ros_test(iceoryx_integrationtest/test_callback_example.py) + add_ros_test(iceoryx_integrationtest/test_callback_in_c_example.py) + add_ros_test(iceoryx_integrationtest/test_multi_publisher_example.py) + add_ros_test(iceoryx_integrationtest/test_icedelivery_example.py) + add_ros_test(iceoryx_integrationtest/test_icedelivery_in_c_example.py) + add_ros_test(iceoryx_integrationtest/test_singleprocess_example.py) + add_ros_test(iceoryx_integrationtest/test_waitset_example.py) + add_ros_test(iceoryx_integrationtest/test_waitset_in_c_example.py) + +endif() + +ament_package() diff --git a/iceoryx_integrationtest/Readme.md b/iceoryx_integrationtest/Readme.md new file mode 100644 index 0000000000..0a938ce42b --- /dev/null +++ b/iceoryx_integrationtest/Readme.md @@ -0,0 +1,111 @@ +# Iceoryx Integrationtests + +## Introduction +To ensure quality standards in iceoryx, we are using automated testing to ensure ISO26262 on unittest and integrationtest level in the respective components. +Additionally we need to make sure that the customer-facing API is functional and software integration specifications are fulfilled. +Test focus is here on SWE.5 according to ASPICE. + +For that purpose we bring in tests which simulate customer behavior to have automatic testing of (Mis)Use-cases. +As testing framework we use the [launch_testing](https://github.com/ros2/launch/tree/master/launch_testing) framework from ROS 2. +The tests are stored in a ROS 2 CMake package where the test executables are build and launched/tested by python scripts. +In these scripts the testing is currently done by evaluating the stdout output of the processes and the exit codes. + +advantages: +- automatic test if processes have exited unexpectedly +- test against custom or error return codes to evaluate error-cases +- test the order of printed messages + +limitations: +- testing against stdout is error prone +- limited functionality for performance testing because the stdout is buffered (messages could be reordered) + +## Setup +For building and executing the tests you need to have ROS2 installed. Please follow the instructions on https://index.ros.org/doc/ros2/Installation. +The system tests are currently tested on ROS 2 "Foxy Fitzroy" in Ubuntu 20.04 LTS. + +For a basic setup you need to install the following packages: +```bash +sudo apt install ros-foxy-ros-base ros-foxy-ros-testing ros-foxy-launch-testing ros-foxy-ament-cmake python3-colcon-common-extensions +``` +For the future versions you can use the corresponding ROS2 release. + +After installing you need to source ROS 2 to make the environment available in your terminal: +```bash +source /opt/ros/foxy/setup.bash +``` + +**_NOTE:_** You can add the source command to your `~/.bashrc` for automatic loading the ROS2 workspace at boot time. + +Required for the colcon build of iceoryx is that the repository is located within a ROS workspace like this: +``` +iceoryx_workspace +└── src + └── iceoryx + ├── cmake + ├── cpptoml_vendor + ├── doc + ├── iceoryx_binding_c + ├── iceoryx_dds + ├── iceoryx_examples + ├── iceoryx_meta + ├── iceoryx_posh + ├── iceoryx_integrationtest + ├── iceoryx_utils + └── tools +``` + +## Test Build and Execution + +Go into your iceoryx_workspace folder and do the colcon build: +```bash +colcon build +``` +Expected output should be like this: `Summary: 13 packages finished [24.1s]` +Colcon automatically creates the folders `build`, `install` and `log`. + +**_NOTE:_** If you get an build error related to CycloneDDS, please delete the build folder from the iceoryx directory. + +For executing tests you can use colcon too: +```bash +source install/setup.bash # your install directory created by colcon +colcon test --packages-select iceoryx_integrationtest +``` +For the case that a test fails the output look like this +```bash +--- stderr: iceoryx_integrationtest +Errors while running CTest +--- +Finished <<< iceoryx_integrationtest [7.49s] [ with test failures ] + +Summary: 13 packages finished [7.80s] + 1 package had stderr output: iceoryx_integrationtest + 1 package had test failures: iceoryx_integrationtest +``` + +For observing test logs in an error case you can use `colcon test-result`: +```bash +colcon test-result --all --verbose +``` + +With colcon all tests in the iceoryx_integrationtest package are executed. In particular cases you want to execute only +one test file. This can be done the following way: +```bash +launch_test src/iceoryx/iceoryx_integrationtest/iceoryx_integrationtest/test_roudi_startup_shutdown.py +``` + +An output for a failing test could look like this: +```bash + ====================================================================== + FAIL: test_roudi_ready (iceoryx_integrationtest.TestRouDiProcess) + ---------------------------------------------------------------------- + Traceback (most recent call last): + File "iceoryx_workspace/src/iceoryx/iceoryx_integrationtest/iceoryx_integrationtest/test_roudi_startup_shutdown.py", line 52, in test_roudi_ready + proc_output.assertWaitFor( + File "/opt/ros/foxy/lib/python3.8/site-packages/launch_testing/io_handler.py", line 146, in assertWaitFor + assert success, 'Waiting for output timed out' + AssertionError: Waiting for output timed out +``` + +## Open points +- use an alternative way of tracing test information of the test processes without involving iceoryx (e.g. DDS or some tracing lib) +- add gtest for detailed testing in the test processes diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_callback_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_callback_example.py new file mode 100755 index 0000000000..682a6df038 --- /dev/null +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_callback_example.py @@ -0,0 +1,103 @@ +# Copyright (c) 2021 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os + +import unittest + +import launch +from launch_ros.substitutions import ExecutableInPackage +import launch_testing +import launch_testing.actions +from launch_testing.asserts import assertSequentialStdout + +import pytest + + +# @brief Test goal: "Integrationtest for the callback example of iceoryx" +# @pre setup ROS2 launch executables for RouDi (debug mode) and the example processes +# @post check if all applications return exitcode 0 (success) after test run +@pytest.mark.launch_test +def generate_test_description(): + + proc_env = os.environ.copy() + colcon_prefix_path = os.environ.get('COLCON_PREFIX_PATH', '') + + roudi_executable = os.path.join( + colcon_prefix_path, + 'iceoryx_posh/bin/', + 'iox-roudi' + ) + roudi_process = launch.actions.ExecuteProcess( + cmd=[roudi_executable, '-l', 'debug'], + env=proc_env, output='screen', + sigterm_timeout='20') + + callback_publisher_executable = os.path.join( + colcon_prefix_path, + 'example_callbacks/bin/', + 'iox-ex-callbacks-publisher' + ) + callback_publisher_process = launch.actions.ExecuteProcess( + cmd=[callback_publisher_executable], + env=proc_env, output='screen') + + callback_subscriber_executable = os.path.join( + colcon_prefix_path, + 'example_callbacks/bin/', + 'iox-ex-callbacks-subscriber' + ) + callback_subscriber_process = launch.actions.ExecuteProcess( + cmd=[callback_subscriber_executable], + env=proc_env, output='screen') + + return launch.LaunchDescription([ + callback_publisher_process, + callback_subscriber_process, + roudi_process, + launch_testing.actions.ReadyToTest() + ]), {'roudi_process': roudi_process, 'callback_publisher_process': callback_publisher_process, 'callback_subscriber_process': callback_subscriber_process} + +# These tests will run concurrently with the dut process. After this test is done, +# the launch system will shut down RouDi + + +class TestCallBackExample(unittest.TestCase): + def test_roudi_ready(self, proc_output): + proc_output.assertWaitFor( + 'RouDi is ready for clients', timeout=45, stream='stdout') + + def test_callback_heartbeat(self, proc_output): + proc_output.assertWaitFor( + 'heartbeat received', timeout=45, stream='stdout') + + def test_callback_example_data_exchange(self, proc_output): + proc_output.assertWaitFor( + 'Radar.FrontLeft.Counter sending : 9', timeout=45, stream='stdout') + proc_output.assertWaitFor( + 'Radar.FrontRight.Counter sending : 16', timeout=45, stream='stdout') + proc_output.assertWaitFor( + 'received: 10', timeout=45, stream='stdout') + proc_output.assertWaitFor( + 'Received samples from FrontLeft and FrontRight. Sum of 9 + 16 = 25', timeout=45, stream='stdout') + +# These tests run after shutdown and examine the stdout log + + +@launch_testing.post_shutdown_test() +class TestMultiCallbackExampleExitCodes(unittest.TestCase): + def test_exit_code(self, proc_info): + launch_testing.asserts.assertExitCodes(proc_info) diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_callback_in_c_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_callback_in_c_example.py new file mode 100755 index 0000000000..e4434dd21a --- /dev/null +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_callback_in_c_example.py @@ -0,0 +1,103 @@ +# Copyright (c) 2021 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os + +import unittest + +import launch +from launch_ros.substitutions import ExecutableInPackage +import launch_testing +import launch_testing.actions +from launch_testing.asserts import assertSequentialStdout + +import pytest + + +# @brief Test goal: "Integrationtest for the callback in C example of iceoryx" +# @pre setup ROS2 launch executables for RouDi (debug mode) and the example processes +# @post check if all applications return exitcode 0 (success) after test run +@pytest.mark.launch_test +def generate_test_description(): + + proc_env = os.environ.copy() + colcon_prefix_path = os.environ.get('COLCON_PREFIX_PATH', '') + + roudi_executable = os.path.join( + colcon_prefix_path, + 'iceoryx_posh/bin/', + 'iox-roudi' + ) + roudi_process = launch.actions.ExecuteProcess( + cmd=[roudi_executable, '-l', 'debug'], + env=proc_env, output='screen', + sigterm_timeout='20') + + callback_publisher_executable = os.path.join( + colcon_prefix_path, + 'example_callbacks_in_c/bin/', + 'iox-ex-c-callbacks-publisher' + ) + callback_publisher_process = launch.actions.ExecuteProcess( + cmd=[callback_publisher_executable], + env=proc_env, output='screen') + + callback_subscriber_executable = os.path.join( + colcon_prefix_path, + 'example_callbacks_in_c/bin/', + 'iox-ex-c-callbacks-subscriber' + ) + callback_subscriber_process = launch.actions.ExecuteProcess( + cmd=[callback_subscriber_executable], + env=proc_env, output='screen') + + return launch.LaunchDescription([ + callback_publisher_process, + callback_subscriber_process, + roudi_process, + launch_testing.actions.ReadyToTest() + ]), {'roudi_process': roudi_process, 'callback_publisher_process': callback_publisher_process, 'callback_subscriber_process': callback_subscriber_process} + +# These tests will run concurrently with the dut process. After this test is done, +# the launch system will shut down RouDi + + +class TestCallBackInCExample(unittest.TestCase): + def test_roudi_ready(self, proc_output): + proc_output.assertWaitFor( + 'RouDi is ready for clients', timeout=45, stream='stdout') + + def test_callback_heartbeat(self, proc_output): + proc_output.assertWaitFor( + 'heartbeat received', timeout=45, stream='stdout') + + def test_callback_example_data_exchange(self, proc_output): + proc_output.assertWaitFor( + 'Radar.FrontLeft.Counter sending : 9', timeout=45, stream='stdout') + proc_output.assertWaitFor( + 'Radar.FrontRight.Counter sending : 16', timeout=45, stream='stdout') + proc_output.assertWaitFor( + 'received: 10', timeout=45, stream='stdout') + proc_output.assertWaitFor( + 'Received samples from FrontLeft and FrontRight. Sum of 9 + 22 = 31', timeout=45, stream='stdout') + +# These tests run after shutdown and examine the stdout log + + +@launch_testing.post_shutdown_test() +class TestMultiCallbackInCExampleExitCodes(unittest.TestCase): + def test_exit_code(self, proc_info): + launch_testing.asserts.assertExitCodes(proc_info) diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_icedelivery_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_icedelivery_example.py new file mode 100755 index 0000000000..6270a9ad96 --- /dev/null +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_icedelivery_example.py @@ -0,0 +1,110 @@ +# Copyright (c) 2021 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os + +import unittest + +import launch +from launch_ros.substitutions import ExecutableInPackage +import launch_testing +import launch_testing.actions +from launch_testing.asserts import assertSequentialStdout + +import pytest + +# @brief Test goal: "Integrationtest for the icedelivery example of iceoryx" +# @pre setup ROS2 launch executables for RouDi (debug mode) and the example processes +# @post check if all applications return exitcode 0 (success) after test run +@pytest.mark.launch_test +def generate_test_description(): + + proc_env = os.environ.copy() + colcon_prefix_path = os.environ.get('COLCON_PREFIX_PATH', '') + executable_list = ['iox-ex-publisher', 'iox-ex-subscriber', + 'iox-ex-publisher-untyped', 'iox-ex-subscriber-untyped', + 'iox-ex-publisher-with-options', 'iox-ex-subscriber-with-options'] + process_list = [] + + for exec in executable_list: + tmp_exec = os.path.join( + colcon_prefix_path, + 'example_icedelivery/bin/', + exec) + tmp_process = launch.actions.ExecuteProcess( + cmd=[tmp_exec], + env=proc_env, output='screen') + process_list.append(tmp_process) + + print("Process list:", process_list) + + roudi_executable = os.path.join( + colcon_prefix_path, + 'iceoryx_posh/bin/', + 'iox-roudi' + ) + roudi_process = launch.actions.ExecuteProcess( + cmd=[roudi_executable, '-l', 'debug'], + env=proc_env, output='screen', + sigterm_timeout='20') + + return launch.LaunchDescription([ + process_list[0], + process_list[1], + process_list[2], + process_list[3], + process_list[4], + process_list[5], + roudi_process, + launch_testing.actions.ReadyToTest() + ]), {'iox-ex-publisher': process_list[0], 'iox-ex-subscriber': process_list[1], + 'iox-ex-publisher-untyped': process_list[2], 'iox-ex-subscriber-untyped': process_list[3], + 'iox-ex-publisher-with-options': process_list[4], 'iox-ex-subscriber-with-options': process_list[5], 'roudi_process': roudi_process} + +# These tests will run concurrently with the dut process. After this test is done, +# the launch system will shut down RouDi + + +class TestIcedeliveryExample(unittest.TestCase): + def test_roudi_ready(self, proc_output): + proc_output.assertWaitFor( + 'RouDi is ready for clients', timeout=45, stream='stdout') + + def test_publisher_subscriber_data_exchange(self, proc_output): + proc_output.assertWaitFor( + 'iox-ex-publisher sent six times value: 15', timeout=45, stream='stdout') + proc_output.assertWaitFor( + 'iox-ex-subscriber got value: 15', timeout=45, stream='stdout') + + def test_publisher_subscriber_untyped_data_exchange(self, proc_output): + proc_output.assertWaitFor( + 'iox-ex-publisher-untyped sent two times value: 15', timeout=45, stream='stdout') + proc_output.assertWaitFor( + 'iox-ex-subscriber-untyped got value: 15', timeout=45, stream='stdout') + + def test_publisher_subscriber_with_options_data_exchange(self, proc_output): + proc_output.assertWaitFor( + 'iox-ex-publisher-with-options sent value: 15', timeout=45, stream='stdout') + proc_output.assertWaitFor( + 'iox-ex-subscriber-with-options got value: 15', timeout=45, stream='stdout') + +# These tests run after shutdown and examine the stdout log + + +@ launch_testing.post_shutdown_test() +class TestIcedeliveryExampleExitCodes(unittest.TestCase): + def test_exit_code(self, proc_info): + launch_testing.asserts.assertExitCodes(proc_info) diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_icedelivery_in_c_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_icedelivery_in_c_example.py new file mode 100755 index 0000000000..9087bcb781 --- /dev/null +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_icedelivery_in_c_example.py @@ -0,0 +1,94 @@ +# Copyright (c) 2021 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os + +import unittest + +import launch +from launch_ros.substitutions import ExecutableInPackage +import launch_testing +import launch_testing.actions +from launch_testing.asserts import assertSequentialStdout + +import pytest + +# @brief Test goal: "Integrationtest for the icedelivery in C example of iceoryx" +# @pre setup ROS2 launch executables for RouDi (debug mode) and the example processes +# @post check if all applications return exitcode 0 (success) after test run +@pytest.mark.launch_test +def generate_test_description(): + + proc_env = os.environ.copy() + colcon_prefix_path = os.environ.get('COLCON_PREFIX_PATH', '') + + roudi_executable = os.path.join( + colcon_prefix_path, + 'iceoryx_posh/bin/', + 'iox-roudi' + ) + roudi_process = launch.actions.ExecuteProcess( + cmd=[roudi_executable, '-l', 'debug'], + env=proc_env, output='screen', + sigterm_timeout='20') + + publisher_c_executable = os.path.join( + colcon_prefix_path, + 'example_icedelivery_in_c/bin/', + 'iox-c-publisher' + ) + publisher_c_process = launch.actions.ExecuteProcess( + cmd=[publisher_c_executable], + env=proc_env, output='screen') + + subscriber_c_executable = os.path.join( + colcon_prefix_path, + 'example_icedelivery_in_c/bin/', + 'iox-c-subscriber' + ) + subscriber_c_process = launch.actions.ExecuteProcess( + cmd=[subscriber_c_executable], + env=proc_env, output='screen') + + return launch.LaunchDescription([ + publisher_c_process, + subscriber_c_process, + roudi_process, + launch_testing.actions.ReadyToTest() + ]), {'roudi_process': roudi_process, 'publisher_c_process': publisher_c_process, 'subscriber_c_process': subscriber_c_process} + +# These tests will run concurrently with the dut process. After this test is done, +# the launch system will shut down RouDi + + +class TestIcedeliveryInCExample(unittest.TestCase): + def test_roudi_ready(self, proc_output): + proc_output.assertWaitFor( + 'RouDi is ready for clients', timeout=45, stream='stdout') + + def test_icedelivery_in_c_data_exchange(self, proc_output): + proc_output.assertWaitFor( + 'Sent value: 5', timeout=45, stream='stdout') + proc_output.assertWaitFor( + 'Got value: 5', timeout=45, stream='stdout') + +# These tests run after shutdown and examine the stdout log + + +@launch_testing.post_shutdown_test() +class TestIcedeliveryInCExampleExitCodes(unittest.TestCase): + def test_exit_code(self, proc_info): + launch_testing.asserts.assertExitCodes(proc_info) diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_multi_publisher_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_multi_publisher_example.py new file mode 100755 index 0000000000..247017961c --- /dev/null +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_multi_publisher_example.py @@ -0,0 +1,98 @@ +# Copyright (c) 2021 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os + +import unittest + +import launch +from launch_ros.substitutions import ExecutableInPackage +import launch_testing +import launch_testing.actions +from launch_testing.asserts import assertSequentialStdout + +import pytest + +# @brief Test goal: "Integrationtest for the multi publisher example of iceoryx" +# @pre setup ROS2 launch executables for RouDi (debug mode) and the example processes +# @post check if all applications return exitcode 0 (success) after test run +@pytest.mark.launch_test +def generate_test_description(): + + proc_env = os.environ.copy() + colcon_prefix_path = os.environ.get('COLCON_PREFIX_PATH', '') + + roudi_executable = os.path.join( + colcon_prefix_path, + 'iceoryx_posh/bin/', + 'iox-roudi' + ) + roudi_process = launch.actions.ExecuteProcess( + cmd=[roudi_executable, '-l', 'debug'], + env=proc_env, output='screen', + sigterm_timeout='20') + + multi_publisher_executable = os.path.join( + colcon_prefix_path, + 'example_multi_publisher/bin/', + 'iox-multi-publisher' + ) + multi_publisher_process = launch.actions.ExecuteProcess( + cmd=[multi_publisher_executable], + env=proc_env, output='screen') + + multi_subscriber_executable = os.path.join( + colcon_prefix_path, + 'example_multi_publisher/bin/', + 'iox-multi-subscriber' + ) + multi_subscriber_process = launch.actions.ExecuteProcess( + cmd=[multi_subscriber_executable], + env=proc_env, output='screen') + + return launch.LaunchDescription([ + roudi_process, + multi_publisher_process, + multi_subscriber_process, + launch_testing.actions.ReadyToTest() + ]), {'roudi_process': roudi_process, 'multi_publisher_process': multi_publisher_process, 'multi_subscriber_process': multi_subscriber_process} + +# These tests will run concurrently with the dut process. After this test is done, +# the launch system will shut down RouDi + + +class TestMultiPublisherExample(unittest.TestCase): + def test_roudi_ready(self, proc_output): + proc_output.assertWaitFor( + 'RouDi is ready for clients', timeout=45, stream='stdout') + + def test_multi_publisher_data_exchange(self, proc_output): + proc_output.assertWaitFor( + 'Counter Instance sending: id 1 counter 5', timeout=45, stream='stdout') + proc_output.assertWaitFor( + 'Counter Instance sending: id 2 counter 5', timeout=45, stream='stdout') + proc_output.assertWaitFor( + 'Received: id 1 counter 5', timeout=45, stream='stdout') + proc_output.assertWaitFor( + 'Received: id 2 counter 5', timeout=45, stream='stdout') + +# These tests run after shutdown and examine the stdout log + + +@launch_testing.post_shutdown_test() +class TestMultiPublisherExampleExitCodes(unittest.TestCase): + def test_exit_code(self, proc_info): + launch_testing.asserts.assertExitCodes(proc_info) diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_roudi_startup_shutdown.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_roudi_startup_shutdown.py new file mode 100755 index 0000000000..608bdab755 --- /dev/null +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_roudi_startup_shutdown.py @@ -0,0 +1,89 @@ +# Copyright (c) 2021 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os + +import unittest + +import launch +import launch.actions +from launch_ros.substitutions import ExecutableInPackage + +import launch_testing +import launch_testing.actions +from launch_testing.asserts import assertSequentialStdout + +import pytest + +# @brief Test goal: "Integrationtest for testing correct RouDi startup and shutdown" +# @pre setup ROS2 launch executable for RouDi (debug mode) +# @post check if all applications return exitcode 0 (success) after test run +@pytest.mark.launch_test +def generate_test_description(): + + colcon_prefix_path = os.environ.get('COLCON_PREFIX_PATH', '') + proc_env = os.environ.copy() + + roudi_executable = os.path.join( + colcon_prefix_path, + 'iceoryx_posh/bin/', + 'iox-roudi' + ) + roudi_process = launch.actions.ExecuteProcess( + cmd=[roudi_executable, '-l', 'debug'], + env=proc_env, output='screen', + sigterm_timeout='20') + + return launch.LaunchDescription([ + roudi_process, + launch_testing.actions.ReadyToTest() + ]), {'roudi_process': roudi_process} + + +class TestRouDiProcess(unittest.TestCase): + def test_roudi_ready(self, proc_output): + proc_output.assertWaitFor( + 'RouDi is ready for clients', timeout=45, stream='stdout') + + +@launch_testing.post_shutdown_test() +class TestRouDiProcessOutput(unittest.TestCase): + def test_exit_code(self, proc_info): + launch_testing.asserts.assertExitCodes(proc_info) + + def test_full_output(self, proc_output, roudi_process): + with assertSequentialStdout(proc_output, roudi_process) as cm: + cm.assertInStdout('Log level set to:') + cm.assertInStdout('[ Reserving shared memory successful ]') + cm.assertInStdout('Registered memory segment') + cm.assertInStdout('[ Reserving shared memory successful ]') + cm.assertInStdout('Roudi registered payload segment') + cm.assertInStdout('RouDi is ready for clients') + + if os.name != 'nt': + cm.assertInStdout("Joining 'ProcessMgmt' thread...") + cm.assertInStdout("...'ProcessMgmt' thread joined.") + cm.assertInStdout("Joining 'IPC-msg-process' thread...") + cm.assertInStdout("...'IPC-msg-process' thread joined.") + + def test_out_of_order(self, proc_output, roudi_process): + with assertSequentialStdout(proc_output, roudi_process) as cm: + cm.assertInStdout('Log level set to:') + cm.assertInStdout('[ Reserving shared memory successful ]') + cm.assertInStdout('Registered memory segment') + cm.assertInStdout('[ Reserving shared memory successful ]') + cm.assertInStdout('Roudi registered payload segment') + cm.assertInStdout('RouDi is ready for clients') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_singleprocess_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_singleprocess_example.py new file mode 100755 index 0000000000..5b61c9f3fd --- /dev/null +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_singleprocess_example.py @@ -0,0 +1,77 @@ +# Copyright (c) 2021 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os + +import unittest + +import launch +from launch_ros.substitutions import ExecutableInPackage +import launch_testing +import launch_testing.actions +from launch_testing.asserts import assertSequentialStdout + +import pytest + +# @brief Test goal: "Integrationtest for the singleprocess example of iceoryx" +# @pre setup ROS2 launch executable for the example process +# @post check if all applications return exitcode 0 (success) after test run +@pytest.mark.launch_test +def generate_test_description(): + + proc_env = os.environ.copy() + colcon_prefix_path = os.environ.get('COLCON_PREFIX_PATH', '') + + singleprocess_ex_executable = os.path.join( + colcon_prefix_path, + 'example_singleprocess/bin/', + 'single_process' + ) + singleprocess_ex_process = launch.actions.ExecuteProcess( + cmd=[singleprocess_ex_executable], + env=proc_env, output='screen') + + return launch.LaunchDescription([ + singleprocess_ex_process, + launch_testing.actions.ReadyToTest() + ]), {'singleprocess_ex_process': singleprocess_ex_process} + +# These tests will run concurrently with the dut process. After this test is done, +# the launch system will shut down RouDi + + +class TestSingleProcessExample(unittest.TestCase): + def test_roudi_ready(self, proc_output): + proc_output.assertWaitFor( + 'RouDi is ready for clients', timeout=45, stream='stdout') + + def test_singleprocess_data_exchange(self, proc_output): + proc_output.assertWaitFor( + 'Sending '+'\033[32m->\033[m '+'10', strip_ansi_escape_sequences=False, timeout=45, stream='stdout') + proc_output.assertWaitFor( + 'Receiving '+'\033[33m<-\033[m '+'10', strip_ansi_escape_sequences=False, timeout=45, stream='stdout') + + def test_singleprocess_finished(self, proc_output): + proc_output.assertWaitFor( + 'Finished', timeout=45, stream='stdout') + +# These tests run after shutdown and examine the stdout log + + +@launch_testing.post_shutdown_test() +class TestSingleProcessExitCodes(unittest.TestCase): + def test_exit_code(self, proc_info): + launch_testing.asserts.assertExitCodes(proc_info) diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_waitset_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_waitset_example.py new file mode 100755 index 0000000000..e41e80b0e2 --- /dev/null +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_waitset_example.py @@ -0,0 +1,106 @@ +# Copyright (c) 2021 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os + +import unittest + +import launch +from launch_ros.substitutions import ExecutableInPackage +import launch_testing +import launch_testing.actions +from launch_testing.asserts import assertSequentialStdout + +import pytest + +# @brief Test goal: "Integrationtest for the waitset example of iceoryx" +# @pre setup ROS2 launch executable for RouDi (debug mode) the example processes +# @post check if all applications return exitcode 0 (success) after test run +@pytest.mark.launch_test +def generate_test_description(): + + proc_env = os.environ.copy() + colcon_prefix_path = os.environ.get('COLCON_PREFIX_PATH', '') + executable_list = ['iox-ex-waitset-publisher', 'iox-ex-waitset-gateway', + 'iox-ex-waitset-grouping', 'iox-ex-waitset-individual', + 'iox-ex-waitset-sync', 'iox-ex-waitset-trigger'] + process_list = [] + + for exec in executable_list: + tmp_exec = os.path.join( + colcon_prefix_path, + 'example_waitset/bin/', + exec) + tmp_process = launch.actions.ExecuteProcess( + cmd=[tmp_exec], + env=proc_env, output='screen') + process_list.append(tmp_process) + + print("Process list:", process_list) + + roudi_executable = os.path.join( + colcon_prefix_path, + 'iceoryx_posh/bin/', + 'iox-roudi' + ) + roudi_process = launch.actions.ExecuteProcess( + cmd=[roudi_executable, '-l', 'debug'], + env=proc_env, output='screen', + sigterm_timeout='20') + + return launch.LaunchDescription([ + process_list[0], + process_list[1], + process_list[2], + process_list[3], + process_list[4], + roudi_process, + launch_testing.actions.ReadyToTest() + ]), {'iox-ex-waitset-publisher': process_list[0], 'iox-ex-waitset-gateway': process_list[1], + 'iox-ex-waitset-grouping': process_list[2], 'iox-ex-waitset-individual': process_list[3], + 'iox-ex-waitset-sync': process_list[4], 'roudi_process': roudi_process} + + +class TestWaitSetExample(unittest.TestCase): + def test_roudi_ready(self, proc_output): + proc_output.assertWaitFor( + 'RouDi is ready for clients', timeout=45, stream='stdout') + + def test_waitset_publisher(self, proc_output): + proc_output.assertWaitFor( + 'Sending: 10', timeout=45, stream='stdout') + + def test_waitset_grouping(self, proc_output): + proc_output.assertWaitFor( + 'dismiss data', timeout=45, stream='stdout') + proc_output.assertWaitFor( + 'received: 10', timeout=45, stream='stdout') + + def test_waitset_individual(self, proc_output): + proc_output.assertWaitFor( + 'subscriber 2 received something - dont care', timeout=45, stream='stdout') + proc_output.assertWaitFor( + 'subscriber 1 received: 10', timeout=45, stream='stdout') + + def test_waitset_sync(self, proc_output): + proc_output.assertWaitFor( + 'activation callback', timeout=45, stream='stdout') + + +@ launch_testing.post_shutdown_test() +class TestWaitSetExampleExitCodes(unittest.TestCase): + def test_exit_code(self, proc_info): + launch_testing.asserts.assertExitCodes(proc_info) diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_waitset_in_c_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_waitset_in_c_example.py new file mode 100755 index 0000000000..51ff537cef --- /dev/null +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_waitset_in_c_example.py @@ -0,0 +1,106 @@ +# Copyright (c) 2021 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os + +import unittest + +import launch +from launch_ros.substitutions import ExecutableInPackage +import launch_testing +import launch_testing.actions +from launch_testing.asserts import assertSequentialStdout + +import pytest + +# @brief Test goal: "Integrationtest for the waitset in C example of iceoryx" +# @pre setup ROS2 launch executable for RouDi (debug mode) the example processes +# @post check if all applications return exitcode 0 (success) after test run +@pytest.mark.launch_test +def generate_test_description(): + + proc_env = os.environ.copy() + colcon_prefix_path = os.environ.get('COLCON_PREFIX_PATH', '') + executable_list = ['iox-ex-c-waitset-publisher', 'iox-ex-c-waitset-gateway', + 'iox-ex-c-waitset-grouping', 'iox-ex-c-waitset-individual', + 'iox-ex-c-waitset-sync'] + process_list = [] + + for exec in executable_list: + tmp_exec = os.path.join( + colcon_prefix_path, + 'example_waitset_in_c/bin/', + exec) + tmp_process = launch.actions.ExecuteProcess( + cmd=[tmp_exec], + env=proc_env, output='screen') + process_list.append(tmp_process) + + print("Process list:", process_list) + + roudi_executable = os.path.join( + colcon_prefix_path, + 'iceoryx_posh/bin/', + 'iox-roudi' + ) + roudi_process = launch.actions.ExecuteProcess( + cmd=[roudi_executable, '-l', 'debug'], + env=proc_env, output='screen', + sigterm_timeout='20') + + return launch.LaunchDescription([ + process_list[0], + process_list[1], + process_list[2], + process_list[3], + process_list[4], + roudi_process, + launch_testing.actions.ReadyToTest() + ]), {'iox-ex-c-waitset-sync': process_list[4], 'iox-ex-c-waitset-gateway': process_list[1], + 'iox-ex-c-waitset-grouping': process_list[2], 'iox-ex-c-waitset-individual': process_list[3], + 'iox-ex-c-waitset-publisher': process_list[0], 'roudi_process': roudi_process} + + +class TestWaitSetInCExample(unittest.TestCase): + def test_roudi_ready(self, proc_output): + proc_output.assertWaitFor( + 'RouDi is ready for clients', timeout=45, stream='stdout') + + def test_waitset_in_c_publisher(self, proc_output): + proc_output.assertWaitFor( + 'Sending: 10', timeout=45, stream='stdout') + + def test_waitset_in_c_grouping(self, proc_output): + proc_output.assertWaitFor( + 'dismiss data', timeout=45, stream='stdout') + proc_output.assertWaitFor( + 'received: 10', timeout=45, stream='stdout') + + def test_waitset_in_c_individual(self, proc_output): + proc_output.assertWaitFor( + 'subscriber 2 received something - dont care', timeout=45, stream='stdout') + proc_output.assertWaitFor( + 'subscriber 1 received: 10', timeout=45, stream='stdout') + + def test_waitset__in_c_sync(self, proc_output): + proc_output.assertWaitFor( + 'activation callback', timeout=45, stream='stdout') + + +@ launch_testing.post_shutdown_test() +class TestWaitSetinCExampleExitCodes(unittest.TestCase): + def test_exit_code(self, proc_info): + launch_testing.asserts.assertExitCodes(proc_info) diff --git a/iceoryx_integrationtest/package.xml b/iceoryx_integrationtest/package.xml new file mode 100644 index 0000000000..8d4cda7d0d --- /dev/null +++ b/iceoryx_integrationtest/package.xml @@ -0,0 +1,18 @@ + + + + iceoryx_integrationtest + 0.1.0 + Iceoryx Software Integrationtest + Dietrich Krönke + Apache 2.0 + + ament_cmake + + ros_testing + ros_testing + + + ament_cmake + + diff --git a/iceoryx_meta/CMakeLists.txt b/iceoryx_meta/CMakeLists.txt index 4ba927468f..33d4bc0e2f 100644 --- a/iceoryx_meta/CMakeLists.txt +++ b/iceoryx_meta/CMakeLists.txt @@ -60,6 +60,7 @@ endif() if(EXAMPLES) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../iceoryx_examples/icedelivery ${CMAKE_BINARY_DIR}/iceoryx_examples/icedelivery) if(BINDING_C) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../iceoryx_examples/callbacks_in_c ${CMAKE_BINARY_DIR}/iceoryx_examples/callbacks_in_c) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../iceoryx_examples/icedelivery_in_c ${CMAKE_BINARY_DIR}/iceoryx_examples/icedelivery_in_c) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../iceoryx_examples/waitset_in_c ${CMAKE_BINARY_DIR}/iceoryx_examples/waitset_in_c) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../iceoryx_examples/iceperf ${CMAKE_BINARY_DIR}/iceoryx_examples/iceperf) diff --git a/iceoryx_meta/build_options.cmake b/iceoryx_meta/build_options.cmake index 62950e5b61..58e1091d67 100644 --- a/iceoryx_meta/build_options.cmake +++ b/iceoryx_meta/build_options.cmake @@ -63,6 +63,7 @@ endif() message("") message(" Configured Options") message(" CMAKE_BUILD_TYPE.....................: " ${CMAKE_BUILD_TYPE}) +message(" CMAKE_TOOLCHAIN_FILE.................: " ${CMAKE_TOOLCHAIN_FILE}) message(" TOML_CONFIG..........................: " ${TOML_CONFIG}) message(" BUILD_ALL............................: " ${BUILD_ALL}) message(" BUILD_STRICT.........................: " ${BUILD_STRICT}) diff --git a/iceoryx_posh/CMakeLists.txt b/iceoryx_posh/CMakeLists.txt index cfa64d3aae..682ec38fa9 100644 --- a/iceoryx_posh/CMakeLists.txt +++ b/iceoryx_posh/CMakeLists.txt @@ -99,12 +99,9 @@ add_library(iceoryx_posh source/popo/ports/server_port_roudi.cpp source/popo/ports/server_port_user.cpp source/popo/building_blocks/chunk_queue_types.cpp + source/popo/building_blocks/condition_listener.cpp + source/popo/building_blocks/condition_notifier.cpp source/popo/building_blocks/condition_variable_data.cpp - source/popo/building_blocks/condition_variable_signaler.cpp - source/popo/building_blocks/condition_variable_waiter.cpp - source/popo/building_blocks/event_variable_data.cpp - source/popo/building_blocks/event_listener.cpp - source/popo/building_blocks/event_notifier.cpp source/popo/building_blocks/locking_policy.cpp source/popo/building_blocks/typed_unique_id.cpp source/popo/listener.cpp diff --git a/iceoryx_posh/include/iceoryx_posh/iceoryx_posh_types.hpp b/iceoryx_posh/include/iceoryx_posh/iceoryx_posh_types.hpp index f9848f0068..40279abfce 100644 --- a/iceoryx_posh/include/iceoryx_posh/iceoryx_posh_types.hpp +++ b/iceoryx_posh/include/iceoryx_posh/iceoryx_posh_types.hpp @@ -101,10 +101,15 @@ constexpr uint32_t MAX_RESPONSES_ALLOCATED_SIMULTANEOUSLY = MAX_REQUESTS_PROCESS constexpr uint32_t MAX_REQUEST_QUEUE_CAPACITY = 1024; // Waitset constexpr uint32_t MAX_NUMBER_OF_CONDITION_VARIABLES = 1024U; +constexpr uint32_t MAX_NUMBER_OF_NOTIFIERS_PER_CONDITION_VARIABLE = 128U; constexpr uint32_t MAX_NUMBER_OF_EVENTS_PER_WAITSET = 128U; +static_assert(MAX_NUMBER_OF_EVENTS_PER_WAITSET <= MAX_NUMBER_OF_NOTIFIERS_PER_CONDITION_VARIABLE, + "The WaitSet capacity is restricted by the maximum amount of notifiers per condition variable."); // Listener constexpr uint8_t MAX_NUMBER_OF_EVENT_VARIABLES = 128U; constexpr uint8_t MAX_NUMBER_OF_EVENTS_PER_LISTENER = 128U; +static_assert(MAX_NUMBER_OF_EVENTS_PER_LISTENER <= MAX_NUMBER_OF_NOTIFIERS_PER_CONDITION_VARIABLE, + "The Listener capacity is restricted by the maximum amount of notifiers per condition variable."); //--------- Communication Resources End--------------------- constexpr uint32_t MAX_APPLICATION_CAPRO_FIFO_SIZE = 128U; diff --git a/iceoryx_posh/include/iceoryx_posh/internal/mepoo/mepoo_segment.inl b/iceoryx_posh/include/iceoryx_posh/internal/mepoo/mepoo_segment.inl index 2d9b025a92..3941ebfa49 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/mepoo/mepoo_segment.inl +++ b/iceoryx_posh/include/iceoryx_posh/internal/mepoo/mepoo_segment.inl @@ -78,8 +78,8 @@ inline SharedMemoryObjectType MePooSegment(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) .and_then([this](auto& sharedMemoryObject) { - setSegmentId(iox::rp::BaseRelativePointer::registerPtr(sharedMemoryObject.getBaseAddress(), - sharedMemoryObject.getSizeInBytes())); + this->setSegmentId(iox::rp::BaseRelativePointer::registerPtr(sharedMemoryObject.getBaseAddress(), + sharedMemoryObject.getSizeInBytes())); LogDebug() << "Roudi registered payload segment " << iox::log::HexFormat(reinterpret_cast(sharedMemoryObject.getBaseAddress())) diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/base_subscriber.inl b/iceoryx_posh/include/iceoryx_posh/internal/popo/base_subscriber.inl index b5ca890f78..61592d6bc5 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/base_subscriber.inl +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/base_subscriber.inl @@ -113,15 +113,7 @@ inline void BaseSubscriber::enableEvent(iox::popo::TriggerHandle&& trigg { m_trigger = std::move(triggerHandle); - if (m_trigger.doesContainEventVariable()) - { - m_port.setEventVariable(*reinterpret_cast(m_trigger.getConditionVariableData()), - m_trigger.getUniqueId()); - } - else - { - m_port.setConditionVariable(m_trigger.getConditionVariableData()); - } + m_port.setConditionVariable(*m_trigger.getConditionVariableData(), m_trigger.getUniqueId()); } diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_data.hpp b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_data.hpp index 5f2321a4a2..d99bb10989 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_data.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_data.hpp @@ -20,6 +20,7 @@ #include "iceoryx_posh/iceoryx_posh_types.hpp" #include "iceoryx_posh/internal/mepoo/shared_pointer.hpp" #include "iceoryx_posh/internal/popo/building_blocks/chunk_queue_types.hpp" +#include "iceoryx_posh/internal/popo/building_blocks/condition_notifier.hpp" #include "iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp" #include "iceoryx_utils/cxx/variant_queue.hpp" #include "iceoryx_utils/internal/relocatable_pointer/relative_ptr.hpp" @@ -42,8 +43,8 @@ struct ChunkQueueData : public LockingPolicy cxx::VariantQueue m_queue; std::atomic_bool m_queueHasOverflown{false}; - rp::RelativePointer m_conditionVariableDataPtr{nullptr}; - cxx::optional m_eventVariableIndex; + rp::RelativePointer m_conditionVariableDataPtr; + cxx::optional m_conditionVariableNotificationIndex; }; } // namespace popo diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_popper.hpp b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_popper.hpp index 024a39329b..bd430cf25a 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_popper.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_popper.hpp @@ -20,7 +20,7 @@ #include "iceoryx_posh/internal/mepoo/shared_chunk.hpp" #include "iceoryx_posh/internal/popo/building_blocks/chunk_queue_data.hpp" #include "iceoryx_posh/internal/popo/building_blocks/chunk_queue_types.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/event_variable_data.hpp" +#include "iceoryx_posh/internal/popo/building_blocks/condition_notifier.hpp" #include "iceoryx_utils/cxx/helplets.hpp" #include "iceoryx_utils/cxx/optional.hpp" @@ -83,11 +83,8 @@ class ChunkQueuePopper /// @brief Attaches a condition variable /// @param[in] ConditionVariableDataPtr, pointer to an condition variable data object - void setConditionVariable(cxx::not_null conditionVariableDataPtr) noexcept; - - /// @todo iox-#350 remove once ConditionVariable and EventVariable are combined - // unset is done with unsetConditionVariable - void setEventVariable(EventVariableData& eventVariableDataPtr, const uint64_t eventId) noexcept; + void setConditionVariable(ConditionVariableData& conditionVariableDataRef, + const uint64_t notificationIndex) noexcept; /// @brief Detaches a condition variable void unsetConditionVariable() noexcept; diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_popper.inl b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_popper.inl index fb4fffdf5b..972bc4ac87 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_popper.inl +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_popper.inl @@ -119,28 +119,22 @@ inline void ChunkQueuePopper::clear() noexcept } template -inline void ChunkQueuePopper::setConditionVariable( - cxx::not_null conditionVariableDataPtr) noexcept +inline void ChunkQueuePopper::setConditionVariable(ConditionVariableData& conditionVariableDataRef, + const uint64_t notificationIndex) noexcept { typename MemberType_t::LockGuard_t lock(*getMembers()); - getMembers()->m_conditionVariableDataPtr = conditionVariableDataPtr; -} - -template -inline void ChunkQueuePopper::setEventVariable(EventVariableData& eventVariableDataPtr, - const uint64_t eventId) noexcept -{ - getMembers()->m_conditionVariableDataPtr = &eventVariableDataPtr; - getMembers()->m_eventVariableIndex.emplace(eventId); + getMembers()->m_conditionVariableDataPtr = &conditionVariableDataRef; + getMembers()->m_conditionVariableNotificationIndex.emplace(notificationIndex); } template inline void ChunkQueuePopper::unsetConditionVariable() noexcept { typename MemberType_t::LockGuard_t lock(*getMembers()); + getMembers()->m_conditionVariableDataPtr = nullptr; - getMembers()->m_eventVariableIndex.reset(); + getMembers()->m_conditionVariableNotificationIndex.reset(); } template diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_pusher.hpp b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_pusher.hpp index 7540d122a4..74780f8ea9 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_pusher.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_pusher.hpp @@ -20,8 +20,7 @@ #include "iceoryx_posh/internal/mepoo/shared_chunk.hpp" #include "iceoryx_posh/internal/popo/building_blocks/chunk_queue_data.hpp" #include "iceoryx_posh/internal/popo/building_blocks/chunk_queue_types.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/condition_variable_signaler.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/event_notifier.hpp" +#include "iceoryx_posh/internal/popo/building_blocks/condition_notifier.hpp" #include "iceoryx_utils/cxx/expected.hpp" #include "iceoryx_utils/cxx/helplets.hpp" diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_pusher.inl b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_pusher.inl index 5e76c5869b..8ae124c1a0 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_pusher.inl +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_queue_pusher.inl @@ -68,17 +68,9 @@ inline void ChunkQueuePusher::push(mepoo::SharedChunk chunk) typename MemberType_t::LockGuard_t lock(*getMembers()); if (getMembers()->m_conditionVariableDataPtr) { - if (getMembers()->m_eventVariableIndex) - { - EventNotifier(*reinterpret_cast(getMembers()->m_conditionVariableDataPtr.get()), - *getMembers()->m_eventVariableIndex) - .notify(); - } - else - { - ConditionVariableSignaler condVarSignaler(getMembers()->m_conditionVariableDataPtr.get()); - condVarSignaler.notifyOne(); - } + ConditionNotifier(*getMembers()->m_conditionVariableDataPtr.get(), + *getMembers()->m_conditionVariableNotificationIndex) + .notify(); } } } diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_listener.hpp b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_listener.hpp new file mode 100644 index 0000000000..d58b4d7c21 --- /dev/null +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_listener.hpp @@ -0,0 +1,86 @@ +// Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2021 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +#ifndef IOX_POSH_POPO_BUILDING_BLOCKS_CONDITION_LISTENER_HPP +#define IOX_POSH_POPO_BUILDING_BLOCKS_CONDITION_LISTENER_HPP + +#include "iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp" +#include "iceoryx_posh/mepoo/memory_info.hpp" +#include "iceoryx_utils/cxx/helplets.hpp" + +namespace iox +{ +namespace popo +{ +/// @brief ConditionListener allows one to wait using a shared memory condition variable +class ConditionListener +{ + public: + using NotificationVector_t = cxx::vector, + MAX_NUMBER_OF_NOTIFIERS_PER_CONDITION_VARIABLE>; + + explicit ConditionListener(ConditionVariableData& condVarData) noexcept; + ~ConditionListener() noexcept = default; + ConditionListener(const ConditionListener& rhs) = delete; + ConditionListener(ConditionListener&& rhs) noexcept = delete; + ConditionListener& operator=(const ConditionListener& rhs) = delete; + ConditionListener& operator=(ConditionListener&& rhs) noexcept = delete; + + /// @brief Resets the underlying semaphore to zero. + void resetSemaphore() noexcept; + + /// @brief Waits until notify is called on the ConditionNotifier or time has run out + /// @param[in] timeToWait, time to wait until the function returns + /// @return False if timeout occured, true if no timeout occured + bool timedWait(const units::Duration timeToWait) noexcept; + + /// @brief Waits until notify is called on the ConditionNotifier + void wait() noexcept; + + /// @brief Was the ConditionListener notified by a ConditionNotifier? + /// @return true if it was notified otherwise false + bool wasNotified() const noexcept; + + /// @brief Used in classes to signal a thread which waits in wait() to return + /// and stop working. Destroy will send an empty notification to wait() and + /// after this call wait() turns into a non blocking call which always + /// returns an empty vector. + void destroy() noexcept; + + /// @brief returns vector of indices of active notifications; blocking if ConditionVariableData was + /// not notified unless destroy() was called before. The indices of active notifications are + /// never empty unless destroy() was called, then it's always empty. + /// + /// @return vector of active notifications + NotificationVector_t waitForNotifications() noexcept; + + + protected: + const ConditionVariableData* getMembers() const noexcept; + ConditionVariableData* getMembers() noexcept; + + private: + void reset(const uint64_t index) noexcept; + + private: + ConditionVariableData* m_condVarDataPtr{nullptr}; + std::atomic_bool m_toBeDestroyed{false}; +}; + +} // namespace popo +} // namespace iox + +#endif // IOX_POSH_POPO_BUILDING_BLOCKS_CONDITION_VARIABLE_WAITER_HPP diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_variable_signaler.hpp b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_notifier.hpp similarity index 57% rename from iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_variable_signaler.hpp rename to iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_notifier.hpp index 242298f53a..a14bfc59ab 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_variable_signaler.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_notifier.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2021 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. @@ -13,8 +14,8 @@ // limitations under the License. // // SPDX-License-Identifier: Apache-2.0 -#ifndef IOX_POSH_POPO_BUILDING_BLOCKS_CONDITION_VARIABLE_SIGNALER_HPP -#define IOX_POSH_POPO_BUILDING_BLOCKS_CONDITION_VARIABLE_SIGNALER_HPP +#ifndef IOX_POSH_POPO_BUILDING_BLOCKS_CONDITION_NOTIFIER_HPP +#define IOX_POSH_POPO_BUILDING_BLOCKS_CONDITION_NOTIFIER_HPP #include "iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp" #include "iceoryx_utils/cxx/helplets.hpp" @@ -23,19 +24,22 @@ namespace iox { namespace popo { -/// @brief ConditionVariableSignaler can notifiy waiting threads and processes using a shared memory condition variable -class ConditionVariableSignaler +/// @brief ConditionNotifier can notifiy waiting threads and processes using a shared memory condition variable +class ConditionNotifier { public: - explicit ConditionVariableSignaler(cxx::not_null condVarDataPtr) noexcept; - virtual ~ConditionVariableSignaler() noexcept = default; - ConditionVariableSignaler(const ConditionVariableSignaler& rhs) = delete; - ConditionVariableSignaler(ConditionVariableSignaler&& rhs) noexcept = delete; - ConditionVariableSignaler& operator=(const ConditionVariableSignaler& rhs) = delete; - ConditionVariableSignaler& operator=(ConditionVariableSignaler&& rhs) noexcept = delete; + static constexpr uint64_t INVALID_NOTIFICATION_INDEX = std::numeric_limits::max(); + + ConditionNotifier(ConditionVariableData& condVarDataRef, const uint64_t index) noexcept; + + ConditionNotifier(const ConditionNotifier& rhs) = delete; + ConditionNotifier(ConditionNotifier&& rhs) noexcept = delete; + ConditionNotifier& operator=(const ConditionNotifier& rhs) = delete; + ConditionNotifier& operator=(ConditionNotifier&& rhs) noexcept = delete; + ~ConditionNotifier() noexcept = default; /// @brief If threads are waiting on the condition variable, this call unblocks one of the waiting threads - void notifyOne() noexcept; + void notify() noexcept; protected: const ConditionVariableData* getMembers() const noexcept; @@ -43,6 +47,7 @@ class ConditionVariableSignaler private: ConditionVariableData* m_condVarDataPtr{nullptr}; + uint64_t m_notificationIndex = INVALID_NOTIFICATION_INDEX; }; } // namespace popo diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp index 6481f25a69..742df47517 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp @@ -29,12 +29,14 @@ namespace popo { struct ConditionVariableData { - ConditionVariableData() noexcept = default; + ConditionVariableData() noexcept; ConditionVariableData(const ProcessName_t& process) noexcept; + ConditionVariableData(const ConditionVariableData& rhs) = delete; ConditionVariableData(ConditionVariableData&& rhs) = delete; ConditionVariableData& operator=(const ConditionVariableData& rhs) = delete; ConditionVariableData& operator=(ConditionVariableData&& rhs) = delete; + ~ConditionVariableData() = default; posix::Semaphore m_semaphore = std::move(posix::Semaphore::create(posix::CreateUnnamedSharedMemorySemaphore, 0u) @@ -47,6 +49,7 @@ struct ConditionVariableData ProcessName_t m_process; std::atomic_bool m_toBeDestroyed{false}; + std::atomic_bool m_activeNotifications[MAX_NUMBER_OF_NOTIFIERS_PER_CONDITION_VARIABLE]; }; } // namespace popo diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_variable_waiter.hpp b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_variable_waiter.hpp deleted file mode 100644 index 4e3947b7df..0000000000 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_variable_waiter.hpp +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2020 by Robert Bosch GmbH. 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. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 -#ifndef IOX_POSH_POPO_BUILDING_BLOCKS_CONDITION_VARIABLE_WAITER_HPP -#define IOX_POSH_POPO_BUILDING_BLOCKS_CONDITION_VARIABLE_WAITER_HPP - -#include "iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp" -#include "iceoryx_posh/mepoo/memory_info.hpp" -#include "iceoryx_utils/cxx/helplets.hpp" - -namespace iox -{ -namespace popo -{ -/// @brief ConditionVariableWaiter allows one to wait using a shared memory condition variable -class ConditionVariableWaiter -{ - public: - explicit ConditionVariableWaiter(cxx::not_null condVarDataPtr) noexcept; - virtual ~ConditionVariableWaiter() noexcept = default; - ConditionVariableWaiter(const ConditionVariableWaiter& rhs) = delete; - ConditionVariableWaiter(ConditionVariableWaiter&& rhs) noexcept = delete; - ConditionVariableWaiter& operator=(const ConditionVariableWaiter& rhs) = delete; - ConditionVariableWaiter& operator=(ConditionVariableWaiter&& rhs) noexcept = delete; - - /// @brief Reinitialises the condition variable - void reset() noexcept; - /// @brief Waits until notify is called on the ConditionVariableSignaler or time has run out - /// @param[in] timeToWait, time to wait until the function returns - /// @return False if timeout occured, true if no timeout occured - bool timedWait(const units::Duration timeToWait) noexcept; - /// @brief Waits until notify is called on the ConditionVariableSignaler - void wait() noexcept; - - /// @brief Was the ConditionVariableWaiter notified by a ConditionVariableSignaler? - /// @return true if it was notified otherwise false - bool wasNotified() const noexcept; - - protected: - const ConditionVariableData* getMembers() const noexcept; - ConditionVariableData* getMembers() noexcept; - - private: - ConditionVariableData* m_condVarDataPtr{nullptr}; -}; - -} // namespace popo -} // namespace iox - -#endif // IOX_POSH_POPO_BUILDING_BLOCKS_CONDITION_VARIABLE_WAITER_HPP diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/event_listener.hpp b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/event_listener.hpp deleted file mode 100644 index 8ef412f5b9..0000000000 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/event_listener.hpp +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 2021 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. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - - -#ifndef IOX_POSH_POPO_BUILDING_BLOCKS_EVENT_LISTENER_HPP -#define IOX_POSH_POPO_BUILDING_BLOCKS_EVENT_LISTENER_HPP - -#include "iceoryx_posh/internal/popo/building_blocks/event_variable_data.hpp" -#include "iceoryx_utils/cxx/helplets.hpp" - -namespace iox -{ -namespace popo -{ -/// @brief An EventListener performs a blocking wait on a shared event variable. -/// When wait returns a list of all the EventNotifier id's which had -/// triggered the EventVariable is returned and the state is reset. -/// -/// @attention Do not use multiple EventListener at the same time for the same EventVariable -class EventListener -{ - public: - using NotificationVector_t = - cxx::vector, MAX_NUMBER_OF_EVENTS_PER_LISTENER>; - - /// @brief creates new EventListener - /// - /// @param[in] dataRef reference to EventVariableData - EventListener(EventVariableData& dataRef) noexcept; - - /// @brief returns vector of indices of active notifications; blocking if EventVariableData was - /// not notified unless destroy() was called before. The indices of active notifications is - /// never empty unless destroy() was called, then it's always empty. - /// - /// @return vector of active notifications - NotificationVector_t wait() noexcept; - - /// @brief Used in classes to signal a thread which waits in wait() to return - /// and stop working. Destroy will send an empty notification to wait() and - /// after this call wait() turns into a non blocking call which always - /// returns an empty vector. - void destroy() noexcept; - - private: - void reset(const uint64_t index) noexcept; - void resetSemaphore() noexcept; - - std::atomic_bool m_toBeDestroyed{false}; - EventVariableData* m_pointerToEventVariableData{nullptr}; -}; -} // namespace popo -} // namespace iox - -#endif diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/event_notifier.hpp b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/event_notifier.hpp deleted file mode 100644 index 45c399bc4a..0000000000 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/event_notifier.hpp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2021 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. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - - -#ifndef IOX_POSH_POPO_BUILDING_BLOCKS_EVENT_NOTIFIER_HPP -#define IOX_POSH_POPO_BUILDING_BLOCKS_EVENT_NOTIFIER_HPP - -#include "iceoryx_posh/internal/popo/building_blocks/event_variable_data.hpp" - -namespace iox -{ -namespace popo -{ -/// @brief An EventNotifier notifies a corresponding EventListener via notify() which is -/// waiting on the same EventVariable. -class EventNotifier -{ - public: - /// @brief creates new EventNotifier - /// - /// @param[in] dataRef reference to EventVariableData - /// @param[in] index index which identifies EventNotifier uniquely. The user has to ensure the uniqueness and the - /// index has to be in the range of [0, MAX_NUMBER_OF_EVENTS_PER_LISTENER) - EventNotifier(EventVariableData& dataRef, const uint64_t index) noexcept; - - /// @brief wakes up the corresponding EventListener which is waiting in wait() - void notify() noexcept; - - private: - EventVariableData* m_pointerToEventVariableData{nullptr}; - uint64_t m_notificationIndex{0U}; -}; -} // namespace popo -} // namespace iox - -#endif - diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/event_variable_data.hpp b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/event_variable_data.hpp deleted file mode 100644 index dbb4c2826d..0000000000 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/event_variable_data.hpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2021 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. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - - -#ifndef IOX_POSH_POPO_BUILDING_BLOCKS_EVENT_VARIABLE_DATA_HPP -#define IOX_POSH_POPO_BUILDING_BLOCKS_EVENT_VARIABLE_DATA_HPP - -#include "iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp" - -namespace iox -{ -namespace popo -{ -/// @brief Shared member variable structure used by EventListener and EventNotifier -struct EventVariableData : public ConditionVariableData -{ - /// @brief sets all entries of notification array to false - EventVariableData() noexcept; - - /// @brief sets all entries of notification array to false and sets process name - /// - /// @param[in] process name of process - EventVariableData(const ProcessName_t& process) noexcept; - - std::atomic_bool m_activeNotifications[MAX_NUMBER_OF_EVENTS_PER_LISTENER]; -}; -} // namespace popo -} // namespace iox - -#endif - diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/listener.inl b/iceoryx_posh/include/iceoryx_posh/internal/popo/listener.inl index 74e7a37ad2..d599255f58 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/listener.inl +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/listener.inl @@ -39,8 +39,8 @@ inline cxx::expected Listener::attachEvent(T& eventOrigin, Callba internal::translateAndCallTypelessCallback, EventAttorney::getInvalidateTriggerMethod(eventOrigin)) .and_then([&](auto& eventId) { - EventAttorney::enableEvent(eventOrigin, - TriggerHandle(*m_eventVariable, {*this, &Listener::removeTrigger}, eventId)); + EventAttorney::enableEvent( + eventOrigin, TriggerHandle(*m_conditionVariableData, {*this, &Listener::removeTrigger}, eventId)); }); } @@ -56,7 +56,9 @@ Listener::attachEvent(T& eventOrigin, const EventType eventType, CallbackRef_t& hasTriggeredCallback, const cxx::MethodCallback& resetCallback, const uint64_t eventId, - const Callback callback) noexcept + const Callback callback, + const uint64_t uniqueId) noexcept : m_eventInfo(eventOrigin, eventId, callback) , m_hasTriggeredCallback(hasTriggeredCallback) , m_resetCallback(resetCallback) - , m_uniqueId(uniqueIdCounter.fetch_add(1U)) + , m_uniqueId(uniqueId) { } diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/wait_set.inl b/iceoryx_posh/include/iceoryx_posh/internal/popo/wait_set.inl index 16d8eeb123..51d3dd8e7e 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/wait_set.inl +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/wait_set.inl @@ -23,15 +23,19 @@ namespace popo { template inline WaitSet::WaitSet() noexcept - : WaitSet(runtime::PoshRuntime::getInstance().getMiddlewareConditionVariable()) + : WaitSet(*runtime::PoshRuntime::getInstance().getMiddlewareConditionVariable()) { } template -inline WaitSet::WaitSet(cxx::not_null condVarDataPtr) noexcept - : m_conditionVariableDataPtr(condVarDataPtr) - , m_conditionVariableWaiter(m_conditionVariableDataPtr) +inline WaitSet::WaitSet(ConditionVariableData& condVarData) noexcept + : m_conditionVariableDataPtr(&condVarData) + , m_conditionListener(condVarData) { + for (uint64_t i = 0U; i < Capacity; ++i) + { + m_indexRepository.push(i); + } } template @@ -55,7 +59,7 @@ WaitSet::attachEventImpl(T& eventOrigin, } Trigger possibleLogicallyEqualTrigger( - &eventOrigin, hasTriggeredCallback, cxx::MethodCallback(), eventId, Trigger::Callback()); + &eventOrigin, hasTriggeredCallback, cxx::MethodCallback(), eventId, Trigger::Callback(), 0U); for (auto& currentTrigger : m_triggerList) { @@ -66,14 +70,17 @@ WaitSet::attachEventImpl(T& eventOrigin, } cxx::MethodCallback invalidationCallback = EventAttorney::getInvalidateTriggerMethod(eventOrigin); - - if (!m_triggerList.push_back( - Trigger{&eventOrigin, hasTriggeredCallback, invalidationCallback, eventId, eventCallback})) + auto index = m_indexRepository.pop(); + if (!index) { return cxx::error(WaitSetError::WAIT_SET_FULL); } - return cxx::success(m_triggerList.back().getUniqueId()); + + cxx::Ensures(m_triggerList.push_back( + Trigger{&eventOrigin, hasTriggeredCallback, invalidationCallback, eventId, eventCallback, *index})); + + return cxx::success(*index); } template @@ -140,6 +147,7 @@ inline void WaitSet::removeTrigger(const uint64_t uniqueTriggerId) noe { currentTrigger->invalidate(); m_triggerList.erase(currentTrigger); + cxx::Ensures(m_indexRepository.push(uniqueTriggerId)); return; } } @@ -159,14 +167,14 @@ inline void WaitSet::removeAllTriggers() noexcept template inline typename WaitSet::EventInfoVector WaitSet::timedWait(const units::Duration timeout) noexcept { - return waitAndReturnTriggeredTriggers([this, timeout] { return !m_conditionVariableWaiter.timedWait(timeout); }); + return waitAndReturnTriggeredTriggers([this, timeout] { return !m_conditionListener.timedWait(timeout); }); } template inline typename WaitSet::EventInfoVector WaitSet::wait() noexcept { return waitAndReturnTriggeredTriggers([this] { - m_conditionVariableWaiter.wait(); + m_conditionListener.wait(); return false; }); } @@ -198,7 +206,7 @@ WaitSet::waitAndReturnTriggeredTriggers(const WaitFunction& wait) noex WaitSet::EventInfoVector triggers; /// Inbetween here and last wait someone could have set the trigger to true, hence reset it. - m_conditionVariableWaiter.reset(); + m_conditionListener.resetSemaphore(); triggers = createVectorWithTriggeredTriggers(); // It is possible that after the reset call and before the createVectorWithTriggeredTriggers call diff --git a/iceoryx_posh/include/iceoryx_posh/internal/roudi/port_manager.hpp b/iceoryx_posh/include/iceoryx_posh/internal/roudi/port_manager.hpp index 0e316b1ee1..6f4909432c 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/roudi/port_manager.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/roudi/port_manager.hpp @@ -21,7 +21,6 @@ #include "iceoryx_posh/iceoryx_posh_types.hpp" #include "iceoryx_posh/internal/capro/capro_message.hpp" #include "iceoryx_posh/internal/mepoo/memory_manager.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/event_variable_data.hpp" #include "iceoryx_posh/internal/popo/ports/application_port.hpp" #include "iceoryx_posh/internal/popo/ports/interface_port.hpp" #include "iceoryx_posh/internal/popo/ports/publisher_port_roudi.hpp" @@ -90,9 +89,6 @@ class PortManager cxx::expected acquireConditionVariableData(const ProcessName_t& process) noexcept; - cxx::expected - acquireEventVariableData(const ProcessName_t& process) noexcept; - void deletePortsOfProcess(const ProcessName_t& processName) noexcept; const std::atomic* serviceRegistryChangeCounter() noexcept; @@ -119,8 +115,6 @@ class PortManager void handleConditionVariables() noexcept; - void handleEventVariables() noexcept; - bool sendToAllMatchingPublisherPorts(const capro::CaproMessage& message, SubscriberPortType& subscriberSource) noexcept; diff --git a/iceoryx_posh/include/iceoryx_posh/internal/roudi/port_pool_data.hpp b/iceoryx_posh/include/iceoryx_posh/internal/roudi/port_pool_data.hpp index b921280d87..985d67cd4d 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/roudi/port_pool_data.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/roudi/port_pool_data.hpp @@ -19,7 +19,6 @@ #include "iceoryx_posh/iceoryx_posh_types.hpp" #include "iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/event_variable_data.hpp" #include "iceoryx_posh/internal/popo/ports/application_port.hpp" #include "iceoryx_posh/internal/popo/ports/interface_port.hpp" #include "iceoryx_posh/internal/popo/ports/publisher_port_data.hpp" @@ -58,7 +57,6 @@ struct PortPoolData FixedPositionContainer m_applicationPortMembers; FixedPositionContainer m_nodeMembers; FixedPositionContainer m_conditionVariableMembers; - FixedPositionContainer m_eventVariableMembers; FixedPositionContainer m_publisherPortMembers; FixedPositionContainer m_subscriberPortMembers; diff --git a/iceoryx_posh/include/iceoryx_posh/internal/roudi/roudi_process.hpp b/iceoryx_posh/include/iceoryx_posh/internal/roudi/roudi_process.hpp index f22ba8e67d..26698eda87 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/roudi/roudi_process.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/roudi/roudi_process.hpp @@ -158,8 +158,6 @@ class ProcessManager : public ProcessManagerInterface void addConditionVariableForProcess(const ProcessName_t& processName) noexcept; - void addEventVariableForProcess(const ProcessName_t& processName) noexcept; - void initIntrospection(ProcessIntrospectionType* processIntrospection) noexcept; void run() noexcept; diff --git a/iceoryx_posh/include/iceoryx_posh/internal/runtime/ipc_interface_base.hpp b/iceoryx_posh/include/iceoryx_posh/internal/runtime/ipc_interface_base.hpp index 36d9040ea7..00a2e74a2e 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/runtime/ipc_interface_base.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/runtime/ipc_interface_base.hpp @@ -61,8 +61,6 @@ enum class IpcMessageType : int32_t CREATE_APPLICATION_ACK, CREATE_CONDITION_VARIABLE, CREATE_CONDITION_VARIABLE_ACK, - CREATE_EVENT_VARIABLE, - CREATE_EVENT_VARIABLE_ACK, CREATE_NODE, CREATE_NODE_ACK, FIND_SERVICE, diff --git a/iceoryx_posh/include/iceoryx_posh/popo/listener.hpp b/iceoryx_posh/include/iceoryx_posh/popo/listener.hpp index 98321c3f89..6f0df77dfc 100644 --- a/iceoryx_posh/include/iceoryx_posh/popo/listener.hpp +++ b/iceoryx_posh/include/iceoryx_posh/popo/listener.hpp @@ -17,8 +17,7 @@ #ifndef IOX_POSH_POPO_LISTENER_HPP #define IOX_POSH_POPO_LISTENER_HPP -#include "iceoryx_posh/internal/popo/building_blocks/event_listener.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/event_variable_data.hpp" +#include "iceoryx_posh/internal/popo/building_blocks/condition_listener.hpp" #include "iceoryx_posh/popo/event_attorney.hpp" #include "iceoryx_posh/popo/trigger_handle.hpp" #include "iceoryx_utils/cxx/expected.hpp" @@ -127,7 +126,7 @@ class Listener uint64_t size() const noexcept; protected: - Listener(EventVariableData* eventVariable) noexcept; + Listener(ConditionVariableData& conditionVariableData) noexcept; private: class Event_t; @@ -200,8 +199,8 @@ class Listener std::mutex m_addEventMutex; std::atomic_bool m_wasDtorCalled{false}; - EventVariableData* m_eventVariable = nullptr; - EventListener m_eventListener; + ConditionVariableData* m_conditionVariableData = nullptr; + ConditionListener m_conditionListener; }; } // namespace popo } // namespace iox diff --git a/iceoryx_posh/include/iceoryx_posh/popo/trigger.hpp b/iceoryx_posh/include/iceoryx_posh/popo/trigger.hpp index 13c44a9152..f06832ce3d 100644 --- a/iceoryx_posh/include/iceoryx_posh/popo/trigger.hpp +++ b/iceoryx_posh/include/iceoryx_posh/popo/trigger.hpp @@ -41,9 +41,9 @@ class Trigger template using Callback = EventInfo::Callback; - /// @brief Creates an empty Trigger - Trigger() noexcept = default; - template + Trigger() noexcept = delete; + Trigger(const Trigger&) = delete; + Trigger& operator=(const Trigger&) = delete; /// @brief Creates a Trigger /// @param[in] origin pointer to the class where the signal originates from, if its set to nullptr the Trigger is in @@ -54,14 +54,14 @@ class Trigger /// @param[in] eventId id of the corresponding event /// @param[in] callback function pointer of type void(*)(T * const) to a callback which can be called by the /// trigger. + template Trigger(T* const origin, const cxx::ConstMethodCallback& hasTriggeredCallback, const cxx::MethodCallback& resetCallback, const uint64_t eventId, - const Callback callback) noexcept; + const Callback callback, + const uint64_t uniqueId) noexcept; - Trigger(const Trigger&) = delete; - Trigger& operator=(const Trigger&) = delete; Trigger(Trigger&& rhs) noexcept; Trigger& operator=(Trigger&& rhs) noexcept; @@ -108,8 +108,6 @@ class Trigger cxx::ConstMethodCallback m_hasTriggeredCallback; cxx::MethodCallback m_resetCallback; uint64_t m_uniqueId = INVALID_TRIGGER_ID; - - static std::atomic uniqueIdCounter; // = 0U; }; diff --git a/iceoryx_posh/include/iceoryx_posh/popo/trigger_handle.hpp b/iceoryx_posh/include/iceoryx_posh/popo/trigger_handle.hpp index e6cb47e8c2..7b5248688a 100644 --- a/iceoryx_posh/include/iceoryx_posh/popo/trigger_handle.hpp +++ b/iceoryx_posh/include/iceoryx_posh/popo/trigger_handle.hpp @@ -18,7 +18,6 @@ #define IOX_POSH_POPO_TRIGGER_HANDLE_HPP #include "iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/event_variable_data.hpp" #include "iceoryx_posh/popo/trigger.hpp" #include "iceoryx_utils/cxx/method_callback.hpp" @@ -39,21 +38,13 @@ class TriggerHandle { public: TriggerHandle() = default; - /// @brief Creates a TriggerHandle - /// @param[in] eventVariableDataRef reference to a event variable data struct - /// @param[in] resetCallback callback which will be called when it goes out of scope or reset is called - /// @param[in] uniqueTriggerId the unique trigger id of the Trigger which corresponds to the TriggerHandle. Usually - /// stored in a Notifyable. It is required for the resetCallback - TriggerHandle(EventVariableData& eventVariableDataRef, - const cxx::MethodCallback resetCallback, - const uint64_t uniqueTriggerId) noexcept; /// @brief Creates a TriggerHandle /// @param[in] conditionVariableDataRef reference to a condition variable data struct /// @param[in] resetCallback callback which will be called it goes out of scope or reset is called /// @param[in] uniqueTriggerId the unique trigger id of the Trigger which corresponds to the TriggerHandle. Usually /// stored in a Notifyable. It is required for the resetCallback - TriggerHandle(ConditionVariableData& conditionVariableDataRef, + TriggerHandle(ConditionVariableData& conditionVariableData, const cxx::MethodCallback resetCallback, const uint64_t uniqueTriggerId) noexcept; TriggerHandle(const TriggerHandle&) = delete; @@ -87,14 +78,10 @@ class TriggerHandle /// @brief returns the pointer to the ConditionVariableData ConditionVariableData* getConditionVariableData() noexcept; - /// @brief returns true if it contains an EventVariable, otherwise false - bool doesContainEventVariable() const noexcept; - private: ConditionVariableData* m_conditionVariableDataPtr = nullptr; cxx::MethodCallback m_resetCallback; uint64_t m_uniqueTriggerId = Trigger::INVALID_TRIGGER_ID; - bool m_doesContainEventVariable = false; mutable std::recursive_mutex m_mutex; }; } // namespace popo diff --git a/iceoryx_posh/include/iceoryx_posh/popo/wait_set.hpp b/iceoryx_posh/include/iceoryx_posh/popo/wait_set.hpp index 167517ef11..c2ae2c4c3e 100644 --- a/iceoryx_posh/include/iceoryx_posh/popo/wait_set.hpp +++ b/iceoryx_posh/include/iceoryx_posh/popo/wait_set.hpp @@ -18,15 +18,17 @@ #define IOX_POSH_POPO_WAIT_SET_HPP #include "iceoryx_posh/iceoryx_posh_types.hpp" +#include "iceoryx_posh/internal/popo/building_blocks/condition_listener.hpp" #include "iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/condition_variable_waiter.hpp" #include "iceoryx_posh/popo/event_attorney.hpp" #include "iceoryx_posh/popo/trigger.hpp" #include "iceoryx_posh/popo/trigger_handle.hpp" #include "iceoryx_posh/runtime/posh_runtime.hpp" #include "iceoryx_utils/cxx/function_ref.hpp" +#include "iceoryx_utils/cxx/helplets.hpp" #include "iceoryx_utils/cxx/list.hpp" #include "iceoryx_utils/cxx/method_callback.hpp" +#include "iceoryx_utils/cxx/stack.hpp" #include "iceoryx_utils/cxx/vector.hpp" namespace iox @@ -124,7 +126,7 @@ class WaitSet uint64_t capacity() const noexcept; protected: - explicit WaitSet(cxx::not_null) noexcept; + explicit WaitSet(ConditionVariableData& condVarData) noexcept; private: template @@ -148,7 +150,8 @@ class WaitSet /// needs to be a list since we return pointer to the underlying EventInfo class with wait TriggerList m_triggerList; ConditionVariableData* m_conditionVariableDataPtr{nullptr}; - ConditionVariableWaiter m_conditionVariableWaiter; + ConditionListener m_conditionListener; + cxx::stack m_indexRepository; }; } // namespace popo diff --git a/iceoryx_posh/include/iceoryx_posh/roudi/port_pool.hpp b/iceoryx_posh/include/iceoryx_posh/roudi/port_pool.hpp index a2395c07da..1be5cd9e89 100644 --- a/iceoryx_posh/include/iceoryx_posh/roudi/port_pool.hpp +++ b/iceoryx_posh/include/iceoryx_posh/roudi/port_pool.hpp @@ -19,7 +19,6 @@ #include "iceoryx_posh/iceoryx_posh_types.hpp" #include "iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/event_variable_data.hpp" #include "iceoryx_posh/internal/popo/ports/application_port.hpp" #include "iceoryx_posh/internal/popo/ports/interface_port.hpp" #include "iceoryx_posh/internal/popo/ports/publisher_port_data.hpp" @@ -68,7 +67,6 @@ class PortPool cxx::vector getNodeDataList() noexcept; cxx::vector getConditionVariableDataList() noexcept; - cxx::vector getEventVariableDataList() noexcept; cxx::expected addPublisherPort(const capro::ServiceDescription& serviceDescription, @@ -107,15 +105,12 @@ class PortPool cxx::expected addConditionVariableData(const ProcessName_t& process) noexcept; - cxx::expected addEventVariableData(const ProcessName_t& process) noexcept; - void removePublisherPort(PublisherPortRouDiType::MemberType_t* const portData) noexcept; void removeSubscriberPort(SubscriberPortType::MemberType_t* const portData) noexcept; void removeInterfacePort(popo::InterfacePortData* const portData) noexcept; void removeApplicationPort(popo::ApplicationPortData* const portData) noexcept; void removeNodeData(runtime::NodeData* const nodeData) noexcept; void removeConditionVariableData(popo::ConditionVariableData* const conditionVariableData) noexcept; - void removeEventVariableData(popo::EventVariableData* const eventVariableData) noexcept; std::atomic* serviceRegistryChangeCounter() noexcept; diff --git a/iceoryx_posh/include/iceoryx_posh/runtime/posh_runtime.hpp b/iceoryx_posh/include/iceoryx_posh/runtime/posh_runtime.hpp index bdd82c37e0..b9fa296f59 100644 --- a/iceoryx_posh/include/iceoryx_posh/runtime/posh_runtime.hpp +++ b/iceoryx_posh/include/iceoryx_posh/runtime/posh_runtime.hpp @@ -20,7 +20,6 @@ #include "iceoryx_posh/capro/service_description.hpp" #include "iceoryx_posh/iceoryx_posh_types.hpp" #include "iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/event_variable_data.hpp" #include "iceoryx_posh/internal/popo/ports/application_port.hpp" #include "iceoryx_posh/internal/popo/ports/interface_port.hpp" #include "iceoryx_posh/internal/popo/ports/publisher_port_user.hpp" @@ -133,10 +132,6 @@ class PoshRuntime /// @return pointer to a created condition variable data popo::ConditionVariableData* getMiddlewareConditionVariable() noexcept; - /// @brief request the RouDi daemon to create an event variable - /// @return pointer to a created event variable data - popo::EventVariableData* getMiddlewareEventVariable() noexcept; - /// @brief request the RouDi daemon to create a node /// @param[in] nodeProperty class which contains all properties which the node should have /// @return pointer to the data of the node @@ -200,9 +195,6 @@ class PoshRuntime cxx::expected requestConditionVariableFromRoudi(const IpcMessage& sendBuffer) noexcept; - cxx::expected - requestEventVariableFromRoudi(const IpcMessage& sendBuffer) noexcept; - /// @brief checks the given application name for certain constraints like length or if is empty const ProcessName_t& verifyInstanceName(cxx::optional name) noexcept; diff --git a/iceoryx_posh/source/popo/building_blocks/condition_listener.cpp b/iceoryx_posh/source/popo/building_blocks/condition_listener.cpp new file mode 100644 index 0000000000..d57d4b1710 --- /dev/null +++ b/iceoryx_posh/source/popo/building_blocks/condition_listener.cpp @@ -0,0 +1,148 @@ +// Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2021 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "iceoryx_posh/internal/popo/building_blocks/condition_listener.hpp" +#include "iceoryx_utils/error_handling/error_handling.hpp" + +namespace iox +{ +namespace popo +{ +ConditionListener::ConditionListener(ConditionVariableData& condVarData) noexcept + : m_condVarDataPtr(&condVarData) +{ +} + +void ConditionListener::resetSemaphore() noexcept +{ + // Count the semaphore down to zero + bool hasFatalError = false; + while (!hasFatalError + && getMembers() + ->m_semaphore.tryWait() + .or_else([&](posix::SemaphoreError) { + errorHandler( + Error::kPOPO__CONDITION_LISTENER_SEMAPHORE_CORRUPTED_IN_RESET, nullptr, ErrorLevel::FATAL); + hasFatalError = true; + }) + .value()) + { + } +} + +void ConditionListener::destroy() noexcept +{ + m_toBeDestroyed.store(true, std::memory_order_relaxed); + getMembers()->m_semaphore.post().or_else([](auto) { + errorHandler(Error::kPOPO__CONDITION_LISTENER_SEMAPHORE_CORRUPTED_IN_DESTROY, nullptr, ErrorLevel::FATAL); + }); +} + +bool ConditionListener::wasNotified() const noexcept +{ + auto result = getMembers()->m_semaphore.getValue(); + if (result.has_error()) + { + errorHandler(Error::kPOPO__CONDITION_LISTENER_SEMAPHORE_CORRUPTED_IN_WAS_TRIGGERED, nullptr, ErrorLevel::FATAL); + return false; + } + + return *result != 0; +} + +void ConditionListener::wait() noexcept +{ + if (m_toBeDestroyed.load(std::memory_order_relaxed)) + { + return; + } + + getMembers()->m_semaphore.wait().or_else([](auto) { + errorHandler(Error::kPOPO__CONDITION_LISTENER_SEMAPHORE_CORRUPTED_IN_WAIT, nullptr, ErrorLevel::FATAL); + }); +} + +bool ConditionListener::timedWait(units::Duration timeToWait) noexcept +{ + if (m_toBeDestroyed.load(std::memory_order_relaxed)) + { + return false; + } + + auto continueOnInterrupt{false}; + auto result = getMembers()->m_semaphore.timedWait(timeToWait, continueOnInterrupt); + + if (result.has_error()) + { + errorHandler(Error::kPOPO__CONDITION_LISTENER_SEMAPHORE_CORRUPTED_IN_TIMED_WAIT, nullptr, ErrorLevel::FATAL); + return false; + } + + return *result != posix::SemaphoreWaitState::TIMEOUT; +} + +ConditionListener::NotificationVector_t ConditionListener::waitForNotifications() noexcept +{ + using Type_t = iox::cxx::BestFittingType_t; + NotificationVector_t activeNotifications; + + resetSemaphore(); + while (!m_toBeDestroyed.load(std::memory_order_relaxed)) + { + for (Type_t i = 0U; i < MAX_NUMBER_OF_NOTIFIERS_PER_CONDITION_VARIABLE; i++) + { + if (getMembers()->m_activeNotifications[i].load(std::memory_order_relaxed)) + { + reset(i); + activeNotifications.emplace_back(i); + } + } + if (!activeNotifications.empty()) + { + return activeNotifications; + } + + if (getMembers()->m_semaphore.wait().has_error()) + { + errorHandler(Error::kPOPO__CONDITION_LISTENER_SEMAPHORE_CORRUPTED_IN_WAIT, nullptr, ErrorLevel::FATAL); + break; + } + } + + return activeNotifications; +} + +void ConditionListener::reset(const uint64_t index) noexcept +{ + if (index < MAX_NUMBER_OF_NOTIFIERS_PER_CONDITION_VARIABLE) + { + getMembers()->m_activeNotifications[index].store(false, std::memory_order_relaxed); + } +} + +const ConditionVariableData* ConditionListener::getMembers() const noexcept +{ + return m_condVarDataPtr; +} + +ConditionVariableData* ConditionListener::getMembers() noexcept +{ + return m_condVarDataPtr; +} + +} // namespace popo +} // namespace iox diff --git a/iceoryx_posh/source/popo/building_blocks/condition_notifier.cpp b/iceoryx_posh/source/popo/building_blocks/condition_notifier.cpp new file mode 100644 index 0000000000..9c90523da1 --- /dev/null +++ b/iceoryx_posh/source/popo/building_blocks/condition_notifier.cpp @@ -0,0 +1,57 @@ +// Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2021 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "iceoryx_posh/internal/popo/building_blocks/condition_notifier.hpp" +#include "iceoryx_posh/internal/log/posh_logging.hpp" + +namespace iox +{ +namespace popo +{ +ConditionNotifier::ConditionNotifier(ConditionVariableData& condVarDataRef, const uint64_t index) noexcept + : m_condVarDataPtr(&condVarDataRef) + , m_notificationIndex(index) +{ + if (index >= MAX_NUMBER_OF_NOTIFIERS_PER_CONDITION_VARIABLE) + { + LogFatal() << "The provided index " << index << " is too large. The index has to be in the range of [0, " + << MAX_NUMBER_OF_NOTIFIERS_PER_CONDITION_VARIABLE << "[."; + errorHandler(Error::kPOPO__CONDITION_NOTIFIER_INDEX_TOO_LARGE, nullptr, ErrorLevel::FATAL); + } +} + +void ConditionNotifier::notify() noexcept +{ + if (m_notificationIndex < MAX_NUMBER_OF_NOTIFIERS_PER_CONDITION_VARIABLE) + { + getMembers()->m_activeNotifications[m_notificationIndex].store(true, std::memory_order_release); + } + getMembers()->m_semaphore.post(); +} + +const ConditionVariableData* ConditionNotifier::getMembers() const noexcept +{ + return m_condVarDataPtr; +} + +ConditionVariableData* ConditionNotifier::getMembers() noexcept +{ + return m_condVarDataPtr; +} + +} // namespace popo +} // namespace iox diff --git a/iceoryx_posh/source/popo/building_blocks/condition_variable_data.cpp b/iceoryx_posh/source/popo/building_blocks/condition_variable_data.cpp index 6779f7ae16..9dfdc0e1f7 100644 --- a/iceoryx_posh/source/popo/building_blocks/condition_variable_data.cpp +++ b/iceoryx_posh/source/popo/building_blocks/condition_variable_data.cpp @@ -1,4 +1,5 @@ -// Copyright (c) 2020 by Robert Bosch GmbH, Apex.AI Inc. All rights reserved. +// Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2020 - 2021 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,9 +21,18 @@ namespace iox { namespace popo { +ConditionVariableData::ConditionVariableData() noexcept + : ConditionVariableData("") +{ +} + ConditionVariableData::ConditionVariableData(const ProcessName_t& process) noexcept : m_process(process) { + for (auto& id : m_activeNotifications) + { + id.store(false, std::memory_order_relaxed); + } } } // namespace popo } // namespace iox diff --git a/iceoryx_posh/source/popo/building_blocks/condition_variable_signaler.cpp b/iceoryx_posh/source/popo/building_blocks/condition_variable_signaler.cpp deleted file mode 100644 index d554e79bf9..0000000000 --- a/iceoryx_posh/source/popo/building_blocks/condition_variable_signaler.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2020 by Robert Bosch GmbH. 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. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -#include "iceoryx_posh/internal/popo/building_blocks/condition_variable_signaler.hpp" - -namespace iox -{ -namespace popo -{ -ConditionVariableSignaler::ConditionVariableSignaler( - cxx::not_null condVarDataPtr) noexcept - : m_condVarDataPtr(condVarDataPtr) -{ -} - -void ConditionVariableSignaler::notifyOne() noexcept -{ - getMembers()->m_semaphore.post(); -} - -const ConditionVariableData* ConditionVariableSignaler::getMembers() const noexcept -{ - return m_condVarDataPtr; -} - -ConditionVariableData* ConditionVariableSignaler::getMembers() noexcept -{ - return m_condVarDataPtr; -} - -} // namespace popo -} // namespace iox diff --git a/iceoryx_posh/source/popo/building_blocks/condition_variable_waiter.cpp b/iceoryx_posh/source/popo/building_blocks/condition_variable_waiter.cpp deleted file mode 100644 index 26ac1669d2..0000000000 --- a/iceoryx_posh/source/popo/building_blocks/condition_variable_waiter.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved. -// Copyright (c) 2021 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. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -#include "iceoryx_posh/internal/popo/building_blocks/condition_variable_waiter.hpp" -#include "iceoryx_utils/error_handling/error_handling.hpp" - -namespace iox -{ -namespace popo -{ -ConditionVariableWaiter::ConditionVariableWaiter(cxx::not_null condVarDataPtr) noexcept - : m_condVarDataPtr(condVarDataPtr) -{ -} - -void ConditionVariableWaiter::reset() noexcept -{ - // Count the semaphore down to zero - while (getMembers() - ->m_semaphore.tryWait() - .or_else([](posix::SemaphoreError) { - errorHandler(Error::kPOPO__CONDITION_VARIABLE_WAITER_SEMAPHORE_CORRUPTED_IN_RESET, - nullptr, - ErrorLevel::FATAL); - }) - .value()) - { - } -} - -bool ConditionVariableWaiter::wasNotified() const noexcept -{ - auto result = getMembers()->m_semaphore.getValue(); - if (result.has_error()) - { - errorHandler( - Error::kPOPO__CONDITION_VARIABLE_WAITER_SEMAPHORE_CORRUPTED_IN_WAS_TRIGGERED, nullptr, ErrorLevel::FATAL); - return false; - } - - return *result != 0; -} - -void ConditionVariableWaiter::wait() noexcept -{ - if (getMembers()->m_semaphore.wait().has_error()) - { - errorHandler(Error::kPOPO__CONDITION_VARIABLE_WAITER_SEMAPHORE_CORRUPTED_IN_WAIT, nullptr, ErrorLevel::FATAL); - } -} - -bool ConditionVariableWaiter::timedWait(units::Duration timeToWait) noexcept -{ - auto continueOnInterrupt{false}; - auto result = getMembers()->m_semaphore.timedWait(timeToWait, continueOnInterrupt); - - if (result.has_error()) - { - errorHandler( - Error::kPOPO__CONDITION_VARIABLE_WAITER_SEMAPHORE_CORRUPTED_IN_TIMED_WAIT, nullptr, ErrorLevel::FATAL); - return false; - } - - return *result != posix::SemaphoreWaitState::TIMEOUT; -} - -const ConditionVariableData* ConditionVariableWaiter::getMembers() const noexcept -{ - return m_condVarDataPtr; -} - -ConditionVariableData* ConditionVariableWaiter::getMembers() noexcept -{ - return m_condVarDataPtr; -} - -} // namespace popo -} // namespace iox diff --git a/iceoryx_posh/source/popo/building_blocks/event_listener.cpp b/iceoryx_posh/source/popo/building_blocks/event_listener.cpp deleted file mode 100644 index cdd982d987..0000000000 --- a/iceoryx_posh/source/popo/building_blocks/event_listener.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2021 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. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - - -#include "iceoryx_posh/internal/popo/building_blocks/event_listener.hpp" - -namespace iox -{ -namespace popo -{ -EventListener::EventListener(EventVariableData& dataRef) noexcept - : m_pointerToEventVariableData(&dataRef) -{ -} - -void EventListener::destroy() noexcept -{ - m_toBeDestroyed.store(true, std::memory_order_relaxed); - if (m_pointerToEventVariableData->m_semaphore.post().has_error()) - { - errorHandler(Error::kPOPO__EVENT_VARIABLE_WAITER_SEMAPHORE_CORRUPTED_IN_DESTROY, nullptr, ErrorLevel::FATAL); - } -} - -EventListener::NotificationVector_t EventListener::wait() noexcept -{ - using Type_t = iox::cxx::BestFittingType_t; - NotificationVector_t activeNotifications; - - resetSemaphore(); - while (!m_toBeDestroyed.load(std::memory_order_relaxed)) - { - for (Type_t i = 0U; i < MAX_NUMBER_OF_EVENTS_PER_LISTENER; i++) - { - if (m_pointerToEventVariableData->m_activeNotifications[i].load(std::memory_order_relaxed)) - { - reset(i); - activeNotifications.emplace_back(i); - } - } - if (!activeNotifications.empty()) - { - return activeNotifications; - } - - if (m_pointerToEventVariableData->m_semaphore.wait().has_error()) - { - errorHandler(Error::kPOPO__EVENT_VARIABLE_WAITER_SEMAPHORE_CORRUPTED_IN_WAIT, nullptr, ErrorLevel::FATAL); - break; - } - } - - return activeNotifications; -} - -void EventListener::reset(const uint64_t index) noexcept -{ - if (index < MAX_NUMBER_OF_EVENTS_PER_LISTENER) - { - m_pointerToEventVariableData->m_activeNotifications[index].store(false, std::memory_order_relaxed); - } -} - -void EventListener::resetSemaphore() noexcept -{ - // Count the semaphore down to zero - bool hasFatalError = false; - while (!hasFatalError - && m_pointerToEventVariableData->m_semaphore.tryWait() - .or_else([&](posix::SemaphoreError) { - errorHandler( - Error::kPOPO__EVENT_VARIABLE_WAITER_SEMAPHORE_CORRUPTED_IN_RESET, nullptr, ErrorLevel::FATAL); - hasFatalError = true; - }) - .value()) - { - } -} -} // namespace popo -} // namespace iox - diff --git a/iceoryx_posh/source/popo/building_blocks/event_notifier.cpp b/iceoryx_posh/source/popo/building_blocks/event_notifier.cpp deleted file mode 100644 index a4ffc43953..0000000000 --- a/iceoryx_posh/source/popo/building_blocks/event_notifier.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2021 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. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - - -#include "iceoryx_posh/internal/popo/building_blocks/event_notifier.hpp" -#include "iceoryx_posh/internal/log/posh_logging.hpp" - -namespace iox -{ -namespace popo -{ -EventNotifier::EventNotifier(EventVariableData& dataRef, const uint64_t index) noexcept - : m_pointerToEventVariableData(&dataRef) - , m_notificationIndex(index) -{ - if (index >= MAX_NUMBER_OF_EVENTS_PER_LISTENER) - { - LogError() << "The provided index " << index << " is too large. The index has to be in the range of [0, " - << MAX_NUMBER_OF_EVENTS_PER_LISTENER << "[."; - errorHandler(Error::kPOPO__EVENT_NOTIFIER_INDEX_TOO_LARGE, nullptr, ErrorLevel::MODERATE); - } -} - -void EventNotifier::notify() noexcept -{ - if (m_notificationIndex < MAX_NUMBER_OF_EVENTS_PER_LISTENER) - { - m_pointerToEventVariableData->m_activeNotifications[m_notificationIndex].store(true, std::memory_order_release); - } - m_pointerToEventVariableData->m_semaphore.post(); -} -} // namespace popo -} // namespace iox - diff --git a/iceoryx_posh/source/popo/listener.cpp b/iceoryx_posh/source/popo/listener.cpp index d2a3bfab41..248d02b6d1 100644 --- a/iceoryx_posh/source/popo/listener.cpp +++ b/iceoryx_posh/source/popo/listener.cpp @@ -15,7 +15,6 @@ // SPDX-License-Identifier: Apache-2.0 #include "iceoryx_posh/popo/listener.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/event_notifier.hpp" #include "iceoryx_posh/runtime/posh_runtime.hpp" #include "iceoryx_utils/cxx/helplets.hpp" @@ -24,13 +23,13 @@ namespace iox namespace popo { Listener::Listener() noexcept - : Listener(runtime::PoshRuntime::getInstance().getMiddlewareEventVariable()) + : Listener(*runtime::PoshRuntime::getInstance().getMiddlewareConditionVariable()) { } -Listener::Listener(EventVariableData* eventVariable) noexcept - : m_eventVariable(eventVariable) - , m_eventListener(*eventVariable) +Listener::Listener(ConditionVariableData& conditionVariable) noexcept + : m_conditionVariableData(&conditionVariable) + , m_conditionListener(conditionVariable) { m_thread = std::thread(&Listener::threadLoop, this); } @@ -38,10 +37,10 @@ Listener::Listener(EventVariableData* eventVariable) noexcept Listener::~Listener() { m_wasDtorCalled.store(true, std::memory_order_relaxed); - m_eventListener.destroy(); + m_conditionListener.destroy(); m_thread.join(); - m_eventVariable->m_toBeDestroyed.store(true, std::memory_order_relaxed); + m_conditionVariableData->m_toBeDestroyed.store(true, std::memory_order_relaxed); } cxx::expected @@ -81,7 +80,7 @@ void Listener::threadLoop() noexcept { while (m_wasDtorCalled.load(std::memory_order_relaxed) == false) { - auto activateNotificationIds = m_eventListener.wait(); + auto activateNotificationIds = m_conditionListener.waitForNotifications(); cxx::forEach(activateNotificationIds, [this](auto id) { m_events[id]->executeCallback(); }); } diff --git a/iceoryx_posh/source/popo/ports/client_port_user.cpp b/iceoryx_posh/source/popo/ports/client_port_user.cpp index e05043ee06..41850a9e9a 100644 --- a/iceoryx_posh/source/popo/ports/client_port_user.cpp +++ b/iceoryx_posh/source/popo/ports/client_port_user.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2021 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. @@ -89,9 +90,10 @@ bool ClientPortUser::hasLostResponsesSinceLastCall() noexcept return m_chunkReceiver.hasOverflown(); } -void ClientPortUser::setConditionVariable(ConditionVariableData* conditionVariableDataPtr) noexcept +void ClientPortUser::setConditionVariable(ConditionVariableData& conditionVariableData, + const uint64_t notificationIndex) noexcept { - m_chunkReceiver.setConditionVariable(conditionVariableDataPtr); + m_chunkReceiver.setConditionVariable(conditionVariableData, notificationIndex); } void ClientPortUser::unsetConditionVariable() noexcept diff --git a/iceoryx_posh/source/popo/ports/server_port_user.cpp b/iceoryx_posh/source/popo/ports/server_port_user.cpp index 1cad897c3c..016bd2ede8 100644 --- a/iceoryx_posh/source/popo/ports/server_port_user.cpp +++ b/iceoryx_posh/source/popo/ports/server_port_user.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2021 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. @@ -102,9 +103,10 @@ bool ServerPortUser::hasClients() const noexcept return m_chunkSender.hasStoredQueues(); } -void ServerPortUser::setConditionVariable(ConditionVariableData* conditionVariableDataPtr) noexcept +void ServerPortUser::setConditionVariable(ConditionVariableData& conditionVariableData, + const uint64_t notificationIndex) noexcept { - m_chunkReceiver.setConditionVariable(conditionVariableDataPtr); + m_chunkReceiver.setConditionVariable(conditionVariableData, notificationIndex); } void ServerPortUser::unsetConditionVariable() noexcept diff --git a/iceoryx_posh/source/popo/ports/subscriber_port_user.cpp b/iceoryx_posh/source/popo/ports/subscriber_port_user.cpp index f260b62188..e06d5a836b 100644 --- a/iceoryx_posh/source/popo/ports/subscriber_port_user.cpp +++ b/iceoryx_posh/source/popo/ports/subscriber_port_user.cpp @@ -89,14 +89,10 @@ bool SubscriberPortUser::hasLostChunksSinceLastCall() noexcept return m_chunkReceiver.hasOverflown(); } -void SubscriberPortUser::setConditionVariable(ConditionVariableData* conditionVariableDataPtr) noexcept +void SubscriberPortUser::setConditionVariable(ConditionVariableData& conditionVariableData, + const uint64_t notificationIndex) noexcept { - m_chunkReceiver.setConditionVariable(conditionVariableDataPtr); -} - -void SubscriberPortUser::setEventVariable(EventVariableData& eventVariableData, const uint64_t eventId) noexcept -{ - m_chunkReceiver.setEventVariable(eventVariableData, eventId); + m_chunkReceiver.setConditionVariable(conditionVariableData, notificationIndex); } void SubscriberPortUser::unsetConditionVariable() noexcept diff --git a/iceoryx_posh/source/popo/trigger.cpp b/iceoryx_posh/source/popo/trigger.cpp index 8dff1a108f..3448ed88aa 100644 --- a/iceoryx_posh/source/popo/trigger.cpp +++ b/iceoryx_posh/source/popo/trigger.cpp @@ -15,13 +15,11 @@ // SPDX-License-Identifier: Apache-2.0 #include "iceoryx_posh/popo/trigger.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/condition_variable_signaler.hpp" namespace iox { namespace popo { -std::atomic Trigger::uniqueIdCounter{0U}; constexpr uint64_t Trigger::INVALID_TRIGGER_ID; Trigger::~Trigger() diff --git a/iceoryx_posh/source/popo/trigger_handle.cpp b/iceoryx_posh/source/popo/trigger_handle.cpp index 4bf97bdc15..1665c74431 100644 --- a/iceoryx_posh/source/popo/trigger_handle.cpp +++ b/iceoryx_posh/source/popo/trigger_handle.cpp @@ -15,25 +15,16 @@ // SPDX-License-Identifier: Apache-2.0 #include "iceoryx_posh/popo/trigger_handle.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/condition_variable_signaler.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/event_notifier.hpp" +#include "iceoryx_posh/internal/popo/building_blocks/condition_notifier.hpp" namespace iox { namespace popo { -TriggerHandle::TriggerHandle(EventVariableData& eventVariableDataRef, +TriggerHandle::TriggerHandle(ConditionVariableData& conditionVariableData, const cxx::MethodCallback resetCallback, const uint64_t uniqueTriggerId) noexcept - : TriggerHandle(static_cast(eventVariableDataRef), resetCallback, uniqueTriggerId) -{ - m_doesContainEventVariable = true; -} - -TriggerHandle::TriggerHandle(ConditionVariableData& conditionVariableDataPtr, - const cxx::MethodCallback resetCallback, - const uint64_t uniqueTriggerId) noexcept - : m_conditionVariableDataPtr(&conditionVariableDataPtr) + : m_conditionVariableDataPtr(&conditionVariableData) , m_resetCallback(resetCallback) , m_uniqueTriggerId(uniqueTriggerId) { @@ -54,10 +45,9 @@ TriggerHandle& TriggerHandle::operator=(TriggerHandle&& rhs) noexcept reset(); - m_conditionVariableDataPtr = std::move(rhs.m_conditionVariableDataPtr); + m_conditionVariableDataPtr = rhs.m_conditionVariableDataPtr; m_resetCallback = std::move(rhs.m_resetCallback); m_uniqueTriggerId = rhs.m_uniqueTriggerId; - m_doesContainEventVariable = rhs.m_doesContainEventVariable; rhs.invalidate(); } @@ -88,15 +78,7 @@ void TriggerHandle::trigger() noexcept if (isValid()) { - if (m_doesContainEventVariable) - { - EventNotifier(*reinterpret_cast(m_conditionVariableDataPtr), m_uniqueTriggerId) - .notify(); - } - else - { - ConditionVariableSignaler(m_conditionVariableDataPtr).notifyOne(); - } + ConditionNotifier(*m_conditionVariableDataPtr, m_uniqueTriggerId).notify(); } } @@ -136,11 +118,5 @@ uint64_t TriggerHandle::getUniqueId() const noexcept return m_uniqueTriggerId; } - -bool TriggerHandle::doesContainEventVariable() const noexcept -{ - return m_doesContainEventVariable; -} - } // namespace popo } // namespace iox diff --git a/iceoryx_posh/source/roudi/port_manager.cpp b/iceoryx_posh/source/roudi/port_manager.cpp index 00f1230801..bf5db4a99f 100644 --- a/iceoryx_posh/source/roudi/port_manager.cpp +++ b/iceoryx_posh/source/roudi/port_manager.cpp @@ -129,8 +129,6 @@ void PortManager::doDiscovery() noexcept handleNodes(); handleConditionVariables(); - - handleEventVariables(); } void PortManager::handlePublisherPorts() noexcept @@ -364,18 +362,6 @@ void PortManager::handleConditionVariables() noexcept } } -void PortManager::handleEventVariables() noexcept -{ - for (auto eventVariableData : m_portPool->getEventVariableDataList()) - { - if (eventVariableData->m_toBeDestroyed.load(std::memory_order_relaxed)) - { - m_portPool->removeEventVariableData(eventVariableData); - LogDebug() << "Destroyed EventVariableData"; - } - } -} - bool PortManager::sendToAllMatchingPublisherPorts(const capro::CaproMessage& message, SubscriberPortType& subscriberSource) noexcept { @@ -513,15 +499,6 @@ void PortManager::deletePortsOfProcess(const ProcessName_t& processName) noexcep LogDebug() << "Deleted condition variable of application" << processName; } } - - for (auto eventVariableData : m_portPool->getEventVariableDataList()) - { - if (processName == eventVariableData->m_process) - { - m_portPool->removeEventVariableData(eventVariableData); - LogDebug() << "Deleted event variable of application" << processName; - } - } } void PortManager::destroyPublisherPort(PublisherPortRouDiType::MemberType_t* const publisherPortData) noexcept @@ -724,11 +701,5 @@ PortManager::acquireConditionVariableData(const ProcessName_t& process) noexcept return m_portPool->addConditionVariableData(process); } -cxx::expected -PortManager::acquireEventVariableData(const ProcessName_t& process) noexcept -{ - return m_portPool->addEventVariableData(process); -} - } // namespace roudi } // namespace iox diff --git a/iceoryx_posh/source/roudi/port_pool.cpp b/iceoryx_posh/source/roudi/port_pool.cpp index a5cbcb86fc..06440f551c 100644 --- a/iceoryx_posh/source/roudi/port_pool.cpp +++ b/iceoryx_posh/source/roudi/port_pool.cpp @@ -49,11 +49,6 @@ PortPool::getConditionVariableDataList() noexcept return m_portPoolData->m_conditionVariableMembers.content(); } -cxx::vector PortPool::getEventVariableDataList() noexcept -{ - return m_portPoolData->m_eventVariableMembers.content(); -} - cxx::expected PortPool::addInterfacePort(const ProcessName_t& applicationName, const capro::Interfaces interface) noexcept { @@ -115,21 +110,6 @@ PortPool::addConditionVariableData(const ProcessName_t& process) noexcept } } -cxx::expected -PortPool::addEventVariableData(const ProcessName_t& process) noexcept -{ - if (m_portPoolData->m_eventVariableMembers.hasFreeSpace()) - { - auto eventVariableData = m_portPoolData->m_eventVariableMembers.insert(process); - return cxx::success(eventVariableData); - } - else - { - errorHandler(Error::kPORT_POOL__EVENT_VARIABLE_LIST_OVERFLOW, nullptr, ErrorLevel::MODERATE); - return cxx::error(PortPoolError::EVENT_VARIABLE_LIST_FULL); - } -} - void PortPool::removeInterfacePort(popo::InterfacePortData* const portData) noexcept { m_portPoolData->m_interfacePortMembers.erase(portData); @@ -150,11 +130,6 @@ void PortPool::removeConditionVariableData(popo::ConditionVariableData* const co m_portPoolData->m_conditionVariableMembers.erase(conditionVariableData); } -void PortPool::removeEventVariableData(popo::EventVariableData* const eventVariableData) noexcept -{ - m_portPoolData->m_eventVariableMembers.erase(eventVariableData); -} - std::atomic* PortPool::serviceRegistryChangeCounter() noexcept { return &m_portPoolData->m_serviceRegistryChangeCounter; diff --git a/iceoryx_posh/source/roudi/roudi.cpp b/iceoryx_posh/source/roudi/roudi.cpp index 44495405a3..5a79ea6154 100644 --- a/iceoryx_posh/source/roudi/roudi.cpp +++ b/iceoryx_posh/source/roudi/roudi.cpp @@ -244,19 +244,6 @@ void RouDi::processMessage(const runtime::IpcMessage& message, } break; } - case runtime::IpcMessageType::CREATE_EVENT_VARIABLE: - { - if (message.getNumberOfElements() != 2) - { - LogError() << "Wrong number of parameters for \"IpcMessageType::CREATE_EVENT_VARIABLE\" from \"" - << processName << "\"received!"; - } - else - { - m_prcMgr.addEventVariableForProcess(processName); - } - break; - } case runtime::IpcMessageType::CREATE_INTERFACE: { if (message.getNumberOfElements() != 4) diff --git a/iceoryx_posh/source/roudi/roudi_cmd_line_parser.cpp b/iceoryx_posh/source/roudi/roudi_cmd_line_parser.cpp index 150d349652..65d49d4df5 100644 --- a/iceoryx_posh/source/roudi/roudi_cmd_line_parser.cpp +++ b/iceoryx_posh/source/roudi/roudi_cmd_line_parser.cpp @@ -160,8 +160,7 @@ CmdLineParser::parse(int argc, char* argv[], const CmdLineArgumentParsingMode cm } else { - m_processKillDelay = - units::Duration::fromSeconds(processKillDelayInSeconds); + m_processKillDelay = units::Duration::fromSeconds(processKillDelayInSeconds); } break; } @@ -212,8 +211,13 @@ CmdLineParser::parse(int argc, char* argv[], const CmdLineArgumentParsingMode cm break; } } - return cxx::success(CmdLineArgs_t{ - m_monitoringMode, m_logLevel, m_compatibilityCheckLevel, m_processKillDelay, m_uniqueRouDiId, m_run, ""}); + return cxx::success(CmdLineArgs_t{m_monitoringMode, + m_logLevel, + m_compatibilityCheckLevel, + m_processKillDelay, + m_uniqueRouDiId, + m_run, + iox::roudi::ConfigFilePathString_t("")}); } // namespace roudi } // namespace config } // namespace iox diff --git a/iceoryx_posh/source/roudi/roudi_process.cpp b/iceoryx_posh/source/roudi/roudi_process.cpp index e6003fd6b6..f2555bb35b 100644 --- a/iceoryx_posh/source/roudi/roudi_process.cpp +++ b/iceoryx_posh/source/roudi/roudi_process.cpp @@ -751,44 +751,6 @@ void ProcessManager::addConditionVariableForProcess(const ProcessName_t& process } } -void ProcessManager::addEventVariableForProcess(const ProcessName_t& processName) noexcept -{ - std::lock_guard g(m_mutex); - - RouDiProcess* process = getProcessFromList(processName); - if (nullptr != process) - { - // Try to create an event variable - m_portManager.acquireEventVariableData(processName) - .and_then([&](auto condVar) { - auto offset = rp::BaseRelativePointer::getOffset(m_mgmtSegmentId, condVar); - - runtime::IpcMessage sendBuffer; - sendBuffer << runtime::IpcMessageTypeToString(runtime::IpcMessageType::CREATE_EVENT_VARIABLE_ACK) - << std::to_string(offset) << std::to_string(m_mgmtSegmentId); - process->sendViaIpcChannel(sendBuffer); - - LogDebug() << "Created new EventVariable for application " << processName; - }) - .or_else([&](PortPoolError error) { - runtime::IpcMessage sendBuffer; - sendBuffer << runtime::IpcMessageTypeToString(runtime::IpcMessageType::ERROR); - if (error == PortPoolError::EVENT_VARIABLE_LIST_FULL) - { - sendBuffer << runtime::IpcMessageErrorTypeToString( - runtime::IpcMessageErrorType::EVENT_VARIABLE_LIST_FULL); - } - process->sendViaIpcChannel(sendBuffer); - - LogDebug() << "Could not create new EventVariable for application " << processName; - }); - } - else - { - LogWarn() << "Unknown application " << processName << " requested a EventVariable."; - } -} - void ProcessManager::initIntrospection(ProcessIntrospectionType* processIntrospection) noexcept { m_processIntrospection = processIntrospection; diff --git a/iceoryx_posh/source/runtime/posh_runtime.cpp b/iceoryx_posh/source/runtime/posh_runtime.cpp index 66cfe54be7..e69a1d9ca2 100644 --- a/iceoryx_posh/source/runtime/posh_runtime.cpp +++ b/iceoryx_posh/source/runtime/posh_runtime.cpp @@ -531,43 +531,6 @@ PoshRuntime::requestConditionVariableFromRoudi(const IpcMessage& sendBuffer) noe return cxx::error(IpcMessageErrorType::REQUEST_CONDITION_VARIABLE_WRONG_IPC_MESSAGE_RESPONSE); } -cxx::expected -PoshRuntime::requestEventVariableFromRoudi(const IpcMessage& sendBuffer) noexcept -{ - IpcMessage receiveBuffer; - if (sendRequestToRouDi(sendBuffer, receiveBuffer) && (3U == receiveBuffer.getNumberOfElements())) - { - std::string mqMessage = receiveBuffer.getElementAtIndex(0U); - - if (stringToIpcMessageType(mqMessage.c_str()) == IpcMessageType::CREATE_EVENT_VARIABLE_ACK) - { - rp::BaseRelativePointer::id_t segmentId{0U}; - cxx::convert::fromString(receiveBuffer.getElementAtIndex(2U).c_str(), segmentId); - rp::BaseRelativePointer::offset_t offset{0U}; - cxx::convert::fromString(receiveBuffer.getElementAtIndex(1U).c_str(), offset); - auto ptr = rp::BaseRelativePointer::getPtr(segmentId, offset); - return cxx::success(reinterpret_cast(ptr)); - } - } - else - { - if (receiveBuffer.getNumberOfElements() == 2U) - { - std::string mqMessage1 = receiveBuffer.getElementAtIndex(0U); - std::string mqMessage2 = receiveBuffer.getElementAtIndex(1U); - if (stringToIpcMessageType(mqMessage1.c_str()) == IpcMessageType::ERROR) - { - LogError() << "Request event variable received no valid event variable port from RouDi."; - return cxx::error(stringToIpcMessageErrorType(mqMessage2.c_str())); - } - } - } - - LogError() << "Request event variable got wrong response from message queue :'" << receiveBuffer.getMessage() - << "'"; - return cxx::error(IpcMessageErrorType::REQUEST_EVENT_VARIABLE_WRONG_IPC_MESSAGE_RESPONSE); -} - popo::ConditionVariableData* PoshRuntime::getMiddlewareConditionVariable() noexcept { IpcMessage sendBuffer; @@ -600,35 +563,6 @@ popo::ConditionVariableData* PoshRuntime::getMiddlewareConditionVariable() noexc return maybeConditionVariable.value(); } -popo::EventVariableData* PoshRuntime::getMiddlewareEventVariable() noexcept -{ - IpcMessage sendBuffer; - sendBuffer << IpcMessageTypeToString(IpcMessageType::CREATE_EVENT_VARIABLE) << m_appName; - - auto maybeEventVariable = requestEventVariableFromRoudi(sendBuffer); - if (maybeEventVariable.has_error()) - { - switch (maybeEventVariable.get_error()) - { - case IpcMessageErrorType::EVENT_VARIABLE_LIST_FULL: - errorHandler(Error::kPOSH__RUNTIME_ROUDI_EVENT_VARIABLE_LIST_FULL, nullptr, iox::ErrorLevel::SEVERE); - break; - case IpcMessageErrorType::REQUEST_EVENT_VARIABLE_WRONG_IPC_MESSAGE_RESPONSE: - errorHandler(Error::kPOSH__RUNTIME_ROUDI_REQUEST_EVENT_VARIABLE_WRONG_MESSAGE_QUEUE_RESPONSE, - nullptr, - iox::ErrorLevel::SEVERE); - break; - default: - errorHandler(Error::kPOSH__RUNTIME_ROUDI_EVENT_VARIABLE_CREATION_UNDEFINED_BEHAVIOR, - nullptr, - iox::ErrorLevel::SEVERE); - break; - } - return nullptr; - } - return maybeEventVariable.value(); -} - bool PoshRuntime::sendRequestToRouDi(const IpcMessage& msg, IpcMessage& answer) noexcept { // runtime must be thread safe diff --git a/iceoryx_posh/source/runtime/shared_memory_user.cpp b/iceoryx_posh/source/runtime/shared_memory_user.cpp index b84acb45ac..996a3736c2 100644 --- a/iceoryx_posh/source/runtime/shared_memory_user.cpp +++ b/iceoryx_posh/source/runtime/shared_memory_user.cpp @@ -46,7 +46,7 @@ SharedMemoryUser::SharedMemoryUser(const bool doMapSharedMemoryIntoThread, << iox::log::HexFormat(reinterpret_cast(sharedMemoryObject.getBaseAddress())) << " with size " << sharedMemoryObject.getSizeInBytes() << " to id " << segmentId; - openDataSegments(segmentId, segmentManagerAddressOffset); + this->openDataSegments(segmentId, segmentManagerAddressOffset); m_shmObject.emplace(std::move(sharedMemoryObject)); }) diff --git a/iceoryx_posh/test/mocks/subscriber_mock.hpp b/iceoryx_posh/test/mocks/subscriber_mock.hpp index 2520d76863..323ac8f549 100644 --- a/iceoryx_posh/test/mocks/subscriber_mock.hpp +++ b/iceoryx_posh/test/mocks/subscriber_mock.hpp @@ -55,8 +55,7 @@ class MockSubscriberPortUser MOCK_METHOD0(releaseQueuedChunks, void()); MOCK_CONST_METHOD0(hasNewChunks, bool()); MOCK_METHOD0(hasLostChunksSinceLastCall, bool()); - MOCK_METHOD1(setConditionVariable, bool(iox::popo::ConditionVariableData*)); - MOCK_METHOD2(setEventVariable, bool(iox::popo::EventVariableData&, uint64_t)); + MOCK_METHOD2(setConditionVariable, bool(iox::popo::ConditionVariableData&, uint64_t)); MOCK_METHOD0(isConditionVariableSet, bool()); MOCK_METHOD0(unsetConditionVariable, bool()); MOCK_METHOD0(destroy, bool()); diff --git a/iceoryx_posh/test/mocks/wait_set_mock.hpp b/iceoryx_posh/test/mocks/wait_set_mock.hpp index ce2a397bc3..20baa32564 100644 --- a/iceoryx_posh/test/mocks/wait_set_mock.hpp +++ b/iceoryx_posh/test/mocks/wait_set_mock.hpp @@ -21,8 +21,8 @@ class WaitSetMock : public iox::popo::WaitSet<> { public: - WaitSetMock(iox::popo::ConditionVariableData* condVarDataPtr) noexcept - : WaitSet(condVarDataPtr) + WaitSetMock(iox::popo::ConditionVariableData& condVarData) noexcept + : WaitSet(condVarData) { } }; diff --git a/iceoryx_posh/test/moduletests/test_popo_base_subscriber.cpp b/iceoryx_posh/test/moduletests/test_popo_base_subscriber.cpp index c7b5430689..9b0be8cb87 100644 --- a/iceoryx_posh/test/moduletests/test_popo_base_subscriber.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_base_subscriber.cpp @@ -158,9 +158,9 @@ TEST_F(BaseSubscriberTest, ClearReceiveBufferCallForwardedToUnderlyingSubscriber TEST_F(BaseSubscriberTest, AttachToWaitsetForwardedToUnderlyingSubscriberPort) { iox::popo::ConditionVariableData condVar("Horscht"); - WaitSetMock waitSet(&condVar); + WaitSetMock waitSet(condVar); // ===== Setup ===== // - EXPECT_CALL(sut.port(), setConditionVariable(&condVar)).Times(1); + EXPECT_CALL(sut.port(), setConditionVariable(_, _)).Times(1); // ===== Test ===== // waitSet.attachEvent(sut, iox::popo::SubscriberEvent::HAS_DATA); // ===== Verify ===== // @@ -171,8 +171,8 @@ TEST_F(BaseSubscriberTest, WaitSetUnsetConditionVariableWhenGoingOutOfScope) { // ===== Setup ===== // iox::popo::ConditionVariableData condVar("Horscht"); - std::unique_ptr waitSet{new WaitSetMock(&condVar)}; - EXPECT_CALL(sut.port(), setConditionVariable(&condVar)).Times(1); + std::unique_ptr waitSet{new WaitSetMock(condVar)}; + EXPECT_CALL(sut.port(), setConditionVariable(_, _)).Times(1); waitSet->attachEvent(sut, iox::popo::SubscriberEvent::HAS_DATA); // ===== Test ===== // EXPECT_CALL(sut.port(), unsetConditionVariable).Times(1); @@ -184,12 +184,12 @@ TEST_F(BaseSubscriberTest, AttachingAttachedSubscriberToNewWaitsetDetachesItFrom { // ===== Setup ===== // iox::popo::ConditionVariableData condVar("Horscht"); - std::unique_ptr waitSet{new WaitSetMock(&condVar)}; - std::unique_ptr waitSet2{new WaitSetMock(&condVar)}; - EXPECT_CALL(sut.port(), setConditionVariable(&condVar)).Times(1); + std::unique_ptr waitSet{new WaitSetMock(condVar)}; + std::unique_ptr waitSet2{new WaitSetMock(condVar)}; + EXPECT_CALL(sut.port(), setConditionVariable(_, _)).Times(1); waitSet->attachEvent(sut, iox::popo::SubscriberEvent::HAS_DATA); // ===== Test ===== // - EXPECT_CALL(sut.port(), setConditionVariable(&condVar)).Times(1); + EXPECT_CALL(sut.port(), setConditionVariable(_, _)).Times(1); waitSet2->attachEvent(sut, iox::popo::SubscriberEvent::HAS_DATA); // ===== Verify ===== // EXPECT_EQ(waitSet->size(), 0U); @@ -201,8 +201,8 @@ TEST_F(BaseSubscriberTest, DetachingAttachedEventCleansup) { // ===== Setup ===== // iox::popo::ConditionVariableData condVar("Horscht"); - std::unique_ptr waitSet{new WaitSetMock(&condVar)}; - EXPECT_CALL(sut.port(), setConditionVariable(&condVar)).Times(1); + std::unique_ptr waitSet{new WaitSetMock(condVar)}; + EXPECT_CALL(sut.port(), setConditionVariable(_, _)).Times(1); waitSet->attachEvent(sut, iox::popo::SubscriberEvent::HAS_DATA); // ===== Test ===== // EXPECT_CALL(sut.port(), unsetConditionVariable).Times(1); diff --git a/iceoryx_posh/test/moduletests/test_popo_chunk_queue.cpp b/iceoryx_posh/test/moduletests/test_popo_chunk_queue.cpp index cbc600e6c2..4ee609b688 100644 --- a/iceoryx_posh/test/moduletests/test_popo_chunk_queue.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_chunk_queue.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2021 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,7 +21,7 @@ #include "iceoryx_posh/internal/popo/building_blocks/chunk_queue_data.hpp" #include "iceoryx_posh/internal/popo/building_blocks/chunk_queue_popper.hpp" #include "iceoryx_posh/internal/popo/building_blocks/chunk_queue_pusher.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/condition_variable_waiter.hpp" +#include "iceoryx_posh/internal/popo/building_blocks/condition_listener.hpp" #include "iceoryx_posh/internal/popo/building_blocks/locking_policy.hpp" #include "iceoryx_posh/mepoo/chunk_header.hpp" #include "iceoryx_utils/internal/posix_wrapper/shared_memory_object/allocator.hpp" @@ -163,7 +164,7 @@ TYPED_TEST(ChunkQueue_test, AttachConditionVariable) { ConditionVariableData condVar("Horscht"); - this->m_popper.setConditionVariable(&condVar); + this->m_popper.setConditionVariable(condVar, 0U); EXPECT_THAT(this->m_popper.isConditionVariableSet(), Eq(true)); } @@ -171,9 +172,9 @@ TYPED_TEST(ChunkQueue_test, AttachConditionVariable) TYPED_TEST(ChunkQueue_test, PushAndNotifyConditionVariable) { ConditionVariableData condVar("Horscht"); - ConditionVariableWaiter condVarWaiter{&condVar}; + ConditionListener condVarWaiter{condVar}; - this->m_popper.setConditionVariable(&condVar); + this->m_popper.setConditionVariable(condVar, 0U); auto chunk = this->allocateChunk(); this->m_pusher.push(chunk); @@ -186,11 +187,11 @@ TYPED_TEST(ChunkQueue_test, AttachSecondConditionVariable) { ConditionVariableData condVar1("Horscht"); ConditionVariableData condVar2("Schnuppi"); - ConditionVariableWaiter condVarWaiter1{&condVar1}; - ConditionVariableWaiter condVarWaiter2{&condVar2}; + ConditionListener condVarWaiter1{condVar1}; + ConditionListener condVarWaiter2{condVar2}; - this->m_popper.setConditionVariable(&condVar1); - this->m_popper.setConditionVariable(&condVar2); + this->m_popper.setConditionVariable(condVar1, 0U); + this->m_popper.setConditionVariable(condVar2, 1U); EXPECT_THAT(condVarWaiter1.timedWait(1_ns), Eq(false)); EXPECT_THAT(condVarWaiter2.timedWait(1_ns), Eq(false)); diff --git a/iceoryx_posh/test/moduletests/test_popo_condition_variable.cpp b/iceoryx_posh/test/moduletests/test_popo_condition_variable.cpp index 93f3bd7027..0ab7a5826d 100644 --- a/iceoryx_posh/test/moduletests/test_popo_condition_variable.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_condition_variable.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2021 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. @@ -14,14 +15,17 @@ // // SPDX-License-Identifier: Apache-2.0 +#include "iceoryx_posh/internal/popo/building_blocks/condition_listener.hpp" +#include "iceoryx_posh/internal/popo/building_blocks/condition_notifier.hpp" #include "iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/condition_variable_signaler.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/condition_variable_waiter.hpp" - #include "test.hpp" +#include "testutils/timing_test.hpp" +#include "testutils/watch_dog.hpp" + #include #include #include +#include using namespace ::testing; using ::testing::Return; @@ -32,21 +36,42 @@ using namespace iox::units::duration_literals; class ConditionVariable_test : public Test { public: - ConditionVariableData m_condVarData{"Horscht"}; - ConditionVariableWaiter m_waiter{&m_condVarData}; - ConditionVariableSignaler m_signaler{&m_condVarData}; + using NotificationVector_t = ConditionListener::NotificationVector_t; + using Type_t = iox::cxx::BestFittingType_t; + const iox::ProcessName_t m_process{"Ferdinand"}; + const iox::units::Duration m_timeToWait = 2_s; + + ConditionVariableData m_condVarData{m_process}; + ConditionListener m_waiter{m_condVarData}; + ConditionNotifier m_signaler{m_condVarData, 0U}; iox::posix::Semaphore m_syncSemaphore = - iox::posix::Semaphore::create(iox::posix::CreateUnnamedSingleProcessSemaphore, 0u).value(); + iox::posix::Semaphore::create(iox::posix::CreateUnnamedSingleProcessSemaphore, 0U).value(); - void SetUp(){}; - void TearDown() + void SetUp() override{}; + void TearDown() override { // Reset condition variable - m_waiter.reset(); + m_waiter.resetSemaphore(); }; }; +TEST_F(ConditionVariable_test, ConditionListenerIsNeitherCopyNorMovable) +{ + EXPECT_FALSE(std::is_copy_constructible::value); + EXPECT_FALSE(std::is_move_constructible::value); + EXPECT_FALSE(std::is_copy_assignable::value); + EXPECT_FALSE(std::is_move_assignable::value); +} + +TEST_F(ConditionVariable_test, ConditionNotifierIsNeitherCopyNorMovable) +{ + EXPECT_FALSE(std::is_copy_constructible::value); + EXPECT_FALSE(std::is_move_constructible::value); + EXPECT_FALSE(std::is_copy_assignable::value); + EXPECT_FALSE(std::is_move_assignable::value); +} + TEST_F(ConditionVariable_test, TimedWaitWithInvalidTimeResultsInFailure) { EXPECT_FALSE(m_waiter.timedWait(0_ms)); @@ -59,13 +84,13 @@ TEST_F(ConditionVariable_test, NoNotifyResultsInTimeoutSingleThreaded) TEST_F(ConditionVariable_test, NotifyOnceResultsInNoTimeoutSingleThreaded) { - m_signaler.notifyOne(); + m_signaler.notify(); EXPECT_TRUE(m_waiter.timedWait(10_ms)); } TEST_F(ConditionVariable_test, NotifyOnceResultsInBeingTriggered) { - m_signaler.notifyOne(); + m_signaler.notify(); EXPECT_TRUE(m_waiter.wasNotified()); } @@ -76,14 +101,14 @@ TEST_F(ConditionVariable_test, NoNotifyResultsInNotBeingTriggered) TEST_F(ConditionVariable_test, WasTriggerCallDoesNotChangeTheState) { - m_signaler.notifyOne(); + m_signaler.notify(); m_waiter.wasNotified(); EXPECT_TRUE(m_waiter.timedWait(10_ms)); } TEST_F(ConditionVariable_test, NotifyOnceResultsInNoWaitSingleThreaded) { - m_signaler.notifyOne(); + m_signaler.notify(); m_waiter.wait(); // We expect that the next line is reached EXPECT_TRUE(true); @@ -91,8 +116,8 @@ TEST_F(ConditionVariable_test, NotifyOnceResultsInNoWaitSingleThreaded) TEST_F(ConditionVariable_test, NotifyTwiceResultsInNoWaitSingleThreaded) { - m_signaler.notifyOne(); - m_signaler.notifyOne(); + m_signaler.notify(); + m_signaler.notify(); m_waiter.wait(); m_waiter.wait(); // We expect that the next line is reached @@ -110,15 +135,15 @@ TEST_F(ConditionVariable_test, WaitAndNotifyResultsInImmediateTriggerMultiThread }); m_syncSemaphore.wait(); counter++; - m_signaler.notifyOne(); + m_signaler.notify(); waiter.join(); } TEST_F(ConditionVariable_test, ResetResultsInBlockingWaitMultiThreaded) { std::atomic counter{0}; - m_signaler.notifyOne(); - m_waiter.reset(); + m_signaler.notify(); + m_waiter.resetSemaphore(); std::thread waiter([&] { EXPECT_THAT(counter, Eq(0)); m_syncSemaphore.post(); @@ -127,14 +152,14 @@ TEST_F(ConditionVariable_test, ResetResultsInBlockingWaitMultiThreaded) }); m_syncSemaphore.wait(); counter++; - m_signaler.notifyOne(); + m_signaler.notify(); waiter.join(); } TEST_F(ConditionVariable_test, ResetWithoutNotifiyResultsInBlockingWaitMultiThreaded) { std::atomic counter{0}; - m_waiter.reset(); + m_waiter.resetSemaphore(); std::thread waiter([&] { EXPECT_THAT(counter, Eq(0)); m_syncSemaphore.post(); @@ -143,7 +168,7 @@ TEST_F(ConditionVariable_test, ResetWithoutNotifiyResultsInBlockingWaitMultiThre }); m_syncSemaphore.wait(); counter++; - m_signaler.notifyOne(); + m_signaler.notify(); waiter.join(); } @@ -158,6 +183,221 @@ TEST_F(ConditionVariable_test, NotifyWhileWaitingResultsNoTimeoutMultiThreaded) }); m_syncSemaphore.wait(); counter++; - m_signaler.notifyOne(); + m_signaler.notify(); waiter.join(); } + +TEST_F(ConditionVariable_test, AllNotificationsAreFalseAfterConstruction) +{ + ConditionVariableData sut; + for (auto& notification : sut.m_activeNotifications) + { + EXPECT_THAT(notification, Eq(false)); + } +} + +TEST_F(ConditionVariable_test, CorrectProcessNameAfterConstructionWithProcessName) +{ + EXPECT_THAT(m_condVarData.m_process.c_str(), StrEq(m_process)); +} + +TEST_F(ConditionVariable_test, AllNotificationsAreFalseAfterConstructionWithProcessName) +{ + for (auto& notification : m_condVarData.m_activeNotifications) + { + EXPECT_THAT(notification, Eq(false)); + } +} + +TEST_F(ConditionVariable_test, NotifyActivatesCorrectIndex) +{ + constexpr Type_t EVENT_INDEX = iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER - 1U; + ConditionNotifier sut(m_condVarData, EVENT_INDEX); + sut.notify(); + for (Type_t i = 0U; i < iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER; i++) + { + if (i == EVENT_INDEX) + { + EXPECT_THAT(m_condVarData.m_activeNotifications[i], Eq(true)); + } + else + { + EXPECT_THAT(m_condVarData.m_activeNotifications[i], Eq(false)); + } + } +} + +TEST_F(ConditionVariable_test, WaitIsNonBlockingAfterDestroyAndReturnsEmptyVector) +{ + ConditionListener sut(m_condVarData); + Watchdog watchdog(m_timeToWait); + watchdog.watchAndActOnFailure([&] { std::terminate(); }); + + sut.destroy(); + const auto& activeNotifications = sut.waitForNotifications(); + + EXPECT_THAT(activeNotifications.size(), Eq(0U)); +} + +TEST_F(ConditionVariable_test, WaitIsNonBlockingAfterDestroyAndNotifyAndReturnsEmptyVector) +{ + ConditionListener sut(m_condVarData); + Watchdog watchdog(m_timeToWait); + watchdog.watchAndActOnFailure([&] { std::terminate(); }); + sut.destroy(); + + ConditionNotifier notifier(m_condVarData, 0U); + notifier.notify(); + + const auto& activeNotifications = sut.waitForNotifications(); + EXPECT_THAT(activeNotifications.size(), Eq(0U)); +} + +TEST_F(ConditionVariable_test, DestroyWakesUpWaitWhichReturnsEmptyVector) +{ + ConditionListener sut(m_condVarData); + + NotificationVector_t activeNotifications; + + Watchdog watchdog(m_timeToWait); + watchdog.watchAndActOnFailure([] { std::terminate(); }); + + std::thread waiter([&] { + activeNotifications = sut.waitForNotifications(); + EXPECT_THAT(activeNotifications.size(), Eq(0U)); + }); + + sut.destroy(); + waiter.join(); +} + +TEST_F(ConditionVariable_test, GetCorrectNotificationVectorAfterNotifyAndWait) +{ + constexpr Type_t EVENT_INDEX = iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER - 1U; + ConditionNotifier notifier(m_condVarData, EVENT_INDEX); + ConditionListener listener(m_condVarData); + + Watchdog watchdog(m_timeToWait); + watchdog.watchAndActOnFailure([&] { listener.destroy(); }); + + notifier.notify(); + const auto& activeNotifications = listener.waitForNotifications(); + + ASSERT_THAT(activeNotifications.size(), Eq(1U)); + EXPECT_THAT(activeNotifications[0], Eq(EVENT_INDEX)); +} + +TEST_F(ConditionVariable_test, GetCorrectNotificationVectorAfterMultipleNotifyAndWait) +{ + constexpr Type_t FIRST_EVENT_INDEX = iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER - 1U; + constexpr Type_t SECOND_EVENT_INDEX = 0U; + ConditionNotifier notifier1(m_condVarData, FIRST_EVENT_INDEX); + ConditionNotifier notifier2(m_condVarData, SECOND_EVENT_INDEX); + ConditionListener listener(m_condVarData); + + Watchdog watchdog(m_timeToWait); + watchdog.watchAndActOnFailure([&] { listener.destroy(); }); + + notifier1.notify(); + notifier2.notify(); + const auto& activeNotifications = listener.waitForNotifications(); + + ASSERT_THAT(activeNotifications.size(), Eq(2U)); + EXPECT_THAT(activeNotifications[0], Eq(SECOND_EVENT_INDEX)); + EXPECT_THAT(activeNotifications[1], Eq(FIRST_EVENT_INDEX)); +} + +TEST_F(ConditionVariable_test, WaitAndNotifyResultsInCorrectNotificationVector) +{ + constexpr Type_t EVENT_INDEX = iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER - 5U; + ConditionNotifier notifier(m_condVarData, EVENT_INDEX); + ConditionListener listener(m_condVarData); + NotificationVector_t activeNotifications; + + Watchdog watchdog(m_timeToWait); + watchdog.watchAndActOnFailure([&] { listener.destroy(); }); + + std::thread waiter([&] { + activeNotifications = listener.waitForNotifications(); + ASSERT_THAT(activeNotifications.size(), Eq(1U)); + EXPECT_THAT(activeNotifications[0], Eq(EVENT_INDEX)); + }); + + notifier.notify(); + waiter.join(); +} + +TIMING_TEST_F(ConditionVariable_test, WaitBlocks, Repeat(5), [&] { + constexpr Type_t EVENT_INDEX = iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER - 5U; + ConditionNotifier notifier(m_condVarData, EVENT_INDEX); + ConditionListener listener(m_condVarData); + NotificationVector_t activeNotifications; + iox::posix::Semaphore threadSetupSemaphore = + iox::posix::Semaphore::create(iox::posix::CreateUnnamedSingleProcessSemaphore, 0U).value(); + std::atomic_bool hasWaited{false}; + + Watchdog watchdog(m_timeToWait); + watchdog.watchAndActOnFailure([&] { listener.destroy(); }); + + std::thread waiter([&] { + threadSetupSemaphore.post(); + activeNotifications = listener.waitForNotifications(); + hasWaited.store(true, std::memory_order_relaxed); + ASSERT_THAT(activeNotifications.size(), Eq(1U)); + EXPECT_THAT(activeNotifications[0], Eq(EVENT_INDEX)); + }); + + threadSetupSemaphore.wait(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + EXPECT_THAT(hasWaited, Eq(false)); + notifier.notify(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + EXPECT_THAT(hasWaited, Eq(true)); + waiter.join(); +}) + +TIMING_TEST_F(ConditionVariable_test, SecondWaitBlocksUntilNewNotification, Repeat(5), [&] { + constexpr Type_t FIRST_EVENT_INDEX = iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER - 2U; + constexpr Type_t SECOND_EVENT_INDEX = 0U; + ConditionNotifier notifier1(m_condVarData, FIRST_EVENT_INDEX); + ConditionNotifier notifier2(m_condVarData, SECOND_EVENT_INDEX); + ConditionListener listener(m_condVarData); + iox::posix::Semaphore threadSetupSemaphore = + iox::posix::Semaphore::create(iox::posix::CreateUnnamedSingleProcessSemaphore, 0U).value(); + std::atomic_bool hasWaited{false}; + + Watchdog watchdogFirstWait(m_timeToWait); + watchdogFirstWait.watchAndActOnFailure([&] { listener.destroy(); }); + + notifier1.notify(); + notifier2.notify(); + NotificationVector_t activeNotifications = listener.waitForNotifications(); + + ASSERT_THAT(activeNotifications.size(), Eq(2U)); + EXPECT_THAT(activeNotifications[0], Eq(SECOND_EVENT_INDEX)); + EXPECT_THAT(activeNotifications[1], Eq(FIRST_EVENT_INDEX)); + + Watchdog watchdogSecondWait(m_timeToWait); + watchdogSecondWait.watchAndActOnFailure([&] { listener.destroy(); }); + + std::thread waiter([&] { + threadSetupSemaphore.post(); + activeNotifications = listener.waitForNotifications(); + hasWaited.store(true, std::memory_order_relaxed); + ASSERT_THAT(activeNotifications.size(), Eq(1U)); + EXPECT_THAT(activeNotifications[0], Eq(FIRST_EVENT_INDEX)); + for (const auto& notification : m_condVarData.m_activeNotifications) + { + EXPECT_THAT(notification, Eq(false)); + } + }); + + threadSetupSemaphore.wait(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + EXPECT_THAT(hasWaited, Eq(false)); + notifier1.notify(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + EXPECT_THAT(hasWaited, Eq(true)); + waiter.join(); +}) + diff --git a/iceoryx_posh/test/moduletests/test_popo_event_info.cpp b/iceoryx_posh/test/moduletests/test_popo_event_info.cpp index 4181ce8eca..bae3bbbc19 100644 --- a/iceoryx_posh/test/moduletests/test_popo_event_info.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_event_info.cpp @@ -15,7 +15,6 @@ // SPDX-License-Identifier: Apache-2.0 #include "iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/condition_variable_waiter.hpp" #include "iceoryx_posh/popo/event_info.hpp" #include "test.hpp" diff --git a/iceoryx_posh/test/moduletests/test_popo_event_variable.cpp b/iceoryx_posh/test/moduletests/test_popo_event_variable.cpp deleted file mode 100644 index a302a9bb18..0000000000 --- a/iceoryx_posh/test/moduletests/test_popo_event_variable.cpp +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright (c) 2021 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. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -#include "iceoryx_posh/internal/popo/building_blocks/event_listener.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/event_notifier.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/event_variable_data.hpp" - -#include "test.hpp" -#include "testutils/timing_test.hpp" -#include "testutils/watch_dog.hpp" - -#include - -using namespace ::testing; -using namespace iox::popo; -using namespace iox::units::duration_literals; - -class EventVariable_test : public Test -{ - public: - using Type_t = iox::cxx::BestFittingType_t; - using NotificationVector_t = EventListener::NotificationVector_t; - - const iox::ProcessName_t m_process{"Ferdinand"}; - EventVariableData m_eventVarData{m_process}; - - const iox::units::Duration m_timeToWait = 2_s; -}; - -TEST_F(EventVariable_test, AllNotificationsAreFalseAfterConstruction) -{ - EventVariableData sut; - for (auto& notification : sut.m_activeNotifications) - { - EXPECT_THAT(notification, Eq(false)); - } -} - -TEST_F(EventVariable_test, CorrectProcessNameAfterConstructionWithProcessName) -{ - EXPECT_THAT(m_eventVarData.m_process.c_str(), StrEq(m_process)); -} - -TEST_F(EventVariable_test, AllNotificationsAreFalseAfterConstructionWithProcessName) -{ - for (auto& notification : m_eventVarData.m_activeNotifications) - { - EXPECT_THAT(notification, Eq(false)); - } -} - -TEST_F(EventVariable_test, NotifyActivatesCorrectIndex) -{ - constexpr Type_t EVENT_INDEX = iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER - 1U; - EventNotifier sut(m_eventVarData, EVENT_INDEX); - sut.notify(); - for (Type_t i = 0U; i < iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER; i++) - { - if (i == EVENT_INDEX) - { - EXPECT_THAT(m_eventVarData.m_activeNotifications[i], Eq(true)); - } - else - { - EXPECT_THAT(m_eventVarData.m_activeNotifications[i], Eq(false)); - } - } -} - -TEST_F(EventVariable_test, NotifyActivatesNoIndexIfIndexIsTooLarge) -{ - constexpr Type_t EVENT_INDEX = iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER; - EventNotifier sut(m_eventVarData, EVENT_INDEX); - sut.notify(); - for (const auto& notification : m_eventVarData.m_activeNotifications) - { - EXPECT_THAT(notification, Eq(false)); - } -} - -TEST_F(EventVariable_test, WaitIsNonBlockingAfterDestroyAndReturnsEmptyVector) -{ - EventListener sut(m_eventVarData); - Watchdog watchdog(m_timeToWait); - watchdog.watchAndActOnFailure([&] { std::terminate(); }); - - sut.destroy(); - const auto& activeNotifications = sut.wait(); - - EXPECT_THAT(activeNotifications.size(), Eq(0U)); -} - -TEST_F(EventVariable_test, WaitIsNonBlockingAfterDestroyAndNotifyAndReturnsEmptyVector) -{ - EventListener sut(m_eventVarData); - Watchdog watchdog(m_timeToWait); - watchdog.watchAndActOnFailure([&] { std::terminate(); }); - sut.destroy(); - - EventNotifier notifier(m_eventVarData, 0U); - notifier.notify(); - - const auto& activeNotifications = sut.wait(); - EXPECT_THAT(activeNotifications.size(), Eq(0U)); -} - -TEST_F(EventVariable_test, DestroyWakesUpWaitWhichReturnsEmptyVector) -{ - EventListener sut(m_eventVarData); - - NotificationVector_t activeNotifications; - - Watchdog watchdog(m_timeToWait); - watchdog.watchAndActOnFailure([] { std::terminate(); }); - - std::thread waiter([&] { - activeNotifications = sut.wait(); - EXPECT_THAT(activeNotifications.size(), Eq(0U)); - }); - - sut.destroy(); - waiter.join(); -} - -TEST_F(EventVariable_test, GetCorrectNotificationVectorAfterNotifyAndWait) -{ - constexpr Type_t EVENT_INDEX = iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER - 1U; - EventNotifier notifier(m_eventVarData, EVENT_INDEX); - EventListener listener(m_eventVarData); - - Watchdog watchdog(m_timeToWait); - watchdog.watchAndActOnFailure([&] { listener.destroy(); }); - - notifier.notify(); - const auto& activeNotifications = listener.wait(); - - ASSERT_THAT(activeNotifications.size(), Eq(1U)); - EXPECT_THAT(activeNotifications[0], Eq(EVENT_INDEX)); -} - -TEST_F(EventVariable_test, GetCorrectNotificationVectorAfterMultipleNotifyAndWait) -{ - constexpr Type_t FIRST_EVENT_INDEX = iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER - 1U; - constexpr Type_t SECOND_EVENT_INDEX = 0U; - EventNotifier notifier1(m_eventVarData, FIRST_EVENT_INDEX); - EventNotifier notifier2(m_eventVarData, SECOND_EVENT_INDEX); - EventListener listener(m_eventVarData); - - Watchdog watchdog(m_timeToWait); - watchdog.watchAndActOnFailure([&] { listener.destroy(); }); - - notifier1.notify(); - notifier2.notify(); - const auto& activeNotifications = listener.wait(); - - ASSERT_THAT(activeNotifications.size(), Eq(2U)); - EXPECT_THAT(activeNotifications[0], Eq(SECOND_EVENT_INDEX)); - EXPECT_THAT(activeNotifications[1], Eq(FIRST_EVENT_INDEX)); -} - -TEST_F(EventVariable_test, WaitAndNotifyResultsInCorrectNotificationVector) -{ - constexpr Type_t EVENT_INDEX = iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER - 5U; - EventNotifier notifier(m_eventVarData, EVENT_INDEX); - EventListener listener(m_eventVarData); - NotificationVector_t activeNotifications; - - Watchdog watchdog(m_timeToWait); - watchdog.watchAndActOnFailure([&] { listener.destroy(); }); - - std::thread waiter([&] { - activeNotifications = listener.wait(); - ASSERT_THAT(activeNotifications.size(), Eq(1U)); - EXPECT_THAT(activeNotifications[0], Eq(EVENT_INDEX)); - }); - - notifier.notify(); - waiter.join(); -} - -TIMING_TEST_F(EventVariable_test, WaitBlocks, Repeat(5), [&] { - constexpr Type_t EVENT_INDEX = iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER - 5U; - EventNotifier notifier(m_eventVarData, EVENT_INDEX); - EventListener listener(m_eventVarData); - NotificationVector_t activeNotifications; - iox::posix::Semaphore threadSetupSemaphore = - iox::posix::Semaphore::create(iox::posix::CreateUnnamedSingleProcessSemaphore, 0U).value(); - std::atomic_bool hasWaited{false}; - - Watchdog watchdog(m_timeToWait); - watchdog.watchAndActOnFailure([&] { listener.destroy(); }); - - std::thread waiter([&] { - threadSetupSemaphore.post(); - activeNotifications = listener.wait(); - hasWaited.store(true, std::memory_order_relaxed); - ASSERT_THAT(activeNotifications.size(), Eq(1U)); - EXPECT_THAT(activeNotifications[0], Eq(EVENT_INDEX)); - }); - - threadSetupSemaphore.wait(); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - EXPECT_THAT(hasWaited, Eq(false)); - notifier.notify(); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - EXPECT_THAT(hasWaited, Eq(true)); - waiter.join(); -}) - -TIMING_TEST_F(EventVariable_test, SecondWaitBlocksUntilNewNotification, Repeat(5), [&] { - constexpr Type_t FIRST_EVENT_INDEX = iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER - 2U; - constexpr Type_t SECOND_EVENT_INDEX = 0U; - EventNotifier notifier1(m_eventVarData, FIRST_EVENT_INDEX); - EventNotifier notifier2(m_eventVarData, SECOND_EVENT_INDEX); - EventListener listener(m_eventVarData); - iox::posix::Semaphore threadSetupSemaphore = - iox::posix::Semaphore::create(iox::posix::CreateUnnamedSingleProcessSemaphore, 0U).value(); - std::atomic_bool hasWaited{false}; - - Watchdog watchdogFirstWait(m_timeToWait); - watchdogFirstWait.watchAndActOnFailure([&] { listener.destroy(); }); - - notifier1.notify(); - notifier2.notify(); - NotificationVector_t activeNotifications = listener.wait(); - - ASSERT_THAT(activeNotifications.size(), Eq(2U)); - EXPECT_THAT(activeNotifications[0], Eq(SECOND_EVENT_INDEX)); - EXPECT_THAT(activeNotifications[1], Eq(FIRST_EVENT_INDEX)); - - Watchdog watchdogSecondWait(m_timeToWait); - watchdogSecondWait.watchAndActOnFailure([&] { listener.destroy(); }); - - std::thread waiter([&] { - threadSetupSemaphore.post(); - activeNotifications = listener.wait(); - hasWaited.store(true, std::memory_order_relaxed); - ASSERT_THAT(activeNotifications.size(), Eq(1U)); - EXPECT_THAT(activeNotifications[0], Eq(FIRST_EVENT_INDEX)); - for (const auto& notification : m_eventVarData.m_activeNotifications) - { - EXPECT_THAT(notification, Eq(false)); - } - }); - - threadSetupSemaphore.wait(); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - EXPECT_THAT(hasWaited, Eq(false)); - notifier1.notify(); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - EXPECT_THAT(hasWaited, Eq(true)); - waiter.join(); -}) - diff --git a/iceoryx_posh/test/moduletests/test_popo_listener.cpp b/iceoryx_posh/test/moduletests/test_popo_listener.cpp index 7dd90bffc7..84f11f76e5 100644 --- a/iceoryx_posh/test/moduletests/test_popo_listener.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_listener.cpp @@ -128,7 +128,7 @@ class SimpleEventClass class TestListener : public Listener { public: - TestListener(EventVariableData* data) noexcept + TestListener(ConditionVariableData& data) noexcept : Listener(data) { } @@ -202,7 +202,7 @@ class Listener_test : public Test e.m_source = nullptr; e.m_count = 0U; } - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); g_invalidateTriggerId = 0U; g_triggerCallbackRuntimeInMs = 0U; g_toBeAttached->clear(); @@ -256,7 +256,7 @@ class Listener_test : public Test static constexpr uint64_t OVERFLOW_TEST_APPENDIX = 1U; using eventArray_t = SimpleEventClass[iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER + OVERFLOW_TEST_APPENDIX]; eventArray_t m_simpleEvents; - EventVariableData m_eventVarData{"Maulbeerblättle"}; + ConditionVariableData m_condVarData{"Maulbeerblättle"}; iox::cxx::optional m_sut; const iox::units::Duration m_fatalTimeout = 2_s; @@ -525,7 +525,7 @@ TEST_F(Listener_test, DetachingNonAttachedEventResetsNothing) // BEGIN calling callbacks /////////////////////////////////// TIMING_TEST_F(Listener_test, CallbackIsCalledAfterNotify, Repeat(5), [&] { - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); SimpleEventClass fuu; m_sut->attachEvent(fuu, SimpleEvent::StoepselBachelorParty, Listener_test::triggerCallback<0U>); @@ -537,7 +537,7 @@ TIMING_TEST_F(Listener_test, CallbackIsCalledAfterNotify, Repeat(5), [&] { }); TIMING_TEST_F(Listener_test, CallbackIsCalledOnlyOnceWhenTriggered, Repeat(5), [&] { - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); SimpleEventClass fuu1; SimpleEventClass fuu2; m_sut->attachEvent(fuu1, SimpleEvent::StoepselBachelorParty, Listener_test::triggerCallback<0U>); @@ -555,7 +555,7 @@ TIMING_TEST_F(Listener_test, CallbackIsCalledOnlyOnceWhenTriggered, Repeat(5), [ }); TIMING_TEST_F(Listener_test, TriggerWhileInCallbackLeadsToAnotherCallback, Repeat(5), [&] { - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); SimpleEventClass fuu; m_sut->attachEvent(fuu, SimpleEvent::StoepselBachelorParty, Listener_test::triggerCallback<0U>); @@ -575,7 +575,7 @@ TIMING_TEST_F(Listener_test, TriggerWhileInCallbackLeadsToAnotherCallback, Repea }); TIMING_TEST_F(Listener_test, TriggerWhileInCallbackLeadsToAnotherCallbackOnce, Repeat(5), [&] { - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); SimpleEventClass fuu; SimpleEventClass bar; m_sut->attachEvent(fuu, SimpleEvent::StoepselBachelorParty, Listener_test::triggerCallback<0U>); @@ -600,7 +600,7 @@ TIMING_TEST_F(Listener_test, TriggerWhileInCallbackLeadsToAnotherCallbackOnce, R }); TIMING_TEST_F(Listener_test, TriggerMultipleTimesWhileInCallbackLeadsToAnotherCallback, Repeat(5), [&] { - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); SimpleEventClass fuu; m_sut->attachEvent(fuu, SimpleEvent::StoepselBachelorParty, Listener_test::triggerCallback<0U>); @@ -623,7 +623,7 @@ TIMING_TEST_F(Listener_test, TriggerMultipleTimesWhileInCallbackLeadsToAnotherCa }); TIMING_TEST_F(Listener_test, TriggerMultipleTimesWhileInCallbackLeadsToAnotherCallbackOnce, Repeat(5), [&] { - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); SimpleEventClass fuu; SimpleEventClass bar; m_sut->attachEvent(fuu, SimpleEvent::StoepselBachelorParty, Listener_test::triggerCallback<0U>); @@ -651,7 +651,7 @@ TIMING_TEST_F(Listener_test, TriggerMultipleTimesWhileInCallbackLeadsToAnotherCa }); TIMING_TEST_F(Listener_test, NoTriggerLeadsToNoCallback, Repeat(5), [&] { - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); SimpleEventClass fuu; m_sut->attachEvent(fuu, SimpleEvent::StoepselBachelorParty, Listener_test::triggerCallback<0U>); @@ -662,7 +662,7 @@ TIMING_TEST_F(Listener_test, NoTriggerLeadsToNoCallback, Repeat(5), [&] { }); TIMING_TEST_F(Listener_test, TriggeringAllEventsCallsAllCallbacks, Repeat(5), [&] { - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER); AttachEvent::doIt(*m_sut, events, SimpleEvent::StoepselBachelorParty); @@ -691,7 +691,7 @@ TIMING_TEST_F(Listener_test, TriggeringAllEventsCallsAllCallbacks, Repeat(5), [& }); TIMING_TEST_F(Listener_test, TriggeringAllEventsCallsAllCallbacksOnce, Repeat(5), [&] { - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER); AttachEvent::doIt(*m_sut, events, SimpleEvent::StoepselBachelorParty); @@ -729,7 +729,7 @@ TIMING_TEST_F(Listener_test, TriggeringAllEventsCallsAllCallbacksOnce, Repeat(5) // BEGIN concurrent attach / detach ////////////////////////////////// TIMING_TEST_F(Listener_test, AttachingWhileCallbackIsRunningWorks, Repeat(5), [&] { - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER); m_sut->attachEvent(events[0U], SimpleEvent::StoepselBachelorParty, triggerCallback<0U>); @@ -747,7 +747,7 @@ TIMING_TEST_F(Listener_test, AttachingWhileCallbackIsRunningWorks, Repeat(5), [& }); TIMING_TEST_F(Listener_test, AttachingMultipleWhileCallbackIsRunningWorks, Repeat(5), [&] { - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER); m_sut->attachEvent(events[iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER - 1U], @@ -775,7 +775,7 @@ TIMING_TEST_F(Listener_test, AttachingMultipleWhileCallbackIsRunningWorks, Repea }); TIMING_TEST_F(Listener_test, DetachingWhileCallbackIsRunningWorks, Repeat(5), [&] { - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER); m_sut->attachEvent(events[0U], SimpleEvent::StoepselBachelorParty, triggerCallback<0U>); @@ -794,7 +794,7 @@ TIMING_TEST_F(Listener_test, DetachingWhileCallbackIsRunningWorks, Repeat(5), [& }); TIMING_TEST_F(Listener_test, DetachingWhileCallbackIsRunningBlocksDetach, Repeat(5), [&] { - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER); m_sut->attachEvent(events[0U], SimpleEvent::StoepselBachelorParty, triggerCallback<0U>); g_triggerCallbackRuntimeInMs = 3U * CALLBACK_WAIT_IN_MS / 2U; @@ -810,7 +810,7 @@ TIMING_TEST_F(Listener_test, DetachingWhileCallbackIsRunningBlocksDetach, Repeat }); TIMING_TEST_F(Listener_test, EventDestructorBlocksWhenCallbackIsRunning, Repeat(5), [&] { - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); SimpleEventClass* event = new SimpleEventClass(); m_sut->attachEvent(*event, SimpleEvent::StoepselBachelorParty, triggerCallback<0U>); g_triggerCallbackRuntimeInMs = 3U * CALLBACK_WAIT_IN_MS / 2U; @@ -827,7 +827,7 @@ TIMING_TEST_F(Listener_test, EventDestructorBlocksWhenCallbackIsRunning, Repeat( TIMING_TEST_F(Listener_test, DetachingMultipleWhileCallbackIsRunningWorks, Repeat(5), [&] { - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER); AttachEvent::doIt(*m_sut, events, SimpleEvent::StoepselBachelorParty); @@ -859,7 +859,7 @@ TIMING_TEST_F(Listener_test, DetachingMultipleWhileCallbackIsRunningWorks, Repea }); TIMING_TEST_F(Listener_test, AttachingDetachingRunsIndependentOfCallback, Repeat(5), [&] { - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER); m_sut->attachEvent(events[iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER - 1U], SimpleEvent::StoepselBachelorParty, @@ -887,7 +887,7 @@ TIMING_TEST_F(Listener_test, AttachingDetachingRunsIndependentOfCallback, Repeat // BEGIN attach / detach in callbacks ////////////////////////////////// TIMING_TEST_F(Listener_test, DetachingSelfInCallbackWorks, Repeat(5), [&] { - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); g_toBeDetached->clear(); std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER); @@ -902,7 +902,7 @@ TIMING_TEST_F(Listener_test, DetachingSelfInCallbackWorks, Repeat(5), [&] { }); TIMING_TEST_F(Listener_test, DetachingNonSelfEventInCallbackWorks, Repeat(5), [&] { - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); g_toBeDetached->clear(); std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER); @@ -928,7 +928,7 @@ TIMING_TEST_F(Listener_test, DetachedCallbacksAreNotBeingCalledWhenTriggeredBefo // running while we retrigger events[0] and events[1]. // Now events[0] remove events[1] before its trigger callback is executed and therefore the // callback is not allowed to be called even so that the trigger came before the detach occurred - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); g_toBeDetached->clear(); std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER); @@ -954,7 +954,7 @@ TIMING_TEST_F(Listener_test, DetachedCallbacksAreNotBeingCalledWhenTriggeredBefo }); TIMING_TEST_F(Listener_test, AttachingInCallbackWorks, Repeat(5), [&] { - m_sut.emplace(&m_eventVarData); + m_sut.emplace(m_condVarData); g_toBeAttached->clear(); std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_LISTENER); diff --git a/iceoryx_posh/test/moduletests/test_popo_publisher_port.cpp b/iceoryx_posh/test/moduletests/test_popo_publisher_port.cpp index 5434dfa828..a6ac715833 100644 --- a/iceoryx_posh/test/moduletests/test_popo_publisher_port.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_publisher_port.cpp @@ -73,7 +73,7 @@ class PublisherPort_test : public Test iox::posix::Allocator m_memoryAllocator{m_memory, MEMORY_SIZE}; iox::mepoo::MePooConfig m_mempoolconf; iox::mepoo::MemoryManager m_memoryManager; - iox::popo::PublisherOptions m_noOfferOnCreatePublisherOptions{0U, "", false}; + iox::popo::PublisherOptions m_noOfferOnCreatePublisherOptions{0U, iox::NodeName_t{""}, false}; ; // publisher port w/o offer on create @@ -83,7 +83,7 @@ class PublisherPort_test : public Test iox::popo::PublisherPortUser m_sutNoOfferOnCreateUserSide{&m_publisherPortData}; // publisher port w/ history - iox::popo::PublisherOptions m_withHistoryPublisherOptions{iox::MAX_PUBLISHER_HISTORY, "", true}; + iox::popo::PublisherOptions m_withHistoryPublisherOptions{iox::MAX_PUBLISHER_HISTORY, iox::NodeName_t{""}, true}; iox::popo::PublisherPortData m_publisherPortDataHistory{ iox::capro::ServiceDescription("x", "y", "z"), "myApp", &m_memoryManager, m_withHistoryPublisherOptions}; iox::popo::PublisherPortUser m_sutWithHistoryUserSide{&m_publisherPortDataHistory}; diff --git a/iceoryx_posh/test/moduletests/test_popo_subscriber_port.cpp b/iceoryx_posh/test/moduletests/test_popo_subscriber_port.cpp index 2b47602d33..163a72c4f2 100644 --- a/iceoryx_posh/test/moduletests/test_popo_subscriber_port.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_subscriber_port.cpp @@ -60,7 +60,7 @@ class SubscriberPortSingleProducer_test : public Test [] { iox::popo::internal::unsetUniqueRouDiId(); }}; iox::popo::SubscriberOptions m_noSubscribeOnCreateOptions{ - iox::popo::SubscriberPortData::ChunkQueueData_t::MAX_CAPACITY, 0U, "", false}; + iox::popo::SubscriberPortData::ChunkQueueData_t::MAX_CAPACITY, 0U, iox::NodeName_t(""), false}; iox::popo::SubscriberPortData m_subscriberPortDataSingleProducer{ TEST_SERVICE_DESCRIPTION, "myApp", diff --git a/iceoryx_posh/test/moduletests/test_popo_trigger.cpp b/iceoryx_posh/test/moduletests/test_popo_trigger.cpp index 369ec4c0d0..2fa2d2227b 100644 --- a/iceoryx_posh/test/moduletests/test_popo_trigger.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_trigger.cpp @@ -15,7 +15,6 @@ // SPDX-License-Identifier: Apache-2.0 #include "iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/condition_variable_waiter.hpp" #include "iceoryx_posh/popo/trigger.hpp" #include "test.hpp" @@ -70,11 +69,13 @@ class Trigger_test : public Test Trigger createValidTrigger(const uint64_t eventId = 0U) { + static uint64_t uniqueId = 0U; return Trigger(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, eventId, - TriggerClass::callback); + TriggerClass::callback, + uniqueId++); } ConditionVariableData m_condVar{"Horscht"}; @@ -83,15 +84,6 @@ class Trigger_test : public Test Trigger_test::TriggerClass* Trigger_test::TriggerClass::m_lastCallbackArgument = nullptr; -TEST_F(Trigger_test, DefaultCTorConstructsEmptyTrigger) -{ - Trigger sut; - - EXPECT_EQ(static_cast(sut), false); - EXPECT_EQ(sut.isValid(), false); - EXPECT_EQ(sut.hasTriggered(), false); -} - TEST_F(Trigger_test, TriggerWithValidOriginIsValid) { Trigger sut = createValidTrigger(); @@ -111,7 +103,7 @@ TEST_F(Trigger_test, MovedConstructedValidTriggerIsValid) TEST_F(Trigger_test, MovedAssignedValidTriggerIsValid) { - Trigger sut; + Trigger sut = createValidTrigger(); Trigger trigger = createValidTrigger(); sut = std::move(trigger); @@ -130,7 +122,7 @@ TEST_F(Trigger_test, MovedConstructedOriginIsInvalidTriggerAfterMove) TEST_F(Trigger_test, MovedAssignedOriginIsInvalidTriggerAfterMove) { - Trigger sut; + Trigger sut = createValidTrigger(); Trigger trigger = createValidTrigger(); sut = std::move(trigger); @@ -140,12 +132,14 @@ TEST_F(Trigger_test, MovedAssignedOriginIsInvalidTriggerAfterMove) TEST_F(Trigger_test, TriggerWithNullptrOriginIsValid) { - uint64_t eventId = 0U; + const uint64_t eventId = 0U; + const uint64_t uniqueTriggerId = 0U; Trigger sut(static_cast(nullptr), {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, eventId, - TriggerClass::callback); + TriggerClass::callback, + uniqueTriggerId); EXPECT_TRUE(sut.isValid()); EXPECT_TRUE(static_cast(sut)); @@ -153,12 +147,14 @@ TEST_F(Trigger_test, TriggerWithNullptrOriginIsValid) TEST_F(Trigger_test, TriggerWithInvalidHasTriggeredCallbackIsInvalid) { - uint64_t eventId = 0U; + const uint64_t eventId = 0U; + const uint64_t uniqueTriggerId = 0U; Trigger sut(&m_triggerClass, cxx::ConstMethodCallback(), {m_triggerClass, &TriggerClass::resetCall}, eventId, - TriggerClass::callback); + TriggerClass::callback, + uniqueTriggerId); EXPECT_FALSE(sut.isValid()); EXPECT_FALSE(static_cast(sut)); @@ -166,12 +162,14 @@ TEST_F(Trigger_test, TriggerWithInvalidHasTriggeredCallbackIsInvalid) TEST_F(Trigger_test, TriggerWithEmptyResetCallIsValid) { - uint64_t eventId = 0U; + const uint64_t eventId = 0U; + const uint64_t uniqueTriggerId = 0U; Trigger sut(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, cxx::MethodCallback(), eventId, - TriggerClass::callback); + TriggerClass::callback, + uniqueTriggerId); EXPECT_TRUE(sut.isValid()); EXPECT_TRUE(static_cast(sut)); @@ -214,12 +212,14 @@ TEST_F(Trigger_test, ResetSetsTriggerIdToInvalid) TEST_F(Trigger_test, TriggerWithEmptyResetInvalidatesTriggerWhenBeingResetted) { - uint64_t eventId = 0U; + const uint64_t eventId = 0U; + const uint64_t uniqueTriggerId = 0U; Trigger sut(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, cxx::MethodCallback(), eventId, - TriggerClass::callback); + TriggerClass::callback, + uniqueTriggerId); sut.reset(); @@ -262,12 +262,14 @@ TEST_F(Trigger_test, UpdateOriginLeadsToDifferentHasTriggeredCallback) TEST_F(Trigger_test, UpdateOriginDoesNotUpdateHasTriggeredIfItsNotOriginatingFromOrigin) { constexpr uint64_t USER_DEFINED_EVENT_ID = 891U; + const uint64_t uniqueTriggerId = 0U; TriggerClass secondTriggerClass, thirdTriggerClass; Trigger sut(&m_triggerClass, {thirdTriggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, USER_DEFINED_EVENT_ID, - TriggerClass::callback); + TriggerClass::callback, + uniqueTriggerId); sut.updateOrigin(&secondTriggerClass); @@ -292,12 +294,14 @@ TEST_F(Trigger_test, UpdateOriginLeadsToDifferentResetCallback) TEST_F(Trigger_test, UpdateOriginDoesNotUpdateResetIfItsNotOriginatingFromOrigin) { constexpr uint64_t USER_DEFINED_EVENT_ID = 892U; + const uint64_t uniqueTriggerId = 0U; TriggerClass secondTriggerClass, thirdTriggerClass; Trigger sut(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {thirdTriggerClass, &TriggerClass::resetCall}, USER_DEFINED_EVENT_ID, - TriggerClass::callback); + TriggerClass::callback, + uniqueTriggerId); sut.updateOrigin(&secondTriggerClass); auto uniqueId = sut.getUniqueId(); @@ -309,12 +313,14 @@ TEST_F(Trigger_test, UpdateOriginDoesNotUpdateResetIfItsNotOriginatingFromOrigin TEST_F(Trigger_test, UpdateOriginUpdatesOriginOfEventInfo) { constexpr uint64_t USER_DEFINED_EVENT_ID = 893U; + const uint64_t uniqueTriggerId = 0U; TriggerClass secondTriggerClass; Trigger sut(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, USER_DEFINED_EVENT_ID, - TriggerClass::callback); + TriggerClass::callback, + uniqueTriggerId); sut.updateOrigin(&secondTriggerClass); EXPECT_TRUE(sut.getEventInfo().doesOriginateFrom(&secondTriggerClass)); @@ -326,11 +332,13 @@ TEST_F(Trigger_test, UpdateOriginUpdatesOriginOfEventInfo) TEST_F(Trigger_test, TriggerIsLogicalEqualToItself) { constexpr uint64_t USER_DEFINED_EVENT_ID = 894U; + const uint64_t uniqueTriggerId = 0U; Trigger sut1(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, USER_DEFINED_EVENT_ID, - TriggerClass::callback); + TriggerClass::callback, + uniqueTriggerId); EXPECT_TRUE(sut1.isLogicalEqualTo(sut1)); } @@ -339,17 +347,21 @@ TEST_F(Trigger_test, TwoTriggersAreLogicalEqualIfRequirementsAreFullfilled) { constexpr uint64_t USER_DEFINED_EVENT_ID = 896U; constexpr uint64_t ANOTHER_USER_DEFINED_EVENT_ID = 8961U; + const uint64_t uniqueTriggerId1 = 0U; + const uint64_t uniqueTriggerId2 = 1U; Trigger sut1(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, USER_DEFINED_EVENT_ID, - TriggerClass::callback); + TriggerClass::callback, + uniqueTriggerId1); Trigger sut2(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, ANOTHER_USER_DEFINED_EVENT_ID, - TriggerClass::callback); + TriggerClass::callback, + uniqueTriggerId2); EXPECT_TRUE(sut1.isLogicalEqualTo(sut2)); @@ -360,17 +372,21 @@ TEST_F(Trigger_test, TwoTriggersAreLogicalEqualIfOnlyTriggerIdDiffers) { constexpr uint64_t USER_DEFINED_EVENT_ID = 2896U; constexpr uint64_t ANOTHER_USER_DEFINED_EVENT_ID = 28961U; + const uint64_t uniqueTriggerId1 = 0U; + const uint64_t uniqueTriggerId2 = 1U; Trigger sut1(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, USER_DEFINED_EVENT_ID, - TriggerClass::callback); + TriggerClass::callback, + uniqueTriggerId1); Trigger sut2(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, ANOTHER_USER_DEFINED_EVENT_ID, - TriggerClass::callback); + TriggerClass::callback, + uniqueTriggerId2); EXPECT_TRUE(sut1.isLogicalEqualTo(sut2)); @@ -381,18 +397,22 @@ TEST_F(Trigger_test, TwoTriggersAreNotLogicalEqualIfHasTriggeredCallbackDiffers) { constexpr uint64_t USER_DEFINED_EVENT_ID = 4896U; constexpr uint64_t ANOTHER_USER_DEFINED_EVENT_ID = 48961U; + const uint64_t uniqueTriggerId1 = 0U; + const uint64_t uniqueTriggerId2 = 1U; TriggerClass secondTriggerClass; Trigger sut1(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, USER_DEFINED_EVENT_ID, - TriggerClass::callback); + TriggerClass::callback, + uniqueTriggerId1); Trigger sut2(&m_triggerClass, {secondTriggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, ANOTHER_USER_DEFINED_EVENT_ID, - TriggerClass::callback); + TriggerClass::callback, + uniqueTriggerId2); EXPECT_FALSE(sut1.isLogicalEqualTo(sut2)); @@ -402,18 +422,22 @@ TEST_F(Trigger_test, TwoTriggersAreNotLogicalEqualIfHasTriggeredCallbackDiffers) TEST_F(Trigger_test, TwoTriggersAreNotLogicalEqualIfOriginDiffers) { constexpr uint64_t USER_DEFINED_EVENT_ID = 4896U; + const uint64_t uniqueTriggerId1 = 0U; + const uint64_t uniqueTriggerId2 = 1U; TriggerClass secondTriggerClass; Trigger sut1(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, USER_DEFINED_EVENT_ID, - TriggerClass::callback); + TriggerClass::callback, + uniqueTriggerId1); Trigger sut2(&secondTriggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, USER_DEFINED_EVENT_ID, - TriggerClass::callback); + TriggerClass::callback, + uniqueTriggerId2); EXPECT_FALSE(sut1.isLogicalEqualTo(sut2)); diff --git a/iceoryx_posh/test/moduletests/test_popo_trigger_handle.cpp b/iceoryx_posh/test/moduletests/test_popo_trigger_handle.cpp index 75ab875290..109dd7d1f2 100644 --- a/iceoryx_posh/test/moduletests/test_popo_trigger_handle.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_trigger_handle.cpp @@ -14,8 +14,8 @@ // // SPDX-License-Identifier: Apache-2.0 +#include "iceoryx_posh/internal/popo/building_blocks/condition_listener.hpp" #include "iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/condition_variable_waiter.hpp" #include "iceoryx_posh/popo/trigger_handle.hpp" #include "test.hpp" @@ -104,7 +104,7 @@ TEST_F(TriggerHandle_test, triggerNotifiesConditionVariable) std::atomic_int stage{0}; std::thread t([&] { - ConditionVariableWaiter(&m_condVar).wait(); + ConditionListener(m_condVar).wait(); stage.store(1); }); diff --git a/iceoryx_posh/test/moduletests/test_popo_user_trigger.cpp b/iceoryx_posh/test/moduletests/test_popo_user_trigger.cpp index 22e45f3c6c..dc0b37b6f4 100644 --- a/iceoryx_posh/test/moduletests/test_popo_user_trigger.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_user_trigger.cpp @@ -31,8 +31,8 @@ class UserTrigger_test : public Test UserTrigger m_sut; ConditionVariableData m_condVar{"Horscht"}; ConditionVariableData m_condVar2{"Schnuppi"}; - WaitSetMock m_waitSet{&m_condVar}; - WaitSetMock m_waitSet2{&m_condVar2}; + WaitSetMock m_waitSet{m_condVar}; + WaitSetMock m_waitSet2{m_condVar2}; void SetUp() { diff --git a/iceoryx_posh/test/moduletests/test_popo_waitset.cpp b/iceoryx_posh/test/moduletests/test_popo_waitset.cpp index 40b17adab6..84364e0221 100644 --- a/iceoryx_posh/test/moduletests/test_popo_waitset.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_waitset.cpp @@ -102,7 +102,7 @@ class WaitSet_test : public Test }; ConditionVariableData m_condVarData{"Horscht"}; - WaitSetMock m_sut{&m_condVarData}; + WaitSetMock m_sut{m_condVarData}; static void triggerCallback1(WaitSet_test::SimpleEventClass* const waitset) { @@ -199,7 +199,7 @@ TEST_F(WaitSet_test, ResetCallbackIsCalledWhenWaitsetGoesOutOfScope) uint64_t uniqueTriggerId = 0U; SimpleEventClass simpleEvent; { - WaitSetMock sut{&m_condVarData}; + WaitSetMock sut{m_condVarData}; constexpr uint64_t USER_DEFINED_EVENT_ID = 421337U; sut.attachEvent(simpleEvent, USER_DEFINED_EVENT_ID); uniqueTriggerId = simpleEvent.getUniqueId(); diff --git a/iceoryx_posh/test/moduletests/test_posh_runtime.cpp b/iceoryx_posh/test/moduletests/test_posh_runtime.cpp index 7ce15e6163..d2cb891e10 100644 --- a/iceoryx_posh/test/moduletests/test_posh_runtime.cpp +++ b/iceoryx_posh/test/moduletests/test_posh_runtime.cpp @@ -502,45 +502,6 @@ TEST_F(PoshRuntime_test, GetMiddlewareConditionVariableListOverflow) EXPECT_TRUE(conditionVariableListOverflowDetected); } -TEST_F(PoshRuntime_test, GetMiddlewareEventVariableIsSuccessful) -{ - auto eventVariable = m_runtime->getMiddlewareEventVariable(); - EXPECT_THAT(eventVariable, Ne(nullptr)); -} - -TEST_F(PoshRuntime_test, GetMaxNumberOfMiddlewareEventVariablesIsSuccessful) -{ - for (uint32_t i = 0U; i < iox::MAX_NUMBER_OF_EVENT_VARIABLES; ++i) - { - auto eventVariable = m_runtime->getMiddlewareEventVariable(); - EXPECT_THAT(eventVariable, Ne(nullptr)); - } -} - -TEST_F(PoshRuntime_test, GetMiddlewareEventVariableListOverflow) -{ - auto eventVariableListOverflowDetected{false}; - auto errorHandlerGuard = iox::ErrorHandler::SetTemporaryErrorHandler( - [&eventVariableListOverflowDetected]( - const iox::Error error, const std::function, const iox::ErrorLevel) { - if (error == iox::Error::kPORT_POOL__EVENT_VARIABLE_LIST_OVERFLOW) - { - eventVariableListOverflowDetected = true; - } - }); - - for (uint32_t i = 0U; i < iox::MAX_NUMBER_OF_EVENT_VARIABLES; ++i) - { - auto eventVariable = m_runtime->getMiddlewareEventVariable(); - ASSERT_THAT(eventVariable, Ne(nullptr)); - } - EXPECT_THAT(eventVariableListOverflowDetected, Eq(false)); - - auto eventVariable = m_runtime->getMiddlewareEventVariable(); - EXPECT_THAT(eventVariable, Eq(nullptr)); - EXPECT_THAT(eventVariableListOverflowDetected, Eq(true)); -} - TIMING_TEST_F(PoshRuntime_test, GetServiceRegistryChangeCounterOfferStopOfferService, Repeat(5), [&] { auto serviceCounter = m_runtime->getServiceRegistryChangeCounter(); auto initialCout = serviceCounter->load(); diff --git a/iceoryx_posh/test/moduletests/test_roudi_portmanager.cpp b/iceoryx_posh/test/moduletests/test_roudi_portmanager.cpp index 67e7475524..95f716df49 100644 --- a/iceoryx_posh/test/moduletests/test_roudi_portmanager.cpp +++ b/iceoryx_posh/test/moduletests/test_roudi_portmanager.cpp @@ -126,22 +126,6 @@ class PortManager_test : public Test iox::cxx::GenericRAII m_uniqueRouDiId{[] { iox::popo::internal::setUniqueRouDiId(0); }, [] { iox::popo::internal::unsetUniqueRouDiId(); }}; - void acquireMaxNumberOfEventVariables( - const std::string& process, - std::function f = std::function()) - { - for (unsigned int i = 0U; i < iox::MAX_NUMBER_OF_EVENT_VARIABLES; i++) - { - iox::ProcessName_t newProcessName(iox::cxx::TruncateToCapacity, process + std::to_string(i)); - auto eventVariableDataResult = m_portManager->acquireEventVariableData(newProcessName); - ASSERT_THAT(eventVariableDataResult.has_error(), Eq(false)); - if (f) - { - f(eventVariableDataResult.value()); - } - } - } - void acquireMaxNumberOfInterfaces( std::string processName, std::function f = std::function()) @@ -225,8 +209,8 @@ void setDestroyFlagAndClearContainer(vector& container) TEST_F(PortManager_test, DoDiscoveryWithSingleShotPublisherFirst) { - PublisherOptions publisherOptions{1U, "node", false}; - SubscriberOptions subscriberOptions{1U, 1U, "node", false}; + PublisherOptions publisherOptions{1U, iox::NodeName_t("node"), false}; + SubscriberOptions subscriberOptions{1U, 1U, iox::NodeName_t("node"), false}; PublisherPortUser publisher( m_portManager @@ -250,8 +234,8 @@ TEST_F(PortManager_test, DoDiscoveryWithSingleShotPublisherFirst) TEST_F(PortManager_test, DoDiscoveryWithSingleShotSubscriberFirst) { - PublisherOptions publisherOptions{1U, "node", false}; - SubscriberOptions subscriberOptions{1U, 1U, "node", false}; + PublisherOptions publisherOptions{1U, iox::NodeName_t("node"), false}; + SubscriberOptions subscriberOptions{1U, 1U, iox::NodeName_t("node"), false}; SubscriberPortUser subscriber( m_portManager->acquireSubscriberPortData({1U, 1U, 1U}, subscriberOptions, "schlomo", PortConfigInfo()).value()); @@ -275,8 +259,8 @@ TEST_F(PortManager_test, DoDiscoveryWithSingleShotSubscriberFirst) TEST_F(PortManager_test, DoDiscoveryWithDiscoveryLoopInBetweenCreationOfSubscriberAndPublisher) { - PublisherOptions publisherOptions{1U, "node", false}; - SubscriberOptions subscriberOptions{1U, 1U, "node", false}; + PublisherOptions publisherOptions{1U, iox::NodeName_t("node"), false}; + SubscriberOptions subscriberOptions{1U, 1U, iox::NodeName_t("node"), false}; SubscriberPortUser subscriber( m_portManager->acquireSubscriberPortData({1U, 1U, 1U}, subscriberOptions, "schlomo", PortConfigInfo()).value()); @@ -300,8 +284,8 @@ TEST_F(PortManager_test, DoDiscoveryWithDiscoveryLoopInBetweenCreationOfSubscrib TEST_F(PortManager_test, DoDiscoveryWithSubscribersCreatedBeforeAndAfterCreationOfPublisher) { - PublisherOptions publisherOptions{1U, "node", false}; - SubscriberOptions subscriberOptions{1U, 1U, "node", false}; + PublisherOptions publisherOptions{1U, iox::NodeName_t("node"), false}; + SubscriberOptions subscriberOptions{1U, 1U, iox::NodeName_t("node"), false}; SubscriberPortUser subscriber1( m_portManager->acquireSubscriberPortData({1U, 1U, 1U}, subscriberOptions, "schlomo", PortConfigInfo()).value()); @@ -332,8 +316,8 @@ TEST_F(PortManager_test, DoDiscoveryWithSubscribersCreatedBeforeAndAfterCreation TEST_F(PortManager_test, SubscribeOnCreateSubscribesWithoutDiscoveryLoopWhenPublisherAvailable) { - PublisherOptions publisherOptions{1U, "node", false}; - SubscriberOptions subscriberOptions{1U, 1U, "node", true}; + PublisherOptions publisherOptions{1U, iox::NodeName_t("node"), false}; + SubscriberOptions subscriberOptions{1U, 1U, iox::NodeName_t("node"), true}; PublisherPortUser publisher( m_portManager ->acquirePublisherPortData( @@ -351,8 +335,8 @@ TEST_F(PortManager_test, SubscribeOnCreateSubscribesWithoutDiscoveryLoopWhenPubl TEST_F(PortManager_test, OfferOnCreateSubscribesWithoutDiscoveryLoopWhenSubscriberAvailable) { - PublisherOptions publisherOptions{1U, "node", true}; - SubscriberOptions subscriberOptions{1U, 1U, "node", false}; + PublisherOptions publisherOptions{1U, iox::NodeName_t("node"), true}; + SubscriberOptions subscriberOptions{1U, 1U, iox::NodeName_t("node"), false}; SubscriberPortUser subscriber( m_portManager->acquireSubscriberPortData({1U, 1U, 1U}, subscriberOptions, "schlomo", PortConfigInfo()).value()); subscriber.subscribe(); @@ -370,8 +354,8 @@ TEST_F(PortManager_test, OfferOnCreateSubscribesWithoutDiscoveryLoopWhenSubscrib TEST_F(PortManager_test, OfferOnCreateAndSubscribeOnCreateNeedsNoMoreDiscoveryLoopSubscriberFirst) { - PublisherOptions publisherOptions{1U, "node", true}; - SubscriberOptions subscriberOptions{1U, 1U, "node", true}; + PublisherOptions publisherOptions{1U, iox::NodeName_t("node"), true}; + SubscriberOptions subscriberOptions{1U, 1U, iox::NodeName_t("node"), true}; SubscriberPortUser subscriber( m_portManager->acquireSubscriberPortData({1U, 1U, 1U}, subscriberOptions, "schlomo", PortConfigInfo()).value()); @@ -387,8 +371,8 @@ TEST_F(PortManager_test, OfferOnCreateAndSubscribeOnCreateNeedsNoMoreDiscoveryLo TEST_F(PortManager_test, OfferOnCreateAndSubscribeOnCreateNeedsNoMoreDiscoveryLoopPublisherFirst) { - PublisherOptions publisherOptions{1U, "node", true}; - SubscriberOptions subscriberOptions{1U, 1U, "node", true}; + PublisherOptions publisherOptions{1U, iox::NodeName_t("node"), true}; + SubscriberOptions subscriberOptions{1U, 1U, iox::NodeName_t("node"), true}; PublisherPortUser publisher( m_portManager ->acquirePublisherPortData( @@ -407,7 +391,7 @@ TEST_F(PortManager_test, OfferOnCreateAndSubscribeOnCreateNeedsNoMoreDiscoveryLo TEST_F(PortManager_test, AcquiringOneMoreThanMaximumNumberOfPublishersFails) { iox::ProcessName_t processName = "test1"; - PublisherOptions publisherOptions{1U, "run1"}; + PublisherOptions publisherOptions{1U, iox::NodeName_t("run1")}; for (unsigned int i = 0; i < iox::MAX_PUBLISHERS; i++) { @@ -436,7 +420,7 @@ TEST_F(PortManager_test, AcquiringOneMoreThanMaximumNumberOfPublishersFails) TEST_F(PortManager_test, AcquiringOneMoreThanMaximumNumberOfSubscribersFails) { iox::ProcessName_t processName1 = "test1"; - SubscriberOptions subscriberOptions{1U, 1U, "run1"}; + SubscriberOptions subscriberOptions{1U, 1U, iox::NodeName_t("run1")}; for (unsigned int i = 0; i < iox::MAX_SUBSCRIBERS; i++) { @@ -633,69 +617,10 @@ TEST_F(PortManager_test, AcquireConditionVariablesDataAfterDestroyingPreviouslyA acquireMaxNumberOfConditionVariables(processName); } -TEST_F(PortManager_test, AcquiringMaximumNumberOfEventVariablesWorks) -{ - std::string process = "BuddyHolly"; - - acquireMaxNumberOfEventVariables(process); -} - -TEST_F(PortManager_test, AcquiringOneMoreThanMaximumNumberOfEventVariableFails) -{ - std::string process = "BuddyHollysBrille"; - - // first acquire all possible event variables - acquireMaxNumberOfEventVariables(process); - - // test if overflow errors get hit - auto errorHandlerCalled{false}; - auto errorHandlerGuard = iox::ErrorHandler::SetTemporaryErrorHandler( - [&errorHandlerCalled](const iox::Error, const std::function, const iox::ErrorLevel) { - errorHandlerCalled = true; - }); - - auto eventVariableDataResult = m_portManager->acquireEventVariableData("AnotherBrille"); - EXPECT_THAT(eventVariableDataResult.has_error(), Eq(true)); - EXPECT_THAT(errorHandlerCalled, Eq(true)); - EXPECT_THAT(eventVariableDataResult.get_error(), Eq(PortPoolError::EVENT_VARIABLE_LIST_FULL)); -} - -TEST_F(PortManager_test, DeletingEventVariableWorks) -{ - std::string process = "BudSpencer"; - - // first acquire all possible event variables - acquireMaxNumberOfEventVariables(process); - - // delete one and add one eventVariableDataResult should be possible now - unsigned int i = 0U; - iox::ProcessName_t newProcessName(iox::cxx::TruncateToCapacity, process + std::to_string(i)); - m_portManager->deletePortsOfProcess(newProcessName); - - auto eventVariableDataResult = m_portManager->acquireEventVariableData(newProcessName); - EXPECT_THAT(eventVariableDataResult.has_error(), Eq(false)); -} - -TEST_F(PortManager_test, DestroyEventVariableAndAddNewOneSucceeds) -{ - iox::ProcessName_t process = "Terence Hill"; - std::vector eventVariableContainer; - - // first acquire all possible event variables - acquireMaxNumberOfEventVariables( - process, [&](auto eventVariableData) { eventVariableContainer.push_back(eventVariableData); }); - - setDestroyFlagAndClearContainer(eventVariableContainer); - m_portManager->doDiscovery(); - - // we should be able to get some more now - acquireMaxNumberOfEventVariables(process); -} - TEST_F(PortManager_test, AcquiringMaximumNumberOfNodesWorks) { std::string processName = "Process"; - std::string nodeName = "Node"; + std::string nodeName = iox::NodeName_t("node"); acquireMaxNumberOfNodes(nodeName, processName, [&](auto node, auto newNodeName, auto newProcessName) { EXPECT_THAT(node->m_node, StrEq(newNodeName)); @@ -706,7 +631,7 @@ TEST_F(PortManager_test, AcquiringMaximumNumberOfNodesWorks) TEST_F(PortManager_test, AcquiringOneMoreThanMaximumNumberOfNodesFails) { std::string processName = "Process"; - std::string nodeName = "Node"; + std::string nodeName = iox::NodeName_t("node"); // first acquire all possible NodeData acquireMaxNumberOfNodes(nodeName, processName); @@ -727,7 +652,7 @@ TEST_F(PortManager_test, AcquiringOneMoreThanMaximumNumberOfNodesFails) TEST_F(PortManager_test, DeleteNodePortfromMaximumNumberandAddOneIsSuccessful) { std::string processName = "Process"; - std::string nodeName = "Node"; + std::string nodeName = iox::NodeName_t("node"); // first acquire all possible NodeData acquireMaxNumberOfNodes(nodeName, processName); @@ -772,8 +697,8 @@ TEST_F(PortManager_test, PortsDestroyInProcess2ChangeStatesOfPortsInProcess1) iox::ProcessName_t processName2 = "myProcess2"; iox::capro::ServiceDescription cap1(1, 1, 1); iox::capro::ServiceDescription cap2(2, 2, 2); - PublisherOptions publisherOptions{1U, "node", false}; - SubscriberOptions subscriberOptions{1U, 1U, "node", false}; + PublisherOptions publisherOptions{1U, iox::NodeName_t("node"), false}; + SubscriberOptions subscriberOptions{1U, 1U, iox::NodeName_t("node"), false}; // two processes process1 and process2 each with a publisher and subscriber that match to the other process auto publisherData1 = diff --git a/iceoryx_utils/CMakeLists.txt b/iceoryx_utils/CMakeLists.txt index e523a425a4..cf95386b8b 100644 --- a/iceoryx_utils/CMakeLists.txt +++ b/iceoryx_utils/CMakeLists.txt @@ -211,9 +211,9 @@ target_link_libraries(iceoryx_platform PRIVATE ${ICEORYX_SANITIZER_FLAGS}) target_compile_options(iceoryx_platform PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) if(LINUX) - target_link_libraries(iceoryx_platform - PUBLIC - rt + target_link_libraries(iceoryx_platform + PUBLIC + rt pthread ) endif(LINUX) @@ -224,12 +224,20 @@ if(PERFORM_CLANG_TIDY) ) endif(PERFORM_CLANG_TIDY) - -target_link_libraries(iceoryx_utils - PUBLIC - iceoryx_utils::iceoryx_platform - PRIVATE - ${ICEORYX_SANITIZER_FLAGS}) +if(QNX) + target_link_libraries(iceoryx_utils + PUBLIC + iceoryx_utils::iceoryx_platform + PRIVATE + socket + ${ICEORYX_SANITIZER_FLAGS}) +else() + target_link_libraries(iceoryx_utils + PUBLIC + iceoryx_utils::iceoryx_platform + PRIVATE + ${ICEORYX_SANITIZER_FLAGS}) +endif(QNX) target_compile_options(iceoryx_utils PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) diff --git a/iceoryx_utils/cmake/IceoryxPlatform.cmake b/iceoryx_utils/cmake/IceoryxPlatform.cmake index 3c57ba7eb2..fae2476f22 100644 --- a/iceoryx_utils/cmake/IceoryxPlatform.cmake +++ b/iceoryx_utils/cmake/IceoryxPlatform.cmake @@ -138,8 +138,9 @@ if(SANITIZE) endif() if(COVERAGE) + set(CMAKE_CXX_OUTPUT_EXTENSION_REPLACE 1) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(ICEORYX_SANITIZER_FLAGS -fprofile-arcs -ftest-coverage CACHE INTERNAL "") + set(ICEORYX_SANITIZER_FLAGS -g -O0 -fprofile-arcs -ftest-coverage CACHE INTERNAL "") else(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") message( FATAL_ERROR "You need to run gcov with gcc compiler." ) endif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") diff --git a/iceoryx_utils/include/iceoryx_utils/cxx/optional.hpp b/iceoryx_utils/include/iceoryx_utils/cxx/optional.hpp index ead747c94f..89f744cc85 100644 --- a/iceoryx_utils/include/iceoryx_utils/cxx/optional.hpp +++ b/iceoryx_utils/include/iceoryx_utils/cxx/optional.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2021 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,6 +18,7 @@ #define IOX_UTILS_CXX_OPTIONAL_HPP #include "iceoryx_utils/cxx/function_ref.hpp" +#include "iceoryx_utils/cxx/helplets.hpp" #include "iceoryx_utils/cxx/types.hpp" #include // needed for placement new in the construct_value member function @@ -69,7 +71,7 @@ class optional /// @brief Creates an optional which has no value. If you access such an /// optional via .value() or the arrow operator the behavior is - /// undefined. + /// defined in the cxx::Expects handling. optional(const nullopt_t&) noexcept; /// @brief Creates an optional by forwarding value to the constructor of @@ -77,6 +79,10 @@ class optional /// @param[in] value rvalue of type T which will be moved into the optional optional(T&& value) noexcept; + /// @brief Creates an optional by using the copy constructor of T. + /// @param[in] value lvalue of type T which will be copy constructed into the optional + optional(const T& value) noexcept; + /// @brief The destructor will call the destructor of T if a value is set. ~optional() noexcept; diff --git a/iceoryx_utils/include/iceoryx_utils/cxx/stack.hpp b/iceoryx_utils/include/iceoryx_utils/cxx/stack.hpp new file mode 100644 index 0000000000..85bbe32f78 --- /dev/null +++ b/iceoryx_utils/include/iceoryx_utils/cxx/stack.hpp @@ -0,0 +1,60 @@ +// Copyright (c) 2021 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +#ifndef IOX_UTILS_CXX_STACK_HPP +#define IOX_UTILS_CXX_STACK_HPP + +#include "iceoryx_utils/cxx/optional.hpp" +#include + +namespace iox +{ +namespace cxx +{ +/// @brief stack implementation with a simple push pop interface +/// @tparam T type which the stack contains +/// @tparam Capacity the capacity of the stack +template +class stack +{ + public: + /// @brief returns the last pushed element when the stack contains elements + /// otherwise a cxx::nullopt + cxx::optional pop() noexcept; + + /// @brief pushed an element into the stack by forwarding all arguments + /// to the constructor of T + /// @param[in] args arguments which will be perfectly forwarded to the constructor of T + /// @return true if the push was successful, otherwise false + template + bool push(Targs&&... args) noexcept; + + /// @brief returns the stack size + uint64_t size() noexcept; + + /// @brief returns the stack capacity + static constexpr uint64_t capacity() noexcept; + + private: + using element_t = uint8_t[sizeof(T)]; + alignas(alignof(T)) element_t m_data[Capacity]; + uint64_t m_size = 0U; +}; +} // namespace cxx +} // namespace iox + +#include "iceoryx_utils/internal/cxx/stack.inl" + +#endif diff --git a/iceoryx_utils/include/iceoryx_utils/error_handling/error_handling.hpp b/iceoryx_utils/include/iceoryx_utils/error_handling/error_handling.hpp index 65dbcedafe..3c5a7c614f 100644 --- a/iceoryx_utils/include/iceoryx_utils/error_handling/error_handling.hpp +++ b/iceoryx_utils/include/iceoryx_utils/error_handling/error_handling.hpp @@ -78,15 +78,13 @@ namespace iox error(POPO__CHUNK_UNLOCKING_ERROR) \ error(POPO__CAPRO_PROTOCOL_ERROR) \ error(POPO__CONDITION_VARIABLE_DATA_FAILED_TO_CREATE_SEMAPHORE) \ - error(POPO__CONDITION_VARIABLE_WAITER_SEMAPHORE_CORRUPTED_IN_WAS_TRIGGERED) \ - error(POPO__CONDITION_VARIABLE_WAITER_SEMAPHORE_CORRUPTED_IN_WAIT) \ - error(POPO__CONDITION_VARIABLE_WAITER_SEMAPHORE_CORRUPTED_IN_TIMED_WAIT) \ - error(POPO__CONDITION_VARIABLE_WAITER_SEMAPHORE_CORRUPTED_IN_RESET) \ + error(POPO__CONDITION_LISTENER_SEMAPHORE_CORRUPTED_IN_WAS_TRIGGERED) \ + error(POPO__CONDITION_LISTENER_SEMAPHORE_CORRUPTED_IN_WAIT) \ + error(POPO__CONDITION_LISTENER_SEMAPHORE_CORRUPTED_IN_TIMED_WAIT) \ + error(POPO__CONDITION_LISTENER_SEMAPHORE_CORRUPTED_IN_RESET) \ + error(POPO__CONDITION_LISTENER_SEMAPHORE_CORRUPTED_IN_DESTROY) \ + error(POPO__CONDITION_NOTIFIER_INDEX_TOO_LARGE) \ error(POPO__EVENT_INFO_TYPE_INCONSISTENCY_IN_GET_ORIGIN) \ - error(POPO__EVENT_VARIABLE_WAITER_SEMAPHORE_CORRUPTED_IN_DESTROY) \ - error(POPO__EVENT_VARIABLE_WAITER_SEMAPHORE_CORRUPTED_IN_WAIT) \ - error(POPO__EVENT_VARIABLE_WAITER_SEMAPHORE_CORRUPTED_IN_RESET) \ - error(POPO__EVENT_NOTIFIER_INDEX_TOO_LARGE) \ error(POPO__TYPED_UNIQUE_ID_ROUDI_HAS_NO_DEFINED_UNIQUE_ID) \ error(POPO__TYPED_UNIQUE_ID_ROUDI_HAS_ALREADY_DEFINED_UNIQUE_ID) \ error(POPO__TYPED_UNIQUE_ID_OVERFLOW) \ diff --git a/iceoryx_utils/include/iceoryx_utils/internal/cxx/optional.inl b/iceoryx_utils/include/iceoryx_utils/internal/cxx/optional.inl index 9ed4db0118..b0191c54a2 100644 --- a/iceoryx_utils/include/iceoryx_utils/internal/cxx/optional.inl +++ b/iceoryx_utils/include/iceoryx_utils/internal/cxx/optional.inl @@ -1,4 +1,5 @@ // Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2021 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. @@ -37,6 +38,12 @@ inline optional::optional(T&& value) noexcept construct_value(std::forward(value)); } +template +inline optional::optional(const T& value) noexcept +{ + construct_value(value); +} + template inline optional::optional(const optional& rhs) noexcept { @@ -222,10 +229,10 @@ inline void optional::reset() noexcept } template - inline T& optional::value() & noexcept +inline T& optional::value() & noexcept { - auto data = (has_value()) ? static_cast(static_cast(m_data)) : nullptr; - return *data; + Expects(has_value()); + return *static_cast(static_cast(m_data)); } template @@ -236,10 +243,10 @@ inline const T& optional::value() const& noexcept } template - inline T&& optional::value() && noexcept +inline T&& optional::value() && noexcept { - auto data = (has_value()) ? static_cast(static_cast(m_data)) : nullptr; - return std::move(*data); + Expects(has_value()); + return std::move(*static_cast(static_cast(m_data))); } template diff --git a/iceoryx_utils/include/iceoryx_utils/internal/cxx/stack.inl b/iceoryx_utils/include/iceoryx_utils/internal/cxx/stack.inl new file mode 100644 index 0000000000..e9e98bbe40 --- /dev/null +++ b/iceoryx_utils/include/iceoryx_utils/internal/cxx/stack.inl @@ -0,0 +1,64 @@ +// Copyright (c) 2021 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +#ifndef IOX_UTILS_CXX_STACK_INL +#define IOX_UTILS_CXX_STACK_INL + +namespace iox +{ +namespace cxx +{ +template +inline cxx::optional stack::pop() noexcept +{ + if (m_size == 0U) + { + return cxx::nullopt; + } + + return *reinterpret_cast(m_data[--m_size]); +} + +template +template +inline bool stack::push(Targs&&... args) noexcept +{ + if (m_size >= Capacity) + { + return false; + } + + new (reinterpret_cast(m_data[m_size++])) T(std::forward(args)...); + return true; +} + +template +inline uint64_t stack::size() noexcept +{ + return m_size; +} + +template +inline constexpr uint64_t stack::capacity() noexcept +{ + return Capacity; +} + + +} // namespace cxx +} // namespace iox + +#endif + diff --git a/iceoryx_utils/include/iceoryx_utils/internal/units/duration.inl b/iceoryx_utils/include/iceoryx_utils/internal/units/duration.inl index c03538b6ab..49b5441233 100644 --- a/iceoryx_utils/include/iceoryx_utils/internal/units/duration.inl +++ b/iceoryx_utils/include/iceoryx_utils/internal/units/duration.inl @@ -31,8 +31,8 @@ inline constexpr Duration::Duration(const Seconds_t seconds, const Nanoseconds_t auto additionalSeconds = nanoseconds / NANOSECS_PER_SEC; if (std::numeric_limits::max() - additionalSeconds < m_seconds) { - std::clog << __PRETTY_FUNCTION__ - << ": Applied values are out of range and would overflow, clamping to max value!" << std::endl; + /// @todo #607 issue warning or fail + m_seconds = std::numeric_limits::max(); m_nanoseconds = NANOSECS_PER_SEC - 1U; } @@ -60,14 +60,14 @@ inline constexpr Duration Duration::zero() noexcept } template -inline constexpr unsigned long long int Duration::positiveValueOrClampToZero(const T value, - const String fromMethod) noexcept +inline constexpr unsigned long long int Duration::positiveValueOrClampToZero(const T value, const String) noexcept { static_assert(std::numeric_limits::is_integer, "only integer types are supported"); if (value < 0) { - std::clog << fromMethod << ": Clamping negative value '" << value << "' to zero!" << std::endl; + /// @todo #607 issue warning or fail + return 0U; } @@ -126,13 +126,13 @@ inline constexpr Duration::Duration(const struct itimerspec& value) noexcept } inline constexpr Duration::Duration(const std::chrono::milliseconds& value) noexcept + : Duration(Duration::fromMilliseconds(value.count())) { - *this = Duration::fromMilliseconds(value.count()); } inline constexpr Duration::Duration(const std::chrono::nanoseconds& value) noexcept + : Duration(Duration::fromNanoseconds(value.count())) { - *this = Duration::fromNanoseconds(value.count()); } inline Duration& Duration::operator=(const std::chrono::milliseconds& rhs) noexcept @@ -150,8 +150,8 @@ inline constexpr uint64_t Duration::toNanoseconds() const noexcept if (*this > MAX_DURATION_BEFORE_OVERFLOW) { - std::clog << __PRETTY_FUNCTION__ << ": Result of conversion would overflow, clamping to max value!" - << std::endl; + /// @todo #607 issue warning or fail + return std::numeric_limits::max(); } @@ -168,8 +168,8 @@ inline constexpr uint64_t Duration::toMicroseconds() const noexcept if (*this > MAX_DURATION_BEFORE_OVERFLOW) { - std::clog << __PRETTY_FUNCTION__ << ": Result of conversion would overflow, clamping to max value!" - << std::endl; + /// @todo #607 issue warning or fail + return std::numeric_limits::max(); } @@ -186,8 +186,8 @@ inline constexpr uint64_t Duration::toMilliseconds() const noexcept if (*this > MAX_DURATION_BEFORE_OVERFLOW) { - std::clog << __PRETTY_FUNCTION__ << ": Result of conversion would overflow, clamping to max value!" - << std::endl; + /// @todo #607 issue warning or fail + return std::numeric_limits::max(); } @@ -221,8 +221,8 @@ inline constexpr Duration::operator timeval() const noexcept static_assert(sizeof(Seconds_t) >= sizeof(SEC_TYPE), "casting might alter result"); if (m_seconds > static_cast(std::numeric_limits::max())) { - std::clog << __PRETTY_FUNCTION__ << ": Result of conversion would overflow, clamping to max value!" - << std::endl; + /// @todo #607 issue warning or fail + return {std::numeric_limits::max(), MICROSECS_PER_SEC - 1U}; } return {static_cast(m_seconds), static_cast(m_nanoseconds / NANOSECS_PER_MICROSEC)}; @@ -271,7 +271,8 @@ inline constexpr Duration Duration::operator+(const Duration& rhs) const noexcep auto sum = Duration{seconds, nanoseconds}; if (sum < *this) { - std::clog << __PRETTY_FUNCTION__ << ": Result of addition would overflow, clamping to max value!" << std::endl; + /// @todo #607 issue warning or fail + return Duration::max(); } return sum; @@ -281,7 +282,8 @@ inline constexpr Duration Duration::operator-(const Duration& rhs) const noexcep { if (*this < rhs) { - std::clog << __PRETTY_FUNCTION__ << ": Result of subtraction would be negative, clamping to zero!" << std::endl; + /// @todo #607 issue warning or fail + return Duration::zero(); } auto seconds = m_seconds - rhs.m_seconds; @@ -313,8 +315,8 @@ Duration::multiplyWith(const std::enable_if_t::value, // check if the result of the m_seconds multiplication would already overflow if (m_seconds > maxBeforeOverflow) { - std::clog << __PRETTY_FUNCTION__ << ": Result of multiplication would overflow, clamping to max value!" - << std::endl; + /// @todo #607 issue warning or fail + return Duration::max(); } auto durationFromSeconds = Duration(m_seconds * multiplicator, 0U); @@ -389,7 +391,8 @@ inline constexpr Duration Duration::fromFloatingPointSeconds(const T floatingPoi if (std::isinf(floatingPointSeconds)) { - std::clog << __PRETTY_FUNCTION__ << ": Multiplication with Inf, clamping to max value!" << std::endl; + /// @todo #607 issue warning or fail + return Duration::max(); } @@ -398,8 +401,8 @@ inline constexpr Duration Duration::fromFloatingPointSeconds(const T floatingPoi if (wouldCastFromFloatingPointProbablyOverflow(secondsFull)) { - std::clog << __PRETTY_FUNCTION__ << ": Result of multiplication would overflow, clamping to max value!" - << std::endl; + /// @todo #607 issue warning or fail + return Duration::max(); } @@ -408,14 +411,15 @@ inline constexpr Duration Duration::fromFloatingPointSeconds(const T floatingPoi } template -inline constexpr Duration -Duration::multiplyWith(const std::enable_if_t::value, T>& rhs) const noexcept +inline constexpr Duration Duration::multiplyWith(const std::enable_if_t::value, T>& rhs) const + noexcept { // operator*(...) takes care of negative values for rhs if (std::isnan(rhs)) { - std::clog << __PRETTY_FUNCTION__ << ": Multiplication with NaN, clamping to max value!" << std::endl; + /// @todo #607 issue warning or fail + return Duration::max(); } @@ -444,8 +448,7 @@ inline constexpr Duration Duration::operator*(const T& rhs) const noexcept { if (rhs < static_cast(0)) { - std::clog << __PRETTY_FUNCTION__ << ": Result of multiplication would be negative, clamping to zero!" - << std::endl; + /// @todo #607 issue warning or fail } return Duration::zero(); } @@ -483,8 +486,8 @@ inline constexpr Duration operator"" _s(unsigned long long int value) noexcept / constexpr Duration::Seconds_t MAX_SECONDS_BEFORE_OVERFLOW{std::numeric_limits::max()}; if (value > MAX_SECONDS_BEFORE_OVERFLOW) { - std::clog << __PRETTY_FUNCTION__ << ": Amount of seconds would overflow Duration, clamping to max value!" - << std::endl; + /// @todo #607 issue warning or fail + return Duration::max(); } return Duration{static_cast(value), 0U}; @@ -495,8 +498,8 @@ inline constexpr Duration operator"" _m(unsigned long long int value) noexcept / constexpr uint64_t MAX_MINUTES_BEFORE_OVERFLOW{std::numeric_limits::max() / Duration::SECS_PER_MINUTE}; if (value > MAX_MINUTES_BEFORE_OVERFLOW) { - std::clog << __PRETTY_FUNCTION__ << ": Amount of minutes would overflow Duration, clamping to max value!" - << std::endl; + /// @todo #607 issue warning or fail + return Duration::max(); } return Duration{static_cast(value * Duration::SECS_PER_MINUTE), 0U}; @@ -507,8 +510,8 @@ inline constexpr Duration operator"" _h(unsigned long long int value) noexcept / constexpr uint64_t MAX_HOURS_BEFORE_OVERFLOW{std::numeric_limits::max() / Duration::SECS_PER_HOUR}; if (value > MAX_HOURS_BEFORE_OVERFLOW) { - std::clog << __PRETTY_FUNCTION__ << ": Amount of hours would overflow Duration, clamping to max value!" - << std::endl; + /// @todo #607 issue warning or fail + return Duration::max(); } return Duration{static_cast(value * Duration::SECS_PER_HOUR), 0U}; @@ -520,8 +523,8 @@ inline constexpr Duration operator"" _d(unsigned long long int value) noexcept / constexpr uint64_t MAX_DAYS_BEFORE_OVERFLOW{std::numeric_limits::max() / SECS_PER_DAY}; if (value > MAX_DAYS_BEFORE_OVERFLOW) { - std::clog << __PRETTY_FUNCTION__ << ": Amount of days would overflow Duration, clamping to max value!" - << std::endl; + /// @todo #607 issue warning or fail + return Duration::max(); } return Duration{static_cast(value * SECS_PER_DAY), 0U}; diff --git a/iceoryx_utils/source/cxx/deadline_timer.cpp b/iceoryx_utils/source/cxx/deadline_timer.cpp index 1f37d72a51..2385cf1792 100644 --- a/iceoryx_utils/source/cxx/deadline_timer.cpp +++ b/iceoryx_utils/source/cxx/deadline_timer.cpp @@ -54,8 +54,7 @@ const iox::units::Duration DeadlineTimer::remainingTime() const noexcept iox::units::Duration DeadlineTimer::getCurrentMonotonicTime() const noexcept { - auto chronoCurrentTime = std::chrono::steady_clock::now().time_since_epoch(); - iox::units::Duration currentTime(chronoCurrentTime); + iox::units::Duration currentTime(std::chrono::steady_clock::now().time_since_epoch()); return currentTime; } diff --git a/iceoryx_utils/test/moduletests/test_cxx_optional.cpp b/iceoryx_utils/test/moduletests/test_cxx_optional.cpp index 32dfed5412..6465eed4e5 100644 --- a/iceoryx_utils/test/moduletests/test_cxx_optional.cpp +++ b/iceoryx_utils/test/moduletests/test_cxx_optional.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2021 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. @@ -473,3 +474,13 @@ TEST_F(Optional_test, ReturningNulloptWithoutConstruction) auto val = []() -> iox::cxx::optional { return iox::cxx::nullopt; }(); EXPECT_THAT(val.has_value(), Eq(false)); } + +TEST_F(Optional_test, CopyConstructionWithElementWorks) +{ + const TestClass testClass{5, 6}; + iox::cxx::optional sut(testClass); + + ASSERT_TRUE(sut.has_value()); + EXPECT_THAT(sut->value, Eq(5)); + EXPECT_THAT(sut->secondValue, Eq(6)); +} diff --git a/iceoryx_utils/test/moduletests/test_cxx_stack.cpp b/iceoryx_utils/test/moduletests/test_cxx_stack.cpp new file mode 100644 index 0000000000..e5faef81f8 --- /dev/null +++ b/iceoryx_utils/test/moduletests/test_cxx_stack.cpp @@ -0,0 +1,114 @@ +// Copyright (c) 2021 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "iceoryx_utils/cxx/stack.hpp" +#include "test.hpp" +using namespace iox; +using namespace ::testing; + +namespace +{ +class TestClass +{ + public: + TestClass() noexcept = default; + TestClass(const int32_t a, const int32_t b, const int32_t c) noexcept + : m_a(a) + , m_b(b) + , m_c(c) + { + } + + bool operator==(const TestClass& rhs) const noexcept + { + return m_a == rhs.m_a && m_b == rhs.m_b && m_c == rhs.m_c; + } + int32_t m_a = 0, m_b = 0, m_c = 0; +}; + +class stack_test : public Test +{ + public: + static constexpr uint64_t STACK_SIZE = 10U; + cxx::stack m_sut; + + void pushElements(const uint64_t numberOfElements) + { + for (uint64_t i = 0U; i < numberOfElements; ++i) + { + ASSERT_TRUE(m_sut.push(1 + i, 2 + i, 3 + i)); + EXPECT_THAT(m_sut.size(), Eq(i + 1U)); + EXPECT_THAT(m_sut.capacity(), Eq(STACK_SIZE)); + } + } +}; +} // namespace + +TEST_F(stack_test, isEmptyOnCreation) +{ + EXPECT_THAT(m_sut.size(), Eq(0U)); + EXPECT_THAT(m_sut.capacity(), Eq(STACK_SIZE)); + EXPECT_THAT(m_sut.pop(), Eq(cxx::nullopt)); +} + +TEST_F(stack_test, pushingOneElementWithDefaultCtorSucceeds) +{ + ASSERT_TRUE(m_sut.push()); + EXPECT_THAT(m_sut.size(), Eq(1U)); + EXPECT_THAT(m_sut.capacity(), Eq(STACK_SIZE)); + + auto element = m_sut.pop(); + ASSERT_TRUE(element.has_value()); + EXPECT_THAT(*element, Eq(TestClass(0, 0, 0))); +} + +TEST_F(stack_test, pushingOneElementWithCustomCtorSucceeds) +{ + pushElements(1U); + + auto element = m_sut.pop(); + ASSERT_TRUE(element.has_value()); + EXPECT_THAT(*element, Eq(TestClass(1, 2, 3))); +} + +TEST_F(stack_test, pushingElementsTillStackIsFullAndPoppingInLIFOOrderSucceeds) +{ + pushElements(STACK_SIZE); + + for (uint64_t i = 0U; i < STACK_SIZE; ++i) + { + auto element = m_sut.pop(); + EXPECT_THAT(m_sut.size(), Eq(STACK_SIZE - i - 1U)); + ASSERT_TRUE(element.has_value()); + EXPECT_THAT(*element, Eq(TestClass(STACK_SIZE - i, 1 + STACK_SIZE - i, 2 + STACK_SIZE - i))); + } +} + +TEST_F(stack_test, ifCapacityIsExceededPushFails) +{ + pushElements(STACK_SIZE); + + EXPECT_FALSE(m_sut.push()); +} + +TEST_F(stack_test, popCreatesSpaceForAnotherElement) +{ + pushElements(STACK_SIZE); + + EXPECT_THAT(m_sut.pop(), Ne(cxx::nullopt)); + EXPECT_TRUE(m_sut.push()); +} + diff --git a/tools/gcov/lcov_generate.sh b/tools/gcov/lcov_generate.sh index 3ef79f46bb..9ad463914b 100755 --- a/tools/gcov/lcov_generate.sh +++ b/tools/gcov/lcov_generate.sh @@ -25,6 +25,7 @@ mkdir -p $OUTPUT_FOLDER case "$2" in "initial") + lcov -z lcov -c -i -d $BUILD_FOLDER -o $OUTPUT_FOLDER/iceoryx_init.info --no-external --rc lcov_branch_coverage=1 ;; "scan") diff --git a/tools/iceoryx_build_test.sh b/tools/iceoryx_build_test.sh index 69188cff30..98e10089f5 100755 --- a/tools/iceoryx_build_test.sh +++ b/tools/iceoryx_build_test.sh @@ -49,8 +49,8 @@ EXAMPLE_FLAG="OFF" BUILD_ALL_FLAG="OFF" BUILD_SHARED="OFF" TOML_FLAG="ON" -EXAMPLES="callbacks ice_multi_publisher icedelivery singleprocess waitset" -COMPONENTS="iceoryx_posh iceoryx_utils iceoryx_introspection iceoryx_binding_c iceoryx_component iceoryx_dds" +EXAMPLES="callbacks callbacks_in_c ice_multi_publisher icedelivery singleprocess waitset" +COMPONENTS="iceoryx_posh iceoryx_utils iceoryx_introspection iceoryx_binding_c iceoryx_component iceoryx_dds" while (( "$#" )); do case "$1" in @@ -58,6 +58,11 @@ while (( "$#" )); do BUILD_DIR=$(realpath $2) shift 2 ;; + -t|--toolchain-file) + TOOLCHAIN_FILE="-DCMAKE_TOOLCHAIN_FILE=$2" + echo $TOOLCHAIN_FILE + shift 2 + ;; -c|--coverage) BUILD_TYPE="Debug" RUN_TEST=true @@ -184,6 +189,7 @@ while (( "$#" )); do echo " -b --build-dir Specify a non-default build directory" echo " -c --coverage Build with gcov and generate a html/xml report." echo " Possible arguments: 'all', 'unit', 'integration', 'only-timing-tests'" + echo " -t --toolchain-file Specify an absolute path to a toolchain file for cross-compiling e.g. (-t $(pwd)/tools/qnx/qnx710.nto.toolchain.aarch64.cmake)" echo "Args:" echo " clean Delete the build/ directory before build-step" echo " release Build with -O3" @@ -258,7 +264,7 @@ if [ "$PACKAGE" == "OFF" ]; then cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_ALL=$BUILD_ALL_FLAG -DBUILD_STRICT=$STRICT_FLAG -DCMAKE_INSTALL_PREFIX=$ICEORYX_INSTALL_PREFIX \ -DBUILD_TEST=$TEST_FLAG -DCOVERAGE=$COV_FLAG -DROUDI_ENVIRONMENT=$ROUDI_ENV_FLAG -DEXAMPLES=$EXAMPLE_FLAG -DTOML_CONFIG=$TOML_FLAG -DBUILD_DOC=$BUILD_DOC \ -DDDS_GATEWAY=$DDS_GATEWAY_FLAG -DBINDING_C=$BINDING_C_FLAG -DONE_TO_MANY_ONLY=$ONE_TO_MANY_ONLY_FLAG -DBUILD_SHARED_LIBS=$BUILD_SHARED \ - -DSANITIZE=$SANITIZE_FLAG -DTEST_WITH_ADDITIONAL_USER=$TEST_ADD_USER $WORKSPACE/iceoryx_meta + -DSANITIZE=$SANITIZE_FLAG -DTEST_WITH_ADDITIONAL_USER=$TEST_ADD_USER $TOOLCHAIN_FILE $WORKSPACE/iceoryx_meta cmake --build . --target install -- -j$NUM_JOBS echo ">>>>>> Finished building iceoryx package <<<<<<" @@ -267,12 +273,12 @@ else cd $WORKSPACE rm -rf build_package mkdir -p build_package - cd build_package + cd build_package cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_STRICT=$STRICT_FLAG -DCMAKE_INSTALL_PREFIX=build_package/install/prefix/ $WORKSPACE/iceoryx_meta cmake --build . --target install -- -j$NUM_JOBS cpack - echo ">>>>>> Finished building iceoryx package <<<<<<" + echo ">>>>>> Finished building iceoryx package <<<<<<" fi