diff --git a/doc/website/getting-started/examples/intermediate-examples.md b/doc/website/getting-started/examples/intermediate-examples.md index b573df9c681..d87c629de39 100644 --- a/doc/website/getting-started/examples/intermediate-examples.md +++ b/doc/website/getting-started/examples/intermediate-examples.md @@ -1,5 +1,6 @@ # Intermediate examples +{! ./../iceoryx_examples/callbacks/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 81005b48e4c..d8ca0002fe7 100644 --- a/doc/website/getting-started/installation.md +++ b/doc/website/getting-started/installation.md @@ -6,9 +6,10 @@ iceoryx_utils and iceoryx_posh are deployed as independent cmake packages. Posh ### Dependencies + - 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 - [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. diff --git a/iceoryx_binding_c/CMakeLists.txt b/iceoryx_binding_c/CMakeLists.txt index 11f4f6b62cf..b7b338b6b3a 100644 --- a/iceoryx_binding_c/CMakeLists.txt +++ b/iceoryx_binding_c/CMakeLists.txt @@ -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. @@ -53,6 +54,7 @@ add_library(${PROJECT_NAME} source/c_subscriber.cpp source/c_event_info.cpp source/c_wait_set.cpp + source/c_chunk.cpp source/cpp2c_enum_translation.cpp source/cpp2c_publisher.cpp source/cpp2c_subscriber.cpp @@ -70,11 +72,22 @@ target_include_directories(${PROJECT_NAME} $ $ ) -target_link_libraries(${PROJECT_NAME} - PRIVATE - iceoryx_posh::iceoryx_posh - iceoryx_utils::iceoryx_utils -) + +if(NOT WIN32) + target_link_libraries(${PROJECT_NAME} + PUBLIC + pthread + PRIVATE + iceoryx_posh::iceoryx_posh + iceoryx_utils::iceoryx_utils + ) +else() + target_link_libraries(${PROJECT_NAME} + PRIVATE + iceoryx_posh::iceoryx_posh + iceoryx_utils::iceoryx_utils + ) +endif() target_compile_options(${PROJECT_NAME} PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) diff --git a/iceoryx_binding_c/include/iceoryx_binding_c/chunk.h b/iceoryx_binding_c/include/iceoryx_binding_c/chunk.h new file mode 100644 index 00000000000..04990ea321c --- /dev/null +++ b/iceoryx_binding_c/include/iceoryx_binding_c/chunk.h @@ -0,0 +1,35 @@ +// 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_CHUNK_H +#define IOX_BINDING_C_CHUNK_H + +#include "iceoryx_binding_c/internal/c2cpp_binding.h" + +/// @brief handle of the chunk header +typedef void* iox_chunk_header_t; + +/// @brief gets the payload from the chunk header +/// @param[in] header pointer to the chunk header +/// @return pointer to the payload +void* iox_chunk_header_to_payload(iox_chunk_header_t const header); + +/// @brief gets the chunk header from the payload +/// @param[in] payload pointer to the payload +/// @return pointer to the chunk header +iox_chunk_header_t iox_chunk_payload_to_header(const void* const payload); + +#endif diff --git a/iceoryx_binding_c/include/iceoryx_binding_c/enums.h b/iceoryx_binding_c/include/iceoryx_binding_c/enums.h index c611808a091..e50691c3eec 100644 --- a/iceoryx_binding_c/include/iceoryx_binding_c/enums.h +++ b/iceoryx_binding_c/include/iceoryx_binding_c/enums.h @@ -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. @@ -38,7 +39,7 @@ enum iox_SubscribeState enum iox_ChunkReceiveResult { ChunkReceiveResult_TOO_MANY_CHUNKS_HELD_IN_PARALLEL, - ChunkReceiveResult_NO_CHUNK_RECEIVED, + ChunkReceiveResult_NO_CHUNK_AVAILABLE, ChunkReceiveResult_UNDEFINED_ERROR, ChunkReceiveResult_SUCCESS, }; diff --git a/iceoryx_binding_c/include/iceoryx_binding_c/publisher.h b/iceoryx_binding_c/include/iceoryx_binding_c/publisher.h index 412bb758f14..e35ec594c43 100644 --- a/iceoryx_binding_c/include/iceoryx_binding_c/publisher.h +++ b/iceoryx_binding_c/include/iceoryx_binding_c/publisher.h @@ -1,4 +1,5 @@ -// Copyright (c) 2020, 2021 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. @@ -24,20 +25,50 @@ /// @brief publisher handle typedef struct cpp2c_Publisher* iox_pub_t; +/// @brief options to be set for a publisher +typedef struct +{ + // size of the history chunk queue + uint64_t historyCapacity; + + // name of the node the publisher belongs to + // nullptr indicates that the default node name is used + const char* nodeName; + + // The option whether the publisher should already be offered when creating it + bool offerOnCreate; + + // this value will be set exclusively by iox_pub_options_init + // and is not supposed to be modified otherwise + uint64_t initCheck; +} iox_pub_options_t; + +/// @brief initialize publisher options to default values +/// @param[in] options pointer to options to be initialized, +/// emit warning if it is a null pointer +/// @attention This must always be called on a newly created options struct to +/// prevent uninitialized values. The options may get extended +/// in the future. +void iox_pub_options_init(iox_pub_options_t* options); + +/// @brief check whether the publisher options were initialized by iox_pub_options_init +/// @param[in] options pointer to options to be checked +/// @return true if options are not null and were initialized, false otherwise +bool iox_pub_options_is_initialized(const iox_pub_options_t* const options); + /// @brief creates a publisher handle /// @param[in] self pointer to preallocated memory of size = sizeof(iox_pub_storage_t) /// @param[in] service serviceString /// @param[in] instance instanceString /// @param[in] event eventString -/// @param[in] historyCapacity size of the history chunk queue -/// @param[in] nodeName name of the node the publisher belongs to +/// @param[in] options publisher options set by the user, +/// if it is a null pointer default options are used /// @return handle of the publisher iox_pub_t iox_pub_init(iox_pub_storage_t* self, const char* const service, const char* const instance, const char* const event, - const uint64_t historyCapacity, - const char* const nodeName); + const iox_pub_options_t* const options); /// @brief removes a publisher handle /// @param[in] self the handle which should be removed @@ -49,23 +80,23 @@ void iox_pub_deinit(iox_pub_t const self); /// @param[in] payloadSize size of the allocated chunk /// @return on success it returns AllocationResult_SUCCESS otherwise a value which /// describes the error -ENUM iox_AllocationResult iox_pub_allocate_chunk(iox_pub_t const self, void** const chunk, const uint32_t payloadSize); +ENUM iox_AllocationResult iox_pub_loan_chunk(iox_pub_t const self, void** const chunk, const uint32_t payloadSize); -/// @brief frees a previously allocated chunk without sending it +/// @brief releases ownership of a previously allocated chunk without sending it /// @param[in] self handle of the publisher /// @param[in] chunk chunk which should be free'd -void iox_pub_free_chunk(iox_pub_t const self, void* const chunk); +void iox_pub_release_chunk(iox_pub_t const self, void* const chunk); /// @brief sends a previously allocated chunk /// @param[in] self handle of the publisher /// @param[in] chunk chunk which should be send -void iox_pub_send_chunk(iox_pub_t const self, void* const chunk); +void iox_pub_publish_chunk(iox_pub_t const self, void* const chunk); /// @brief returns the previously sended chunk /// @param[in] self handle of the publisher /// @return nullptr if no chunk was previously send otherwise a pointer to the /// previous chunk -const void* iox_pub_try_get_previous_chunk(iox_pub_t const self); +const void* iox_pub_loan_previous_chunk(iox_pub_t const self); /// @brief offers the service /// @param[in] self handle of the publisher diff --git a/iceoryx_binding_c/include/iceoryx_binding_c/subscriber.h b/iceoryx_binding_c/include/iceoryx_binding_c/subscriber.h index df6547af060..81948f2f000 100644 --- a/iceoryx_binding_c/include/iceoryx_binding_c/subscriber.h +++ b/iceoryx_binding_c/include/iceoryx_binding_c/subscriber.h @@ -1,4 +1,5 @@ -// Copyright (c) 2020, 2021 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. @@ -24,22 +25,53 @@ /// @brief Subscriber handle typedef struct cpp2c_Subscriber* iox_sub_t; +/// @brief options to be set for a subscriber +typedef struct +{ + // size of the history chunk queue + uint64_t queueCapacity; + + // number of chunks received after subscription if chunks are available + // nullptr indicates that the default node name is used + uint64_t historyRequest; + + // name of the node the subscriber belongs to + const char* nodeName; + + // The option whether the subscriber shall try to subscribe when creating it + bool subscribeOnCreate; + + // this value will be set exclusively by iox_sub_options_init + // and is not supposed to be modified otherwise + uint64_t initCheck; +} iox_sub_options_t; + +/// @brief initialize subscriber options to default values +/// @param[in] options pointer to options to be initialized, +/// emit warning if it is a null pointer +/// @attention This must always be called on a newly created options struct to +/// prevent uninitialized values. The options may get extended +/// in the future. +void iox_sub_options_init(iox_sub_options_t* const options); + +/// @brief check whether the subscriber options were initialized by iox_sub_options_init +/// @param[in] options pointer to options to be checked +/// @return true if options are not null and were initialized, false otherwise +bool iox_sub_options_is_initialized(const iox_sub_options_t* const options); + /// @brief initialize subscriber handle /// @param[in] self pointer to preallocated memory of size = sizeof(iox_sub_storage_t) /// @param[in] service serviceString /// @param[in] instance instanceString /// @param[in] event eventString -/// @param[in] queueCapacity size of the receiver queue -/// @param[in] historyRequest of chunks received after subscription if chunks are available -/// @param[in] nodeName name of node where the subscriber belongs to +/// @param[in] options subscriber options set by the user, +/// if it is a null pointer default options are used /// @return handle of the subscriber iox_sub_t iox_sub_init(iox_sub_storage_t* self, const char* const service, const char* const instance, const char* const event, - const uint64_t queueCapacity, - const uint64_t historyRequest, - const char* const nodeName); + const iox_sub_options_t* const options); /// @brief deinitialize a subscriber handle /// @param[in] self the handle which should be removed @@ -64,7 +96,7 @@ ENUM iox_SubscribeState iox_sub_get_subscription_state(iox_sub_t const self); /// @param[in] chunk pointer in which the pointer to the chunk is stored /// @return if a chunk could be received it returns ChunkReceiveResult_SUCCESS otherwise /// an enum which describes the error -ENUM iox_ChunkReceiveResult iox_sub_get_chunk(iox_sub_t const self, const void** const chunk); +ENUM iox_ChunkReceiveResult iox_sub_take_chunk(iox_sub_t const self, const void** const chunk); /// @brief release a previously acquired chunk (via iox_sub_getChunk) /// @param[in] self handle to the subscriber @@ -77,12 +109,12 @@ void iox_sub_release_queued_chunks(iox_sub_t const self); /// @brief are new chunks available? /// @param[in] self handle to the subscriber -/// @return true if there are chunks otherwise false +/// @return true if there are chunks, otherwise false bool iox_sub_has_chunks(iox_sub_t const self); /// @brief are chunks lost? /// @param[in] self handle to the subscriber -/// @return true if there are lost chunks otherwise false +/// @return true if there are lost chunks due to overflowing queue, otherwise false bool iox_sub_has_lost_chunks(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 938c38e0bef..7c210632d84 100644 --- a/iceoryx_binding_c/include/iceoryx_binding_c/types.h +++ b/iceoryx_binding_c/include/iceoryx_binding_c/types.h @@ -39,7 +39,7 @@ struct iox_user_trigger_storage_t_ // the value of the array size is the result of the following formula: // sizeof(UserTrigger) / 8 #if defined(__APPLE__) - uint64_t do_not_touch_me[16]; + uint64_t do_not_touch_me[17]; #else uint64_t do_not_touch_me[14]; #endif @@ -51,7 +51,7 @@ struct iox_sub_storage_t_ // the value of the array size is the result of the following formula: // sizeof(cpp2c_Subscriber) / 8 #if defined(__APPLE__) - uint64_t do_not_touch_me[16]; + uint64_t do_not_touch_me[17]; #else uint64_t do_not_touch_me[14]; #endif diff --git a/iceoryx_binding_c/source/c_chunk.cpp b/iceoryx_binding_c/source/c_chunk.cpp new file mode 100644 index 00000000000..4d3a997e7cb --- /dev/null +++ b/iceoryx_binding_c/source/c_chunk.cpp @@ -0,0 +1,34 @@ +// 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/mepoo/chunk_header.hpp" + + +extern "C" { +#include "iceoryx_binding_c/chunk.h" +} + +using namespace iox::mepoo; + +void* iox_chunk_header_to_payload(iox_chunk_header_t const header) +{ + return reinterpret_cast(header)->payload(); +} + +iox_chunk_header_t iox_chunk_payload_to_header(const void* const payload) +{ + return ChunkHeader::fromPayload(payload); +} diff --git a/iceoryx_binding_c/source/c_publisher.cpp b/iceoryx_binding_c/source/c_publisher.cpp index f29f519c4a0..d68029e3e73 100644 --- a/iceoryx_binding_c/source/c_publisher.cpp +++ b/iceoryx_binding_c/source/c_publisher.cpp @@ -1,4 +1,5 @@ -// Copyright (c) 2020, 2021 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. @@ -30,25 +31,64 @@ extern "C" { #include "iceoryx_binding_c/publisher.h" } +constexpr uint64_t PUBLISHER_OPTIONS_INIT_CHECK_CONSTANT = 123454321; + +void iox_pub_options_init(iox_pub_options_t* options) +{ + if (options == nullptr) + { + LogWarn() << "publisher options initialization skipped - null pointer provided"; + return; + } + + PublisherOptions publisherOptions; + options->historyCapacity = publisherOptions.historyCapacity; + options->nodeName = nullptr; + options->offerOnCreate = publisherOptions.offerOnCreate; + + options->initCheck = PUBLISHER_OPTIONS_INIT_CHECK_CONSTANT; +} + +bool iox_pub_options_is_initialized(const iox_pub_options_t* const options) +{ + return options && options->initCheck == PUBLISHER_OPTIONS_INIT_CHECK_CONSTANT; +} + iox_pub_t iox_pub_init(iox_pub_storage_t* self, const char* const service, const char* const instance, const char* const event, - const uint64_t historyCapacity, - const char* const nodeName) + const iox_pub_options_t* const options) { new (self) cpp2c_Publisher(); iox_pub_t me = reinterpret_cast(self); - PublisherOptions options; - options.historyCapacity = historyCapacity; - options.nodeName = NodeName_t(TruncateToCapacity, nodeName); + PublisherOptions publisherOptions; + + // use default options otherwise + if (options != nullptr) + { + if (!iox_pub_options_is_initialized(options)) + { + // note that they may have been initialized but the initCheck + // pattern overwritten afterwards, we cannot be sure but it is a misuse + LogFatal() << "publisher options may not have been initialized with iox_pub_init"; + errorHandler(Error::kBINDING_C__PUBLISHER_OPTIONS_NOT_INITIALIZED); + } + publisherOptions.historyCapacity = options->historyCapacity; + if (options->nodeName != nullptr) + { + publisherOptions.nodeName = NodeName_t(TruncateToCapacity, options->nodeName); + } + publisherOptions.offerOnCreate = options->offerOnCreate; + } + me->m_portData = PoshRuntime::getInstance().getMiddlewarePublisher( ServiceDescription{ IdString_t(TruncateToCapacity, service), IdString_t(TruncateToCapacity, instance), IdString_t(TruncateToCapacity, event), }, - options); + publisherOptions); return me; } @@ -58,7 +98,7 @@ void iox_pub_deinit(iox_pub_t const self) self->~cpp2c_Publisher(); } -iox_AllocationResult iox_pub_allocate_chunk(iox_pub_t const self, void** const chunk, const uint32_t payloadSize) +iox_AllocationResult iox_pub_loan_chunk(iox_pub_t const self, void** const chunk, const uint32_t payloadSize) { auto result = PublisherPortUser(self->m_portData).tryAllocateChunk(payloadSize).and_then([&](ChunkHeader* h) { *chunk = h->payload(); @@ -71,17 +111,17 @@ iox_AllocationResult iox_pub_allocate_chunk(iox_pub_t const self, void** const c return AllocationResult_SUCCESS; } -void iox_pub_free_chunk(iox_pub_t const self, void* const chunk) +void iox_pub_release_chunk(iox_pub_t const self, void* const chunk) { PublisherPortUser(self->m_portData).releaseChunk(ChunkHeader::fromPayload(chunk)); } -void iox_pub_send_chunk(iox_pub_t const self, void* const chunk) +void iox_pub_publish_chunk(iox_pub_t const self, void* const chunk) { PublisherPortUser(self->m_portData).sendChunk(ChunkHeader::fromPayload(chunk)); } -const void* iox_pub_try_get_previous_chunk(iox_pub_t const self) +const void* iox_pub_loan_previous_chunk(iox_pub_t const self) { const void* returnValue = nullptr; PublisherPortUser(self->m_portData).tryGetPreviousChunk().and_then([&](const ChunkHeader* h) { diff --git a/iceoryx_binding_c/source/c_subscriber.cpp b/iceoryx_binding_c/source/c_subscriber.cpp index 26529751cd1..6618ffddb2c 100644 --- a/iceoryx_binding_c/source/c_subscriber.cpp +++ b/iceoryx_binding_c/source/c_subscriber.cpp @@ -1,4 +1,5 @@ -// Copyright (c) 2020, 2021 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. @@ -35,25 +36,64 @@ extern "C" { #include "iceoryx_binding_c/subscriber.h" } +constexpr uint64_t SUBSCRIBER_OPTIONS_INIT_CHECK_CONSTANT = 543212345; + +void iox_sub_options_init(iox_sub_options_t* options) +{ + if (options == nullptr) + { + LogWarn() << "subscriber options initialization skipped - null pointer provided"; + return; + } + + SubscriberOptions subscriberOptions; + options->queueCapacity = subscriberOptions.queueCapacity; + options->historyRequest = subscriberOptions.historyRequest; + options->nodeName = nullptr; + options->subscribeOnCreate = subscriberOptions.subscribeOnCreate; + + options->initCheck = SUBSCRIBER_OPTIONS_INIT_CHECK_CONSTANT; +} + +bool iox_sub_options_is_initialized(const iox_sub_options_t* const options) +{ + return options && options->initCheck == SUBSCRIBER_OPTIONS_INIT_CHECK_CONSTANT; +} + iox_sub_t iox_sub_init(iox_sub_storage_t* self, const char* const service, const char* const instance, const char* const event, - const uint64_t queueCapacity, - const uint64_t historyRequest, - const char* const nodeName) + const iox_sub_options_t* const options) { new (self) cpp2c_Subscriber(); iox_sub_t me = reinterpret_cast(self); - SubscriberOptions options; - options.queueCapacity = queueCapacity; - options.historyRequest = historyRequest; - options.nodeName = NodeName_t(TruncateToCapacity, nodeName); + SubscriberOptions subscriberOptions; + + // use default options otherwise + if (options != nullptr) + { + if (!iox_sub_options_is_initialized(options)) + { + // note that they may have been initialized but the initCheck + // pattern overwritten afterwards, we cannot be sure but it is a misuse + LogFatal() << "subscriber options may not have been initialized with iox_sub_init"; + errorHandler(Error::kBINDING_C__SUBSCRIBER_OPTIONS_NOT_INITIALIZED); + } + subscriberOptions.queueCapacity = options->queueCapacity; + subscriberOptions.historyRequest = options->historyRequest; + if (options->nodeName != nullptr) + { + subscriberOptions.nodeName = NodeName_t(TruncateToCapacity, options->nodeName); + } + subscriberOptions.subscribeOnCreate = options->subscribeOnCreate; + } + me->m_portData = PoshRuntime::getInstance().getMiddlewareSubscriber(ServiceDescription{IdString_t(TruncateToCapacity, service), IdString_t(TruncateToCapacity, instance), IdString_t(TruncateToCapacity, event)}, - options); + subscriberOptions); return me; } @@ -78,7 +118,7 @@ iox_SubscribeState iox_sub_get_subscription_state(iox_sub_t const self) return cpp2c::SubscribeState(SubscriberPortUser(self->m_portData).getSubscriptionState()); } -iox_ChunkReceiveResult iox_sub_get_chunk(iox_sub_t const self, const void** const payload) +iox_ChunkReceiveResult iox_sub_take_chunk(iox_sub_t const self, const void** const payload) { auto result = SubscriberPortUser(self->m_portData).tryGetChunk(); if (result.has_error()) @@ -86,12 +126,7 @@ iox_ChunkReceiveResult iox_sub_get_chunk(iox_sub_t const self, const void** cons return cpp2c::ChunkReceiveResult(result.get_error()); } - if (!result->has_value()) - { - return ChunkReceiveResult_NO_CHUNK_RECEIVED; - } - - *payload = (**result)->payload(); + *payload = result.value()->payload(); return ChunkReceiveResult_SUCCESS; } diff --git a/iceoryx_binding_c/source/cpp2c_enum_translation.cpp b/iceoryx_binding_c/source/cpp2c_enum_translation.cpp index 35a21a94cc6..c5ee83032e0 100644 --- a/iceoryx_binding_c/source/cpp2c_enum_translation.cpp +++ b/iceoryx_binding_c/source/cpp2c_enum_translation.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. @@ -45,6 +46,8 @@ iox_ChunkReceiveResult ChunkReceiveResult(const iox::popo::ChunkReceiveResult va { switch (value) { + case ChunkReceiveResult::NO_CHUNK_AVAILABLE: + return ChunkReceiveResult_NO_CHUNK_AVAILABLE; case ChunkReceiveResult::TOO_MANY_CHUNKS_HELD_IN_PARALLEL: return ChunkReceiveResult_TOO_MANY_CHUNKS_HELD_IN_PARALLEL; default: diff --git a/iceoryx_binding_c/test/moduletests/test_cpp2c_enum_translation.cpp b/iceoryx_binding_c/test/moduletests/test_cpp2c_enum_translation.cpp index 48c84d215b5..4973f253ecd 100644 --- a/iceoryx_binding_c/test/moduletests/test_cpp2c_enum_translation.cpp +++ b/iceoryx_binding_c/test/moduletests/test_cpp2c_enum_translation.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. @@ -38,6 +39,9 @@ TEST(cpp2c_enum_translation_test, SubscribeState) TEST(cpp2c_enum_translation_test, ChunkReceiveResult) { + EXPECT_EQ(cpp2c::ChunkReceiveResult(iox::popo::ChunkReceiveResult::NO_CHUNK_AVAILABLE), + ChunkReceiveResult_NO_CHUNK_AVAILABLE); + EXPECT_EQ(cpp2c::ChunkReceiveResult(iox::popo::ChunkReceiveResult::TOO_MANY_CHUNKS_HELD_IN_PARALLEL), ChunkReceiveResult_TOO_MANY_CHUNKS_HELD_IN_PARALLEL); @@ -75,4 +79,3 @@ TEST(cpp2c_enum_translation_test, WaitSetResult) EXPECT_EQ(cpp2c::WaitSetResult(static_cast(-1)), WaitSetResult_UNDEFINED_ERROR); #pragma GCC diagnostic pop } - diff --git a/iceoryx_binding_c/test/moduletests/test_publisher.cpp b/iceoryx_binding_c/test/moduletests/test_publisher.cpp index 574a0748b24..b25853f9f5c 100644 --- a/iceoryx_binding_c/test/moduletests/test_publisher.cpp +++ b/iceoryx_binding_c/test/moduletests/test_publisher.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. @@ -28,6 +29,7 @@ using namespace iox::cxx; using namespace iox::posix; extern "C" { +#include "iceoryx_binding_c/chunk.h" #include "iceoryx_binding_c/publisher.h" } @@ -116,9 +118,11 @@ class iox_pub_test : public Test cpp2c_Publisher m_sut; }; -TEST_F(iox_pub_test, initialStateIsNotOffered) +TEST_F(iox_pub_test, initialStateOfIsOfferedIsAsExpected) { - EXPECT_FALSE(iox_pub_is_offered(&m_sut)); + PublisherOptions iGotOptions; + auto expectedIsOffered = iGotOptions.offerOnCreate; + EXPECT_EQ(expectedIsOffered, iox_pub_is_offered(&m_sut)); } TEST_F(iox_pub_test, is_offeredAfterOffer) @@ -157,7 +161,24 @@ TEST_F(iox_pub_test, noSubscribersAfterUnsubscribe) TEST_F(iox_pub_test, allocateChunkForOneChunkIsSuccessful) { void* chunk = nullptr; - EXPECT_EQ(AllocationResult_SUCCESS, iox_pub_allocate_chunk(&m_sut, &chunk, sizeof(DummySample))); + EXPECT_EQ(AllocationResult_SUCCESS, iox_pub_loan_chunk(&m_sut, &chunk, sizeof(DummySample))); +} + +TEST_F(iox_pub_test, chunkHeaderCanBeObtainedFromChunk) +{ + void* chunk = nullptr; + ASSERT_EQ(AllocationResult_SUCCESS, iox_pub_loan_chunk(&m_sut, &chunk, sizeof(DummySample))); + auto header = iox_chunk_payload_to_header(chunk); + EXPECT_NE(header, nullptr); +} + +TEST_F(iox_pub_test, chunkHeaderCanBeConvertedBackToPayload) +{ + void* chunk = nullptr; + ASSERT_EQ(AllocationResult_SUCCESS, iox_pub_loan_chunk(&m_sut, &chunk, sizeof(DummySample))); + auto header = iox_chunk_payload_to_header(chunk); + auto payload = iox_chunk_header_to_payload(header); + EXPECT_EQ(payload, chunk); } TEST_F(iox_pub_test, allocate_chunkFailsWhenHoldingToManyChunksInParallel) @@ -165,10 +186,10 @@ TEST_F(iox_pub_test, allocate_chunkFailsWhenHoldingToManyChunksInParallel) void* chunk = nullptr; for (int i = 0; i < 8 /* ///@todo actually it should be MAX_CHUNKS_HELD_PER_RECEIVER but it does not work*/; ++i) { - EXPECT_EQ(AllocationResult_SUCCESS, iox_pub_allocate_chunk(&m_sut, &chunk, 100)); + EXPECT_EQ(AllocationResult_SUCCESS, iox_pub_loan_chunk(&m_sut, &chunk, 100)); } - EXPECT_EQ(AllocationResult_TOO_MANY_CHUNKS_ALLOCATED_IN_PARALLEL, iox_pub_allocate_chunk(&m_sut, &chunk, 100)); + EXPECT_EQ(AllocationResult_TOO_MANY_CHUNKS_ALLOCATED_IN_PARALLEL, iox_pub_loan_chunk(&m_sut, &chunk, 100)); } TEST_F(iox_pub_test, allocate_chunkFailsWhenOutOfChunks) @@ -184,36 +205,36 @@ TEST_F(iox_pub_test, allocate_chunkFailsWhenOutOfChunks) } void* chunk = nullptr; - EXPECT_EQ(AllocationResult_RUNNING_OUT_OF_CHUNKS, iox_pub_allocate_chunk(&m_sut, &chunk, 100)); + EXPECT_EQ(AllocationResult_RUNNING_OUT_OF_CHUNKS, iox_pub_loan_chunk(&m_sut, &chunk, 100)); } TEST_F(iox_pub_test, allocatingChunkAcquiresMemory) { void* chunk = nullptr; - iox_pub_allocate_chunk(&m_sut, &chunk, 100); + iox_pub_loan_chunk(&m_sut, &chunk, 100); EXPECT_THAT(m_memoryManager.getMemPoolInfo(0).m_usedChunks, Eq(1u)); } TEST_F(iox_pub_test, freeingAnAllocatedChunkReleasesTheMemory) { void* chunk = nullptr; - iox_pub_allocate_chunk(&m_sut, &chunk, 100); - iox_pub_free_chunk(&m_sut, chunk); + iox_pub_loan_chunk(&m_sut, &chunk, 100); + iox_pub_release_chunk(&m_sut, chunk); EXPECT_THAT(m_memoryManager.getMemPoolInfo(0).m_usedChunks, Eq(0u)); } TEST_F(iox_pub_test, noLastChunkWhenNothingSent) { - EXPECT_EQ(iox_pub_try_get_previous_chunk(&m_sut), nullptr); + EXPECT_EQ(iox_pub_loan_previous_chunk(&m_sut), nullptr); } TEST_F(iox_pub_test, lastChunkAvailableAfterSend) { void* chunk = nullptr; - iox_pub_allocate_chunk(&m_sut, &chunk, 100); - iox_pub_send_chunk(&m_sut, chunk); + iox_pub_loan_chunk(&m_sut, &chunk, 100); + iox_pub_publish_chunk(&m_sut, chunk); - const void* lastChunk = iox_pub_try_get_previous_chunk(&m_sut); + const void* lastChunk = iox_pub_loan_previous_chunk(&m_sut); EXPECT_EQ(chunk, lastChunk); } @@ -223,9 +244,9 @@ TEST_F(iox_pub_test, sendDeliversChunk) void* chunk = nullptr; iox_pub_offer(&m_sut); this->Subscribe(&m_publisherPortData); - iox_pub_allocate_chunk(&m_sut, &chunk, 100); + iox_pub_loan_chunk(&m_sut, &chunk, 100); static_cast(chunk)->dummy = 4711; - iox_pub_send_chunk(&m_sut, chunk); + iox_pub_publish_chunk(&m_sut, chunk); iox::popo::ChunkQueuePopper m_chunkQueuePopper(&m_chunkQueueData); auto maybeSharedChunk = m_chunkQueuePopper.tryPop(); @@ -235,3 +256,52 @@ TEST_F(iox_pub_test, sendDeliversChunk) EXPECT_TRUE(static_cast(maybeSharedChunk->getPayload())->dummy == 4711); } +TEST(iox_pub_options_test, publisherOptionsAreInitializedCorrectly) +{ + iox_pub_options_t sut; + sut.historyCapacity = 37; + sut.nodeName = "Dr.Gonzo"; + sut.offerOnCreate = false; + + PublisherOptions options; + // set offerOnCreate to the opposite of the expected default to check if it gets overwritten to default + sut.offerOnCreate = (options.offerOnCreate == false) ? true : false; + + iox_pub_options_init(&sut); + EXPECT_EQ(sut.historyCapacity, options.historyCapacity); + EXPECT_EQ(sut.nodeName, nullptr); + EXPECT_EQ(sut.offerOnCreate, options.offerOnCreate); + EXPECT_TRUE(iox_pub_options_is_initialized(&sut)); +} + +TEST(iox_pub_options_test, publisherOptionsInitializationCheckReturnsTrueAfterDefaultInit) +{ + iox_pub_options_t sut; + iox_pub_options_init(&sut); + EXPECT_TRUE(iox_pub_options_is_initialized(&sut)); +} + +TEST(iox_pub_options_test, publisherOptionsInitializationCheckReturnsFalseWithoutDefaultInit) +{ + iox_pub_options_t sut; + EXPECT_FALSE(iox_pub_options_is_initialized(&sut)); +} + +TEST(iox_pub_options_test, publisherOptionInitializationWithNullptrDoesNotCrash) +{ + EXPECT_EXIT( + { + iox_pub_options_init(nullptr); + exit(0); + }, + ::testing::ExitedWithCode(0), + ".*"); +} + +TEST(iox_pub_options_test, publisherInitializationTerminatesIfOptionsAreNotInitialized) +{ + iox_pub_options_t options; + iox_pub_storage_t storage; + + EXPECT_DEATH({ iox_pub_init(&storage, "a", "b", "c", &options); }, ".*"); +} diff --git a/iceoryx_binding_c/test/moduletests/test_subscriber.cpp b/iceoryx_binding_c/test/moduletests/test_subscriber.cpp index 900cd321637..68d15fbd58f 100644 --- a/iceoryx_binding_c/test/moduletests/test_subscriber.cpp +++ b/iceoryx_binding_c/test/moduletests/test_subscriber.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. @@ -31,6 +32,8 @@ using namespace iox::cxx; using namespace iox::posix; extern "C" { +#include "iceoryx_binding_c/chunk.h" +#include "iceoryx_binding_c/runtime.h" #include "iceoryx_binding_c/subscriber.h" #include "iceoryx_binding_c/types.h" #include "iceoryx_binding_c/wait_set.h" @@ -157,7 +160,7 @@ TEST_F(iox_sub_test, UnsubscribeLeadsToUnscribeRequestedState) TEST_F(iox_sub_test, initialStateNoChunksAvailable) { const void* chunk = nullptr; - EXPECT_EQ(iox_sub_get_chunk(m_sut, &chunk), ChunkReceiveResult_NO_CHUNK_RECEIVED); + EXPECT_EQ(iox_sub_take_chunk(m_sut, &chunk), ChunkReceiveResult_NO_CHUNK_AVAILABLE); } TEST_F(iox_sub_test, receiveChunkWhenThereIsOne) @@ -166,7 +169,7 @@ TEST_F(iox_sub_test, receiveChunkWhenThereIsOne) m_chunkPusher.push(m_memoryManager.getChunk(100U)); const void* chunk = nullptr; - EXPECT_EQ(iox_sub_get_chunk(m_sut, &chunk), ChunkReceiveResult_SUCCESS); + EXPECT_EQ(iox_sub_take_chunk(m_sut, &chunk), ChunkReceiveResult_SUCCESS); } TEST_F(iox_sub_test, receiveChunkWithContent) @@ -183,10 +186,25 @@ TEST_F(iox_sub_test, receiveChunkWithContent) const void* chunk = nullptr; - ASSERT_EQ(iox_sub_get_chunk(m_sut, &chunk), ChunkReceiveResult_SUCCESS); + ASSERT_EQ(iox_sub_take_chunk(m_sut, &chunk), ChunkReceiveResult_SUCCESS); EXPECT_THAT(static_cast(chunk)->value, Eq(1234)); } +TEST_F(iox_sub_test, chunkHeaderCanBeObtainedFromChunkAfterTake) +{ + this->Subscribe(&m_portPtr); + auto sharedChunk = m_memoryManager.getChunk(100U); + m_chunkPusher.push(sharedChunk); + + const void* chunk = nullptr; + + ASSERT_EQ(iox_sub_take_chunk(m_sut, &chunk), ChunkReceiveResult_SUCCESS); + auto header = iox_chunk_payload_to_header(chunk); + ASSERT_NE(header, nullptr); + auto payload = iox_chunk_header_to_payload(header); + EXPECT_EQ(payload, chunk); +} + TEST_F(iox_sub_test, receiveChunkWhenToManyChunksAreHold) { this->Subscribe(&m_portPtr); @@ -194,11 +212,11 @@ TEST_F(iox_sub_test, receiveChunkWhenToManyChunksAreHold) for (uint64_t i = 0U; i < MAX_CHUNKS_HELD_PER_SUBSCRIBER_SIMULTANEOUSLY + 1U; ++i) { m_chunkPusher.push(m_memoryManager.getChunk(100U)); - iox_sub_get_chunk(m_sut, &chunk); + iox_sub_take_chunk(m_sut, &chunk); } m_chunkPusher.push(m_memoryManager.getChunk(100U)); - EXPECT_EQ(iox_sub_get_chunk(m_sut, &chunk), ChunkReceiveResult_TOO_MANY_CHUNKS_HELD_IN_PARALLEL); + EXPECT_EQ(iox_sub_take_chunk(m_sut, &chunk), ChunkReceiveResult_TOO_MANY_CHUNKS_HELD_IN_PARALLEL); } TEST_F(iox_sub_test, releaseChunkWorks) @@ -207,7 +225,7 @@ TEST_F(iox_sub_test, releaseChunkWorks) m_chunkPusher.push(m_memoryManager.getChunk(100U)); const void* chunk = nullptr; - iox_sub_get_chunk(m_sut, &chunk); + iox_sub_take_chunk(m_sut, &chunk); EXPECT_THAT(m_memoryManager.getMemPoolInfo(0).m_usedChunks, Eq(1U)); iox_sub_release_chunk(m_sut, chunk); @@ -295,8 +313,7 @@ TEST_F(iox_sub_test, hasDataTriggersWaitSetWithCorrectEventId) TEST_F(iox_sub_test, hasDataTriggersWaitSetWithCorrectCallback) { - iox_ws_attach_subscriber_event( - m_waitSet.get(), m_sut, SubscriberEvent_HAS_DATA, 0U, iox_sub_test::triggerCallback); + iox_ws_attach_subscriber_event(m_waitSet.get(), m_sut, SubscriberEvent_HAS_DATA, 0U, iox_sub_test::triggerCallback); this->Subscribe(&m_portPtr); m_chunkPusher.push(m_memoryManager.getChunk(100U)); @@ -322,3 +339,55 @@ TEST_F(iox_sub_test, deinitSubscriberDetachesTriggerFromWaitSet) free(subscriber); } + +TEST(iox_sub_options_test, subscriberOptionsAreInitializedCorrectly) +{ + iox_sub_options_t sut; + sut.queueCapacity = 37; + sut.historyRequest = 73; + sut.nodeName = "Dr.Gonzo"; + sut.subscribeOnCreate = false; + + SubscriberOptions options; + // set subscribeOnCreate to the opposite of the expected default to check if it gets overwritten to default + sut.subscribeOnCreate = (options.subscribeOnCreate == false) ? true : false; + + iox_sub_options_init(&sut); + EXPECT_EQ(sut.queueCapacity, options.queueCapacity); + EXPECT_EQ(sut.historyRequest, options.historyRequest); + EXPECT_EQ(sut.nodeName, nullptr); + EXPECT_EQ(sut.subscribeOnCreate, options.subscribeOnCreate); + EXPECT_TRUE(iox_sub_options_is_initialized(&sut)); +} + +TEST(iox_sub_options_test, subscriberOptionsInitializationCheckReturnsTrueAfterDefaultInit) +{ + iox_sub_options_t sut; + iox_sub_options_init(&sut); + EXPECT_TRUE(iox_sub_options_is_initialized(&sut)); +} + +TEST(iox_sub_options_test, subscriberOptionsInitializationCheckReturnsFalseWithoutDefaultInit) +{ + iox_sub_options_t sut; + EXPECT_FALSE(iox_sub_options_is_initialized(&sut)); +} + +TEST(iox_sub_options_test, subscriberOptionInitializationWithNullptrDoesNotCrash) +{ + EXPECT_EXIT( + { + iox_sub_options_init(nullptr); + exit(0); + }, + ::testing::ExitedWithCode(0), + ".*"); +} + +TEST(iox_sub_options_test, subscriberInitializationTerminatesIfOptionsAreNotInitialized) +{ + iox_sub_options_t options; + iox_sub_storage_t storage; + + EXPECT_DEATH({ iox_sub_init(&storage, "a", "b", "c", &options); }, ".*"); +} diff --git a/iceoryx_dds/include/iceoryx_dds/internal/gateway/dds_to_iox.inl b/iceoryx_dds/include/iceoryx_dds/internal/gateway/dds_to_iox.inl index 728beeb2ab7..12d03f2f3d7 100644 --- a/iceoryx_dds/include/iceoryx_dds/internal/gateway/dds_to_iox.inl +++ b/iceoryx_dds/include/iceoryx_dds/internal/gateway/dds_to_iox.inl @@ -68,6 +68,7 @@ inline void DDS2IceoryxGateway::forward(const channel_t& c reader->takeNext(static_cast(chunk), size) .and_then([&]() { publisher->publish(chunk); }) .or_else([&](DataReaderError err) { + publisher->release(chunk); LogWarn() << "[DDS2IceoryxGateway] Encountered error reading from DDS network: " << dds::DataReaderErrorString[static_cast(err)]; }); diff --git a/iceoryx_dds/include/iceoryx_dds/internal/gateway/iox_to_dds.inl b/iceoryx_dds/include/iceoryx_dds/internal/gateway/iox_to_dds.inl index bce07e3f315..5c123be4379 100644 --- a/iceoryx_dds/include/iceoryx_dds/internal/gateway/iox_to_dds.inl +++ b/iceoryx_dds/include/iceoryx_dds/internal/gateway/iox_to_dds.inl @@ -111,7 +111,7 @@ inline void Iceoryx2DDSGateway::forward(const channel_t& c auto dataWriter = channel.getExternalTerminal(); auto header = iox::mepoo::ChunkHeader::fromPayload(payload); dataWriter->write(static_cast(payload), header->payloadSize); - subscriber->releaseChunk(payload); + subscriber->release(payload); }); } } diff --git a/iceoryx_examples/README.md b/iceoryx_examples/README.md index 9f9d7a47d79..ef63b1a66a6 100644 --- a/iceoryx_examples/README.md +++ b/iceoryx_examples/README.md @@ -22,6 +22,7 @@ cd someExample | Example | Description |Level | |:-------------------------------------------------------|:------------|:----:| +|[callbacks](./callbacks/) | Implement callbacks which are triggered by events. | 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 | diff --git a/iceoryx_examples/callbacks/CMakeLists.txt b/iceoryx_examples/callbacks/CMakeLists.txt new file mode 100644 index 00000000000..e76847e26e5 --- /dev/null +++ b/iceoryx_examples/callbacks/CMakeLists.txt @@ -0,0 +1,53 @@ +# 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) + +include(GNUInstallDirs) + +find_package(iceoryx_posh CONFIG REQUIRED) + +get_target_property(ICEORYX_CXX_STANDARD iceoryx_posh::iceoryx_posh CXX_STANDARD) +if ( NOT ICEORYX_CXX_STANDARD ) + include(IceoryxPlatform) +endif () + +add_executable(iox-ex-callbacks-publisher ./ice_callbacks_publisher.cpp) +target_link_libraries(iox-ex-callbacks-publisher + iceoryx_posh::iceoryx_posh +) +target_compile_options(iox-ex-callbacks-publisher PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) + +add_executable(iox-ex-callbacks-subscriber ./ice_callbacks_subscriber.cpp) +target_link_libraries(iox-ex-callbacks-subscriber + iceoryx_posh::iceoryx_posh +) +target_compile_options(iox-ex-callbacks-subscriber PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) + +set_target_properties( + iox-ex-callbacks-subscriber + iox-ex-callbacks-publisher + PROPERTIES + CXX_STANDARD_REQUIRED ON + CXX_STANDARD ${ICEORYX_CXX_STANDARD} + POSITION_INDEPENDENT_CODE ON +) + +install( + TARGETS iox-ex-callbacks-publisher iox-ex-callbacks-subscriber + RUNTIME DESTINATION bin +) diff --git a/iceoryx_examples/callbacks/README.md b/iceoryx_examples/callbacks/README.md new file mode 100644 index 00000000000..01f23349ac5 --- /dev/null +++ b/iceoryx_examples/callbacks/README.md @@ -0,0 +1,2 @@ +# ActiveCallSet - WORK IN PROGRESS - + diff --git a/iceoryx_examples/callbacks/ice_callbacks_publisher.cpp b/iceoryx_examples/callbacks/ice_callbacks_publisher.cpp new file mode 100644 index 00000000000..776eba8a580 --- /dev/null +++ b/iceoryx_examples/callbacks/ice_callbacks_publisher.cpp @@ -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 + +#include "iceoryx_posh/popo/publisher.hpp" +#include "iceoryx_posh/runtime/posh_runtime.hpp" +#include "iceoryx_utils/posix_wrapper/signal_handler.hpp" +#include "topic_data.hpp" + +#include +#include +#include + +bool killswitch = false; + +static void sigHandler(int f_sig [[gnu::unused]]) +{ + killswitch = true; +} + +void sending() +{ + iox::runtime::PoshRuntime::initRuntime("iox-ex-callbacks-publisher"); + + iox::popo::Publisher myPublisher({"Radar", "FrontLeft", "Counter"}); + myPublisher.offer(); + + for (uint32_t counter = 0U; !killswitch; ++counter) + { + myPublisher.publishCopyOf(CounterTopic{counter}); + std::cout << "Sending: " << counter << std::endl; + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + } + + myPublisher.stopOffer(); +} + +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(); + + return (EXIT_SUCCESS); +} diff --git a/iceoryx_examples/callbacks/ice_callbacks_subscriber.cpp b/iceoryx_examples/callbacks/ice_callbacks_subscriber.cpp new file mode 100644 index 00000000000..7f21dc2c829 --- /dev/null +++ b/iceoryx_examples/callbacks/ice_callbacks_subscriber.cpp @@ -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 + +#include "iceoryx_posh/popo/active_call_set.hpp" +#include "iceoryx_posh/popo/subscriber.hpp" +#include "iceoryx_posh/popo/user_trigger.hpp" +#include "iceoryx_posh/runtime/posh_runtime.hpp" +#include "iceoryx_utils/posix_wrapper/semaphore.hpp" +#include "iceoryx_utils/posix_wrapper/signal_handler.hpp" +#include "topic_data.hpp" + +#include +#include +#include + +iox::popo::UserTrigger shutdownTrigger; +iox::posix::Semaphore mainLoopBlocker = + iox::posix::Semaphore::create(iox::posix::CreateUnnamedSingleProcessSemaphore, 0).value(); + +std::atomic_bool keepRunning{true}; + +static void sigHandler(int f_sig [[gnu::unused]]) +{ + shutdownTrigger.trigger(); +} + +void shutdownTriggerCallback(iox::popo::UserTrigger*) +{ + keepRunning.store(false); +} + +void onSampleReceived(iox::popo::Subscriber* subscriber) +{ + subscriber->take().and_then([](auto& sample) { printf("received: %d\n", sample->counter); }); +} + +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::popo::ActiveCallSet callSet; + + // attach shutdownTrigger to handle CTRL+C + callSet.attachEvent(shutdownTrigger, shutdownTriggerCallback); + + iox::popo::Subscriber subscriber({"Radar", "FrontLeft", "Counter"}); + + subscriber.subscribe(); + + callSet.attachEvent(subscriber, iox::popo::SubscriberEvent::HAS_DATA, onSampleReceived); + + while (keepRunning) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + callSet.detachEvent(shutdownTrigger); + callSet.detachEvent(subscriber, iox::popo::SubscriberEvent::HAS_DATA); + + return (EXIT_SUCCESS); +} diff --git a/iceoryx_examples/callbacks/topic_data.hpp b/iceoryx_examples/callbacks/topic_data.hpp new file mode 100644 index 00000000000..63a122f0212 --- /dev/null +++ b/iceoryx_examples/callbacks/topic_data.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_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/ice_multi_publisher/README.md b/iceoryx_examples/ice_multi_publisher/README.md index fd94d411de8..2142b70655c 100644 --- a/iceoryx_examples/ice_multi_publisher/README.md +++ b/iceoryx_examples/ice_multi_publisher/README.md @@ -113,10 +113,8 @@ iox::capro::IdString instance{iox::cxx::TruncateToCapacity, instanceName}; iox::popo::Publisher publisher({"Group", instance, "Counter"}); ``` -After construction, we immediately offer the topic and start sending data. +After construction, we immediately start sending data. ```cpp -publisher.offer(); - for (uint32_t counter = 0U; !killswitch; ++counter) { CounterTopic data{counter, id}; @@ -150,11 +148,6 @@ We create a subscriber via iox::popo::Subscriber subscriber({"Group", "Instance", "Counter"}); ``` -and immediately subscribe. -```cpp -subscriber.subscribe(); -``` - Notice that all identifiers match the ones provided by the two publishers. We periodically wake up @@ -184,10 +177,7 @@ and wait for some time before looking for data again. std::cout << "Waiting for data ... " << std::endl; ``` -When Ctrl+C is pressed we exit the loop and unsubscribe -```cpp -subscriber.unsubscribe(); -``` +When Ctrl+C is pressed we exit the loop before joining the receiver thread ```cpp diff --git a/iceoryx_examples/ice_multi_publisher/ice_multi_publisher.cpp b/iceoryx_examples/ice_multi_publisher/ice_multi_publisher.cpp index cf67da4f979..0322cc427d2 100644 --- a/iceoryx_examples/ice_multi_publisher/ice_multi_publisher.cpp +++ b/iceoryx_examples/ice_multi_publisher/ice_multi_publisher.cpp @@ -42,7 +42,6 @@ void send(uint32_t id, const char* instanceName, std::chrono::milliseconds delay // All three of the string identifiers together uniquely identify a topic // and can also depend on values known only at runtime (like instance in this case). iox::popo::Publisher publisher({"Group", instance, "Counter"}, publisherOptions); - publisher.offer(); for (uint32_t counter = 0U; !killswitch; ++counter) { diff --git a/iceoryx_examples/ice_multi_publisher/ice_subscriber.cpp b/iceoryx_examples/ice_multi_publisher/ice_subscriber.cpp index 9a35260f7d2..35617263f2f 100644 --- a/iceoryx_examples/ice_multi_publisher/ice_subscriber.cpp +++ b/iceoryx_examples/ice_multi_publisher/ice_subscriber.cpp @@ -36,8 +36,6 @@ void receive() iox::popo::Subscriber subscriber({"Group", "Instance", "Counter"}, subscriberOptions); - subscriber.subscribe(); - while (!killswitch) { std::this_thread::sleep_for(std::chrono::seconds(1)); @@ -50,7 +48,6 @@ void receive() }; std::cout << "Waiting for data ... " << std::endl; } - subscriber.unsubscribe(); } int main() diff --git a/iceoryx_examples/icecrystal/Readme.md b/iceoryx_examples/icecrystal/Readme.md index 97a300cfb59..805cb8da790 100644 --- a/iceoryx_examples/icecrystal/Readme.md +++ b/iceoryx_examples/icecrystal/Readme.md @@ -50,15 +50,10 @@ The process view will show you the processes (incl. PID), which are currently re --port Subscribe to port introspection data. -The port view shows both publisher and subscriber ports that are created by RouDi in the shared memory. Their respective -service description (service, instance, event) is shown to identify them uniquely. The columns `Process` and -`used by process` display to which process the ports belong and how they are currently connected. Size in bytes of -both sample size and chunk size (sample size + meta data) and statistical data of `Chunks [/Minute]` is provided as -well. When a publisher port instantly provides data to a subscriber with the `subscribe()` call, the `Field` column is -ticked. The service discovery protocol allows you to define the `Propagation scope` of the data. This can enable -data forwarding to other machines e.g. over network or just consume them internally. When a `Callback` is -registered on subscriber side, the box is ticked accordingly. `FiFo size / capacity` shows the consumption of chunks -on the subscriber side and is a useful column to debug potential memleaks. +The port view shows both publisher and subscriber ports that are created by RouDi in the shared memory. Their respective service +description (service, instance, event) is shown to identify them uniquely. The columns `Process` and `Node` display to which +process and node the ports belong. The service discovery protocol allows you to define the `Propagation scope` of the data. This +can enable data forwarding to other machines e.g. over network or just consume them internally. --all Subscribe to all available introspection data. diff --git a/iceoryx_examples/icedelivery/CMakeLists.txt b/iceoryx_examples/icedelivery/CMakeLists.txt index 9df83c2cd4a..082b2b1f340 100644 --- a/iceoryx_examples/icedelivery/CMakeLists.txt +++ b/iceoryx_examples/icedelivery/CMakeLists.txt @@ -34,11 +34,11 @@ target_link_libraries(iox-ex-publisher ) target_compile_options(iox-ex-publisher PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) -add_executable(iox-ex-publisher-with-history ./iox_publisher_with_history.cpp) -target_link_libraries(iox-ex-publisher-with-history +add_executable(iox-ex-publisher-with-options ./iox_publisher_with_options.cpp) +target_link_libraries(iox-ex-publisher-with-options iceoryx_posh::iceoryx_posh ) -target_compile_options(iox-ex-publisher-with-history PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) +target_compile_options(iox-ex-publisher-with-options PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) add_executable(iox-ex-publisher-untyped ./iox_publisher_untyped.cpp) target_link_libraries(iox-ex-publisher-untyped @@ -52,11 +52,11 @@ target_link_libraries(iox-ex-subscriber ) target_compile_options(iox-ex-subscriber PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) -add_executable(iox-ex-subscriber-with-history ./iox_subscriber_with_history.cpp) -target_link_libraries(iox-ex-subscriber-with-history +add_executable(iox-ex-subscriber-with-options ./iox_subscriber_with_options.cpp) +target_link_libraries(iox-ex-subscriber-with-options iceoryx_posh::iceoryx_posh ) -target_compile_options(iox-ex-subscriber-with-history PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) +target_compile_options(iox-ex-subscriber-with-options PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) add_executable(iox-ex-subscriber-untyped ./iox_subscriber_untyped.cpp) target_link_libraries(iox-ex-subscriber-untyped @@ -65,7 +65,7 @@ target_link_libraries(iox-ex-subscriber-untyped target_compile_options(iox-ex-subscriber-untyped PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) set_target_properties(iox-ex-subscriber iox-ex-subscriber-untyped - iox-ex-publisher-with-history iox-ex-subscriber-with-history + iox-ex-publisher-with-options iox-ex-subscriber-with-options iox-ex-publisher iox-ex-publisher-untyped PROPERTIES CXX_STANDARD_REQUIRED ON @@ -76,6 +76,6 @@ set_target_properties(iox-ex-subscriber iox-ex-subscriber-untyped # ========================================================== // install(TARGETS iox-ex-publisher-untyped iox-ex-publisher - iox-ex-publisher-with-history iox-ex-subscriber-with-history + iox-ex-publisher-with-options iox-ex-subscriber-with-options iox-ex-subscriber-untyped iox-ex-subscriber RUNTIME DESTINATION bin) diff --git a/iceoryx_examples/icedelivery/README.md b/iceoryx_examples/icedelivery/README.md index 099eff2c46e..96342add371 100644 --- a/iceoryx_examples/icedelivery/README.md +++ b/iceoryx_examples/icedelivery/README.md @@ -73,11 +73,10 @@ unique string identifier for this publisher. iox::runtime::PoshRuntime::initRuntime("iox-ex-publisher"); ``` -Now that RouDi knows our publisher application is existing, let's create a publisher instance and offer our charming struct +Now that RouDi knows our publisher application is existing, let's create a publisher instance for our charming struct to everyone: ```cpp iox::popo::UntypedPublisher untypedPublisher({"Radar", "FrontLeft", "Object"}); -untypedPublisher.offer(); ``` The strings inside the first parameter of the constructor of `iox::popo::Publisher` are of the type @@ -184,11 +183,6 @@ are created, a default value will be used which sets the queueCapacity to the ma iox::popo::UntypedSubscriber untypedSubscriber({"Radar", "FrontLeft", "Object"}, subscriberOptions); ``` -After the creation, the subscriber object subscribes to the offered data -```cpp -untypedSubscriber.subscribe(); -``` - When using the default n:m communication philosophy, the `SubscriptionState` is immediately `SUBSCRIBED`. However, when restricting iceoryx to the 1:n communication philosophy before being in the state `SUBSCRIBED`, the state is change to `SUBSCRIBE_REQUESTED`. diff --git a/iceoryx_examples/icedelivery/iox_publisher.cpp b/iceoryx_examples/icedelivery/iox_publisher.cpp index 63ba6bdae27..c45d8b645e2 100644 --- a/iceoryx_examples/icedelivery/iox_publisher.cpp +++ b/iceoryx_examples/icedelivery/iox_publisher.cpp @@ -45,7 +45,6 @@ int main() iox::runtime::PoshRuntime::initRuntime("iox-ex-publisher"); iox::popo::Publisher publisher({"Radar", "FrontLeft", "Object"}); - publisher.offer(); double ct = 0.0; while (!killswitch) diff --git a/iceoryx_examples/icedelivery/iox_publisher_untyped.cpp b/iceoryx_examples/icedelivery/iox_publisher_untyped.cpp index ea8a3187dcd..0e8918f022a 100644 --- a/iceoryx_examples/icedelivery/iox_publisher_untyped.cpp +++ b/iceoryx_examples/icedelivery/iox_publisher_untyped.cpp @@ -39,8 +39,7 @@ int main() iox::runtime::PoshRuntime::initRuntime("iox-ex-publisher-untyped"); - iox::popo::UntypedPublisher publisher({"Radar", "FrontRight", "Object"}); - publisher.offer(); + iox::popo::UntypedPublisher publisher({"Radar", "FrontLeft", "Object"}); double ct = 0.0; while (!killswitch) diff --git a/iceoryx_examples/icedelivery/iox_publisher_with_history.cpp b/iceoryx_examples/icedelivery/iox_publisher_with_options.cpp similarity index 77% rename from iceoryx_examples/icedelivery/iox_publisher_with_history.cpp rename to iceoryx_examples/icedelivery/iox_publisher_with_options.cpp index 00469051a18..5e355a1d7db 100644 --- a/iceoryx_examples/icedelivery/iox_publisher_with_history.cpp +++ b/iceoryx_examples/icedelivery/iox_publisher_with_options.cpp @@ -36,13 +36,23 @@ 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-history"); + iox::runtime::PoshRuntime::initRuntime("iox-ex-publisher-with-options"); - // create publisher options to set a historyCapacity of 10U + // create publisher with some options set iox::popo::PublisherOptions publisherOptions; + + // the publishers stores the last 10 samples for possible late joiners publisherOptions.historyCapacity = 10U; - iox::popo::Publisher publisher({"Radar", "RearLeft", "Object"}, publisherOptions); + // when the publisher is created, it is not yet visible + publisherOptions.offerOnCreate = false; + + // grouping of publishers and subscribers within a process + publisherOptions.nodeName = "Pub_Node_With_Options"; + + iox::popo::Publisher publisher({"Radar", "FrontLeft", "Object"}, publisherOptions); + + // we have to explicitely offer the publisher for making it visible to subscribers publisher.offer(); double ct = 0.0; diff --git a/iceoryx_examples/icedelivery/iox_subscriber.cpp b/iceoryx_examples/icedelivery/iox_subscriber.cpp index 542acfda206..a9703209ba2 100644 --- a/iceoryx_examples/icedelivery/iox_subscriber.cpp +++ b/iceoryx_examples/icedelivery/iox_subscriber.cpp @@ -44,7 +44,6 @@ int main() iox::popo::SubscriberOptions subscriberOptions; subscriberOptions.queueCapacity = 10U; iox::popo::Subscriber subscriber({"Radar", "FrontLeft", "Object"}, subscriberOptions); - subscriber.subscribe(); // run until interrupted by Ctrl-C while (!killswitch) @@ -71,7 +70,5 @@ int main() std::this_thread::sleep_for(std::chrono::milliseconds(100)); } - subscriber.unsubscribe(); - return (EXIT_SUCCESS); } diff --git a/iceoryx_examples/icedelivery/iox_subscriber_untyped.cpp b/iceoryx_examples/icedelivery/iox_subscriber_untyped.cpp index 22bfc331f16..a73035e6e71 100644 --- a/iceoryx_examples/icedelivery/iox_subscriber_untyped.cpp +++ b/iceoryx_examples/icedelivery/iox_subscriber_untyped.cpp @@ -43,8 +43,7 @@ int main() // initialized subscriber iox::popo::SubscriberOptions subscriberOptions; subscriberOptions.queueCapacity = 10U; - iox::popo::UntypedSubscriber subscriber({"Radar", "FrontRight", "Object"}, subscriberOptions); - subscriber.subscribe(); + iox::popo::UntypedSubscriber subscriber({"Radar", "FrontLeft", "Object"}, subscriberOptions); // run until interrupted by Ctrl-C while (!killswitch) @@ -58,7 +57,7 @@ int main() // note that we explicitly have to release the data // and afterwards the pointer access is undefined behavior - subscriber.releaseChunk(data); + subscriber.release(data); }) .or_else([](auto& result) { if (result != iox::popo::ChunkReceiveResult::NO_CHUNK_AVAILABLE) @@ -75,7 +74,5 @@ int main() std::this_thread::sleep_for(std::chrono::milliseconds(100)); } - subscriber.unsubscribe(); - return (EXIT_SUCCESS); } diff --git a/iceoryx_examples/icedelivery/iox_subscriber_with_history.cpp b/iceoryx_examples/icedelivery/iox_subscriber_with_options.cpp similarity index 70% rename from iceoryx_examples/icedelivery/iox_subscriber_with_history.cpp rename to iceoryx_examples/icedelivery/iox_subscriber_with_options.cpp index ffa4cdb98d9..18edeaa7748 100644 --- a/iceoryx_examples/icedelivery/iox_subscriber_with_history.cpp +++ b/iceoryx_examples/icedelivery/iox_subscriber_with_options.cpp @@ -37,16 +37,29 @@ int main() auto signalTermGuard = iox::posix::registerSignalHandler(iox::posix::Signal::TERM, sigHandler); // initialize runtime - iox::runtime::PoshRuntime::initRuntime("iox-ex-subscriber-with-history"); + iox::runtime::PoshRuntime::initRuntime("iox-ex-subscriber-with-options"); - // initialized subscriber + // create subscriber with some options set iox::popo::SubscriberOptions subscriberOptions; + + // the queue can hold 10 samples, on overflow the oldest sample will be replaced with the new arriving one subscriberOptions.queueCapacity = 10U; + // When starting the subscriber late it will miss the first samples which the - // publisher has send. The history ensures that we at least get the last 5 - // samples sent by the publisher when we subscribe (if at least 5 were already sent). + // publisher has send. The history request ensures that we at least get the last 5 + // samples sent by the publisher when we subscribe (if at least 5 were already sent + // and the publisher has history enabled). subscriberOptions.historyRequest = 5U; - iox::popo::Subscriber subscriber({"Radar", "RearLeft", "Object"}, subscriberOptions); + + // when the subscriber is created, no attempts are made to connect to any publishers that may exist + subscriberOptions.subscribeOnCreate = false; + + // grouping of publishers and subscribers within a process + subscriberOptions.nodeName = "Sub_Node_With_Options"; + + iox::popo::Subscriber subscriber({"Radar", "FrontLeft", "Object"}, subscriberOptions); + + // We have to explicitly call subscribe() otherwise the subscriber will not try to connect to publishers subscriber.subscribe(); // run until interrupted by Ctrl-C @@ -60,7 +73,9 @@ int main() do { subscriber.take() - .and_then([](auto& object) { std::cout << "Iox-Subscriber-with-history got value: " << object->x << std::endl; }) + .and_then([](auto& object) { + std::cout << "Iox-Subscriber-with-history 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 c8ff3e31a17..3c006078dc4 100644 --- a/iceoryx_examples/icedelivery_in_c/CMakeLists.txt +++ b/iceoryx_examples/icedelivery_in_c/CMakeLists.txt @@ -34,7 +34,6 @@ 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 - iceoryx_posh::iceoryx_posh iceoryx_binding_c::iceoryx_binding_c ) set_target_properties(iox-c-publisher PROPERTIES @@ -45,7 +44,6 @@ target_compile_options(iox-c-publisher PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SAN add_executable(iox-c-subscriber ./ice_c_subscriber.c) set_source_files_properties(./ice_c_subscriber.c PROPERTIES LANGUAGE C) target_link_libraries(iox-c-subscriber - iceoryx_posh::iceoryx_posh iceoryx_binding_c::iceoryx_binding_c ) set_target_properties(iox-c-subscriber PROPERTIES diff --git a/iceoryx_examples/icedelivery_in_c/README.md b/iceoryx_examples/icedelivery_in_c/README.md index d5408d777c7..e820e9fa55b 100644 --- a/iceoryx_examples/icedelivery_in_c/README.md +++ b/iceoryx_examples/icedelivery_in_c/README.md @@ -46,19 +46,17 @@ Let's take a look at the `receiving` function which comes with the The `subscriberStorage` is the place where the subscriber is stored in memory and `subscriber` is actually a pointer to that location. ```c - const uint64_t historyRequest = 10U; - const uint64_t queueCapacity = 5U; - const char* const nodeName = "iox-c-subscriber-node"; + iox_sub_options_t options; + iox_sub_options_init(&options); + options.historyRequest = 10U; + options.queueCapacity = 5U; + options.nodeName = "iox-c-subscriber-node"; + iox_sub_storage_t subscriberStorage; - iox_sub_t subscriber = iox_sub_init(&subscriberStorage, "Radar", "FrontLeft", "Object", queueCapacity, historyRequest, nodeName); + iox_sub_t subscriber = iox_sub_init(&subscriberStorage, "Radar", "FrontLeft", "Object", &options); ``` - 3. We subscribe to the service. - ```c - iox_sub_subscribe(subscriber); - ``` - - 4. In this loop we receive samples as long the `killswitch` is not + 3. In this loop we receive samples as long the `killswitch` is not set to `true` by an external signal and then print the counter value to the console. ```c @@ -67,7 +65,7 @@ Let's take a look at the `receiving` function which comes with the if (SubscribeState_SUBSCRIBED == iox_sub_get_subscription_state(subscriber)) { const void* chunk = NULL; - while (ChunkReceiveResult_SUCCESS == iox_sub_get_chunk(subscriber, &chunk)) + while (ChunkReceiveResult_SUCCESS == iox_sub_take_chunk(subscriber, &chunk)) { const struct RadarObject* sample = (const struct RadarObject*)(chunk); printf("Got value: %.0f\n", sample->x); @@ -82,13 +80,8 @@ Let's take a look at the `receiving` function which comes with the sleep_for(1000); } ``` - - 5. After we stop receiving samples we would like to unsubscribe. - ```c - iox_sub_unsubscribe(subscriber); - ``` - 6. When using the C API we have to cleanup the subscriber after + 4. When using the C API we have to cleanup the subscriber after its usage. ```c iox_sub_deinit(subscriber); @@ -116,17 +109,15 @@ Let's take a look at the `sending` function which comes with the 2. We create a publisher with the service {"Radar", "FrontLeft", "Counter"} ```c - const uint64_t historyRequest = 10U; - const char* const nodeName = "iox-c-publisher-node"; + iox_pub_options_t options; + iox_pub_options_init(&options); + options.historyCapacity = 10U; + options.nodeName = "iox-c-publisher-node"; iox_pub_storage_t publisherStorage; - iox_pub_t publisher = iox_pub_init(&publisherStorage, "Radar", "FrontLeft", "Object", historyRequest, nodeName); - ``` - 3. We offer our service to the world. - ```c - iox_pub_offer(publisher); + iox_pub_t publisher = iox_pub_init(&publisherStorage, "Radar", "FrontLeft", "Object", &options); ``` - 4. Till an external signal sets `killswitch` to `true` we will send an + 3. Till an external signal sets `killswitch` to `true` we will send an incrementing number to all subscribers every send and print the value of this number to the console. ```c @@ -135,7 +126,7 @@ Let's take a look at the `sending` function which comes with the while (!killswitch) { void* chunk = NULL; - if (AllocationResult_SUCCESS == iox_pub_allocate_chunk(publisher, &chunk, sizeof(struct RadarObject))) + if (AllocationResult_SUCCESS == iox_pub_loan_chunk(publisher, &chunk, sizeof(struct RadarObject))) { struct RadarObject* sample = (struct RadarObject*)chunk; @@ -145,7 +136,7 @@ Let's take a look at the `sending` function which comes with the printf("Sent value: %.0f\n", ct); - iox_pub_send_chunk(publisher, chunk); + iox_pub_publish_chunk(publisher, chunk); ++ct; @@ -158,12 +149,7 @@ Let's take a look at the `sending` function which comes with the } ``` - 5. We stop offering our service. - ```c - iox_pub_stop_offer(publisher); - ``` - - 6. And we cleanup our publisher port. + 5. And we cleanup our publisher port. ```c iox_pub_destroy(publisher); ``` diff --git a/iceoryx_examples/icedelivery_in_c/ice_c_publisher.c b/iceoryx_examples/icedelivery_in_c/ice_c_publisher.c index c07cb85b97c..2751359ff9c 100644 --- a/iceoryx_examples/icedelivery_in_c/ice_c_publisher.c +++ b/iceoryx_examples/icedelivery_in_c/ice_c_publisher.c @@ -37,19 +37,19 @@ void sending() { iox_runtime_init("iox-c-publisher"); - const uint64_t historyCapacity = 10U; - const char* const nodeName = "iox-c-publisher-node"; + iox_pub_options_t options; + iox_pub_options_init(&options); + options.historyCapacity = 10U; + options.nodeName = "iox-c-publisher-node"; iox_pub_storage_t publisherStorage; - iox_pub_t publisher = iox_pub_init(&publisherStorage, "Radar", "FrontLeft", "Object", historyCapacity, nodeName); - - iox_pub_offer(publisher); + iox_pub_t publisher = iox_pub_init(&publisherStorage, "Radar", "FrontLeft", "Object", &options); double ct = 0.0; while (!killswitch) { void* chunk = NULL; - if (AllocationResult_SUCCESS == iox_pub_allocate_chunk(publisher, &chunk, sizeof(struct RadarObject))) + if (AllocationResult_SUCCESS == iox_pub_loan_chunk(publisher, &chunk, sizeof(struct RadarObject))) { struct RadarObject* sample = (struct RadarObject*)chunk; @@ -60,7 +60,7 @@ void sending() printf("Sent value: %.0f\n", ct); fflush(stdout); - iox_pub_send_chunk(publisher, chunk); + iox_pub_publish_chunk(publisher, chunk); ++ct; @@ -72,7 +72,6 @@ void sending() } } - iox_pub_stop_offer(publisher); iox_pub_deinit(publisher); } diff --git a/iceoryx_examples/icedelivery_in_c/ice_c_subscriber.c b/iceoryx_examples/icedelivery_in_c/ice_c_subscriber.c index 125da9b578c..da05d46a2a3 100644 --- a/iceoryx_examples/icedelivery_in_c/ice_c_subscriber.c +++ b/iceoryx_examples/icedelivery_in_c/ice_c_subscriber.c @@ -41,14 +41,15 @@ void receiving() // When starting the subscriber late it will miss the first samples which the // publisher has send. The history ensures that we at least get the last 10 // samples send by the publisher when we subscribe. - const uint64_t historyRequest = 10U; - const uint64_t queueCapacity = 5U; - const char* const nodeName = "iox-c-subscriber-node"; + iox_sub_options_t options; + iox_sub_options_init(&options); + options.historyRequest = 10U; + options.queueCapacity = 5U; + options.nodeName = "iox-c-subscriber-node"; iox_sub_storage_t subscriberStorage; iox_sub_t subscriber = - iox_sub_init(&subscriberStorage, "Radar", "FrontLeft", "Object", queueCapacity, historyRequest, nodeName); - iox_sub_subscribe(subscriber); + iox_sub_init(&subscriberStorage, "Radar", "FrontLeft", "Object", &options); while (!killswitch) { @@ -57,7 +58,7 @@ void receiving() const void* chunk = NULL; // we will receive here more then one sample since the publisher is sending a // new sample every 400ms and we check for new samples only every second - while (ChunkReceiveResult_SUCCESS == iox_sub_get_chunk(subscriber, &chunk)) + while (ChunkReceiveResult_SUCCESS == iox_sub_take_chunk(subscriber, &chunk)) { const struct RadarObject* sample = (const struct RadarObject*)(chunk); printf("Got value: %.0f\n", sample->x); @@ -74,7 +75,6 @@ void receiving() sleep_for(1000); } - iox_sub_unsubscribe(subscriber); iox_sub_deinit(subscriber); } diff --git a/iceoryx_examples/iceperf/iceoryx.cpp b/iceoryx_examples/iceperf/iceoryx.cpp index 64a534d8c51..fa58f46b537 100644 --- a/iceoryx_examples/iceperf/iceoryx.cpp +++ b/iceoryx_examples/iceperf/iceoryx.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. @@ -37,9 +38,6 @@ void Iceoryx::initFollower() noexcept void Iceoryx::init() noexcept { - m_publisher.offer(); - m_subscriber.subscribe(); - std::cout << "Waiting for: subscription" << std::flush; while (m_subscriber.getSubscriptionState() != iox::SubscribeState::SUBSCRIBED) { @@ -91,7 +89,7 @@ PerfTopic Iceoryx::receivePerfTopic() noexcept m_subscriber.take().and_then([&](const void* data) { receivedSample = *(static_cast(data)); hasReceivedSample = true; - m_subscriber.releaseChunk(data); + m_subscriber.release(data); }); } while (!hasReceivedSample); diff --git a/iceoryx_examples/iceperf/iceoryx_c.cpp b/iceoryx_examples/iceperf/iceoryx_c.cpp index 32d67f4d1b3..306363a1cbf 100644 --- a/iceoryx_examples/iceperf/iceoryx_c.cpp +++ b/iceoryx_examples/iceperf/iceoryx_c.cpp @@ -20,9 +20,19 @@ #include IceoryxC::IceoryxC(const iox::capro::IdString_t& publisherName, const iox::capro::IdString_t& subscriberName) noexcept - : m_publisher(iox_pub_init(&m_publisherStorage, "Comedians", publisherName.c_str(), "Duo", 0U, "Slapstick")) - , m_subscriber(iox_sub_init(&m_subscriberStorage, "Comedians", subscriberName.c_str(), "Duo", 10U, 0U, "Slapstick")) { + iox_pub_options_t publisherOptions; + iox_pub_options_init(&publisherOptions); + publisherOptions.historyCapacity = 0U; + publisherOptions.nodeName = "SlapStick"; + m_publisher = iox_pub_init(&m_publisherStorage, "Comedians", publisherName.c_str(), "Duo", &publisherOptions); + + iox_sub_options_t subscriberOptions; + iox_sub_options_init(&subscriberOptions); + subscriberOptions.queueCapacity = 10U; + subscriberOptions.historyRequest = 0U; + subscriberOptions.nodeName = "Slapstick"; + m_subscriber = iox_sub_init(&m_subscriberStorage, "Comedians", subscriberName.c_str(), "Duo", &subscriberOptions); } IceoryxC::~IceoryxC() @@ -78,13 +88,13 @@ void IceoryxC::shutdown() noexcept void IceoryxC::sendPerfTopic(uint32_t payloadSizeInBytes, bool runFlag) noexcept { void* chunk = nullptr; - if (iox_pub_allocate_chunk(m_publisher, &chunk, payloadSizeInBytes) == AllocationResult_SUCCESS) + if (iox_pub_loan_chunk(m_publisher, &chunk, payloadSizeInBytes) == AllocationResult_SUCCESS) { auto sendSample = static_cast(chunk); sendSample->payloadSize = payloadSizeInBytes; sendSample->run = runFlag; sendSample->subPackets = 1; - iox_pub_send_chunk(m_publisher, chunk); + iox_pub_publish_chunk(m_publisher, chunk); } } @@ -96,7 +106,7 @@ PerfTopic IceoryxC::receivePerfTopic() noexcept do { const void* sample = nullptr; - if (iox_sub_get_chunk(m_subscriber, &sample) == ChunkReceiveResult_SUCCESS) + if (iox_sub_take_chunk(m_subscriber, &sample) == ChunkReceiveResult_SUCCESS) { receivedSample = *(static_cast(sample)); hasReceivedSample = true; diff --git a/iceoryx_examples/singleprocess/README.md b/iceoryx_examples/singleprocess/README.md index e994d1b7621..c0eea7b4f3f 100644 --- a/iceoryx_examples/singleprocess/README.md +++ b/iceoryx_examples/singleprocess/README.md @@ -104,13 +104,11 @@ you here with a short overview. #### Publisher We create a typed publisher with the following service description -(Service = `Single`, Instance = `Process`, Event = `Demo`) and offer our service -to the world. +(Service = `Single`, Instance = `Process`, Event = `Demo`) ```cpp iox::popo::PublisherOptions publisherOptions; publisherOptions.historyCapacity = 10U; iox::popo::Publisher publisher({"Single", "Process", "Demo"}, publisherOptions); -publisher.offer(); ``` After that we are sending numbers in ascending order with an 100ms interval in a `while` loop till the variable `keepRunning` is false. @@ -131,14 +129,12 @@ while (keepRunning.load()) #### Subscriber Like with the publisher we are creating a corresponding subscriber port with the -same service description and subscribe to our service. +same service description. ```cpp iox::popo::SubscriberOptions options; options.queueCapacity = 10U; options.historyRequest = 5U; iox::popo::Subscriber subscriber({"Single", "Process", "Demo"}, options); - - subscriber.subscribe(); ``` Now we can receive the data in a while loop till `keepRunning` is false. But we only try to acquire data if our `SubscribeState` is `SUBSCRIBED`. diff --git a/iceoryx_examples/singleprocess/single_process.cpp b/iceoryx_examples/singleprocess/single_process.cpp index 24272d05813..c0bb8a35823 100644 --- a/iceoryx_examples/singleprocess/single_process.cpp +++ b/iceoryx_examples/singleprocess/single_process.cpp @@ -58,7 +58,6 @@ void publisher() iox::popo::PublisherOptions publisherOptions; publisherOptions.historyCapacity = 10U; iox::popo::Publisher publisher({"Single", "Process", "Demo"}, publisherOptions); - publisher.offer(); uint64_t counter{0}; std::string greenRightArrow("\033[32m->\033[m "); @@ -81,8 +80,6 @@ void subscriber() options.historyRequest = 5U; iox::popo::Subscriber subscriber({"Single", "Process", "Demo"}, options); - subscriber.subscribe(); - std::string orangeLeftArrow("\033[33m<-\033[m "); while (keepRunning.load()) { diff --git a/iceoryx_examples/waitset/README.md b/iceoryx_examples/waitset/README.md index 541281353af..6c18525933a 100644 --- a/iceoryx_examples/waitset/README.md +++ b/iceoryx_examples/waitset/README.md @@ -145,7 +145,7 @@ iox::popo::WaitSet waitset; waitset.attachEvent(shutdownTrigger); ``` -After that we create a vector to hold our subscribers, we create, subscribe and then +After that we create a vector to hold our subscribers, we create and then attach them to a _WaitSet_ with the `HAS_DATA` event and the `subscriberCallback`. Everytime one of the subscribers is receiving a new sample it will trigger the _WaitSet_. @@ -156,7 +156,6 @@ for (auto i = 0; i < NUMBER_OF_SUBSCRIBERS; ++i) subscriberVector.emplace_back(iox::capro::ServiceDescription{"Radar", "FrontLeft", "Counter"}); auto& subscriber = subscriberVector.back(); - subscriber.subscribe(); waitset.attachEvent(subscriber, iox::popo::SubscriberEvent::HAS_DATA, &subscriberCallback); } ``` @@ -202,15 +201,13 @@ iox::popo::WaitSet waitset; waitset.attachEvent(shutdownTrigger); ``` -Now we create a vector of 4 subscribers and subscribe them to our topic. +Now we create a vector of 4 subscribers. ```cpp iox::cxx::vector subscriberVector; for (auto i = 0; i < NUMBER_OF_SUBSCRIBERS; ++i) { subscriberVector.emplace_back(iox::capro::ServiceDescription{"Radar", "FrontLeft", "Counter"}); auto& subscriber = subscriberVector.back(); - - subscriber.subscribe(); } ``` @@ -282,15 +279,12 @@ iox::popo::WaitSet waitset<>; waitset.attachEvent(shutdownTrigger); ``` -Additionally, we create two subscribers, subscribe them to our topic and attach +Additionally, we create two subscribers and attach them to the waitset to let them inform us whenever they receive a new sample. ```cpp iox::popo::Subscriber subscriber1({"Radar", "FrontLeft", "Counter"}); iox::popo::Subscriber subscriber2({"Radar", "FrontLeft", "Counter"}); -subscriber1.subscribe(); -subscriber2.subscribe(); - waitset.attachEvent(subscriber1, iox::popo::SubscriberEvent::HAS_DATA); waitset.attachEvent(subscriber2, iox::popo::SubscriberEvent::HAS_DATA); ``` diff --git a/iceoryx_examples/waitset/ice_waitset_gateway.cpp b/iceoryx_examples/waitset/ice_waitset_gateway.cpp index d7c0e1483a6..768e05dd072 100644 --- a/iceoryx_examples/waitset/ice_waitset_gateway.cpp +++ b/iceoryx_examples/waitset/ice_waitset_gateway.cpp @@ -66,7 +66,6 @@ int main() subscriberVector.emplace_back(iox::capro::ServiceDescription{"Radar", "FrontLeft", "Counter"}); auto& subscriber = subscriberVector.back(); - subscriber.subscribe(); waitset.attachEvent(subscriber, iox::popo::SubscriberEvent::HAS_DATA, 0, &subscriberCallback); } diff --git a/iceoryx_examples/waitset/ice_waitset_grouping.cpp b/iceoryx_examples/waitset/ice_waitset_grouping.cpp index 2f30710ec76..5f4c3e567a5 100644 --- a/iceoryx_examples/waitset/ice_waitset_grouping.cpp +++ b/iceoryx_examples/waitset/ice_waitset_grouping.cpp @@ -52,8 +52,6 @@ int main() { subscriberVector.emplace_back(iox::capro::ServiceDescription{"Radar", "FrontLeft", "Counter"}); auto& subscriber = subscriberVector.back(); - - subscriber.subscribe(); } constexpr uint64_t FIRST_GROUP_ID = 123U; @@ -90,7 +88,7 @@ int main() subscriber->take().and_then([&](auto& payload) { const CounterTopic* data = static_cast(payload); std::cout << "received: " << std::dec << data->counter << std::endl; - subscriber->releaseChunk(payload); + subscriber->release(payload); }); } // dismiss the received data for the second group diff --git a/iceoryx_examples/waitset/ice_waitset_individual.cpp b/iceoryx_examples/waitset/ice_waitset_individual.cpp index a6eea07c89c..c5c609e073f 100644 --- a/iceoryx_examples/waitset/ice_waitset_individual.cpp +++ b/iceoryx_examples/waitset/ice_waitset_individual.cpp @@ -48,9 +48,6 @@ int main() iox::popo::Subscriber subscriber1({"Radar", "FrontLeft", "Counter"}); iox::popo::Subscriber subscriber2({"Radar", "FrontLeft", "Counter"}); - subscriber1.subscribe(); - subscriber2.subscribe(); - waitset.attachEvent(subscriber1, iox::popo::SubscriberEvent::HAS_DATA); waitset.attachEvent(subscriber2, iox::popo::SubscriberEvent::HAS_DATA); diff --git a/iceoryx_examples/waitset/ice_waitset_publisher.cpp b/iceoryx_examples/waitset/ice_waitset_publisher.cpp index 9ec1e8f4659..41ff2417f4a 100644 --- a/iceoryx_examples/waitset/ice_waitset_publisher.cpp +++ b/iceoryx_examples/waitset/ice_waitset_publisher.cpp @@ -33,7 +33,6 @@ void sending() { iox::runtime::PoshRuntime::initRuntime("iox-ex-publisher-waitset"); iox::popo::Publisher myPublisher({"Radar", "FrontLeft", "Counter"}); - myPublisher.offer(); for (uint32_t counter = 0U; !killswitch; ++counter) { diff --git a/iceoryx_examples/waitset_in_c/CMakeLists.txt b/iceoryx_examples/waitset_in_c/CMakeLists.txt index 5f2ef38ecea..fd95d182c7d 100644 --- a/iceoryx_examples/waitset_in_c/CMakeLists.txt +++ b/iceoryx_examples/waitset_in_c/CMakeLists.txt @@ -31,35 +31,30 @@ 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_posh::iceoryx_posh iceoryx_binding_c::iceoryx_binding_c ) target_compile_options(iox-ex-c-waitset-publisher PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) add_executable(iox-ex-c-waitset-gateway ./ice_c_waitset_gateway.c) target_link_libraries(iox-ex-c-waitset-gateway - iceoryx_posh::iceoryx_posh iceoryx_binding_c::iceoryx_binding_c ) target_compile_options(iox-ex-c-waitset-gateway PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) add_executable(iox-ex-c-waitset-grouping ./ice_c_waitset_grouping.c) target_link_libraries(iox-ex-c-waitset-grouping - iceoryx_posh::iceoryx_posh iceoryx_binding_c::iceoryx_binding_c ) target_compile_options(iox-ex-c-waitset-grouping PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) add_executable(iox-ex-c-waitset-individual ./ice_c_waitset_individual.c) target_link_libraries(iox-ex-c-waitset-individual - iceoryx_posh::iceoryx_posh iceoryx_binding_c::iceoryx_binding_c ) target_compile_options(iox-ex-c-waitset-individual PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) add_executable(iox-ex-c-waitset-sync ./ice_c_waitset_sync.c) target_link_libraries(iox-ex-c-waitset-sync - iceoryx_posh::iceoryx_posh iceoryx_binding_c::iceoryx_binding_c ) target_compile_options(iox-ex-c-waitset-sync PRIVATE ${ICEORYX_WARNINGS} ${ICEORYX_SANITIZER_FLAGS}) diff --git a/iceoryx_examples/waitset_in_c/README.md b/iceoryx_examples/waitset_in_c/README.md index 02185f79585..2bb9378c89b 100644 --- a/iceoryx_examples/waitset_in_c/README.md +++ b/iceoryx_examples/waitset_in_c/README.md @@ -30,7 +30,7 @@ prints out the subscriber pointer and the content of the received sample. void subscriberCallback(iox_sub_t const subscriber) { const void* chunk; - if (iox_sub_get_chunk(subscriber, &chunk)) + if (iox_sub_take_chunk(subscriber, &chunk)) { printf("subscriber: %p received %u\n", subscriber, ((struct CounterTopic*)chunk)->counter); @@ -58,14 +58,15 @@ the `subscriberCallback` and an event id `1U`. ```c iox_sub_storage_t subscriberStorage[NUMBER_OF_SUBSCRIBERS]; -const uint64_t historyRequest = 1U; -const uint64_t queueCapacity = 256U; -const char* const nodeName = "iox-c-ex-waitSet-gateway-node"; +iox_sub_options_t options; +iox_sub_options_init(&options); +options.historyRequest = 1U; +options.queueCapacity = 256U; +options.nodeName = "iox-c-ex-waitSet-gateway-node"; for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) { - iox_sub_t subscriber = iox_sub_init(&(subscriberStorage[i]), "Radar", "FrontLeft", "Counter", queueCapacity, historyRequest, nodeName); + iox_sub_t subscriber = iox_sub_init(&(subscriberStorage[i]), "Radar", "FrontLeft", "Counter", &options); - iox_sub_subscribe(subscriber); iox_ws_attach_subscriber_event(waitSet, subscriber, SubscriberEvent_HAS_DATA, 1U, subscriberCallback); } ``` @@ -111,7 +112,6 @@ Before we can close the program we cleanup all resources. ```c for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) { - iox_sub_unsubscribe((iox_sub_t) & (subscriberStorage[i])); iox_sub_deinit((iox_sub_t) & (subscriberStorage[i])); } @@ -141,14 +141,15 @@ After that we can create a list of subscribers and subscribe them to our topic. iox_sub_storage_t subscriberStorage[NUMBER_OF_SUBSCRIBERS]; iox_sub_t subscriber[NUMBER_OF_SUBSCRIBERS]; -const uint64_t historyRequest = 1U; -const uint64_t queueCapacity = 256U; -const char* const nodeName = "iox-c-ex-waitset-grouping-node"; +iox_sub_options_t options; +iox_sub_options_init(&options); +options.historyRequest = 1U; +options.queueCapacity = 256U; +options.nodeName = "iox-c-ex-waitset-grouping-node"; for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) { - subscriber[i] = iox_sub_init(&(subscriberStorage[i]), "Radar", "FrontLeft", "Counter", queueCapacity, historyRequest, nodeName); + subscriber[i] = iox_sub_init(&(subscriberStorage[i]), "Radar", "FrontLeft", "Counter", &options); - iox_sub_subscribe(subscriber[i]); } ``` @@ -203,7 +204,7 @@ for (uint64_t i = 0U; i < numberOfEvents; ++i) { iox_sub_t subscriber = iox_event_info_get_subscriber_origin(event); const void* chunk; - if (iox_sub_get_chunk(subscriber, &chunk)) + if (iox_sub_take_chunk(subscriber, &chunk)) { printf("received: %u\n", ((struct CounterTopic*)chunk)->counter); @@ -223,7 +224,6 @@ The last thing we have to do is to cleanup all the acquired resources. ```c for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) { - iox_sub_unsubscribe((iox_sub_t) & (subscriberStorage[i])); iox_sub_deinit((iox_sub_t) & (subscriberStorage[i])); } @@ -252,17 +252,15 @@ iox_ws_attach_user_trigger_event(waitSet, shutdownTrigger, 0U, NULL); Now we create two subscriber, subscribe them to our topic and attach them to the waitset without a callback and with the same trigger id. ```c -const uint64_t historyRequest = 1U; -const uint64_t queueCapacity = 256U; - -const char* const nodeName1 = "iox-c-ex-waitset-individual-node1"; -const char* const nodeName2 = "iox-c-ex-waitset-individual-node2"; - -subscriber[0] = iox_sub_init(&(subscriberStorage[0]), "Radar", "FrontLeft", "Counter", queueCapacity, historyRequest, nodeName1); -subscriber[1] = iox_sub_init(&(subscriberStorage[1]), "Radar", "FrontLeft", "Counter", queueCapacity, historyRequest, nodeName2); - -iox_sub_subscribe(subscriber[0]); -iox_sub_subscribe(subscriber[1]); +iox_sub_options_t options; +iox_sub_options_init(&options); +options.historyRequest = 1U; +options.queueCapacity = 256U; +options.nodeName = "iox-c-ex-waitset-individual-node1"; + +subscriber[0] = iox_sub_init(&(subscriberStorage[0]), "Radar", "FrontLeft", "Counter", &options); +options.nodeName = "iox-c-ex-waitset-individual-node2"; +subscriber[1] = iox_sub_init(&(subscriberStorage[1]), "Radar", "FrontLeft", "Counter", &options); iox_ws_attach_subscriber_event(waitSet, subscriber[0U], SubscriberEvent_HAS_DATA, 0U, NULL); iox_ws_attach_subscriber_event(waitSet, subscriber[1U], SubscriberEvent_HAS_DATA, 0U, NULL); @@ -295,7 +293,7 @@ originated from the second subscriber we discard the data. else if (iox_event_info_does_originate_from_subscriber(event, subscriber[0])) { const void* chunk; - if (iox_sub_get_chunk(subscriber[0], &chunk)) + if (iox_sub_take_chunk(subscriber[0], &chunk)) { printf("subscriber 1 received: %u\n", ((struct CounterTopic*)chunk)->counter); @@ -314,7 +312,6 @@ We conclude the example as always, be cleaning up the resources. ```c for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) { - iox_sub_unsubscribe((iox_sub_t) & (subscriberStorage[i])); iox_sub_deinit((iox_sub_t) & (subscriberStorage[i])); } 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 e577cdc9825..354c59978ec 100644 --- a/iceoryx_examples/waitset_in_c/ice_c_waitset_gateway.c +++ b/iceoryx_examples/waitset_in_c/ice_c_waitset_gateway.c @@ -48,7 +48,7 @@ static void sigHandler(int signalValue) void subscriberCallback(iox_sub_t const subscriber) { const void* chunk; - if (iox_sub_get_chunk(subscriber, &chunk)) + if (iox_sub_take_chunk(subscriber, &chunk)) { printf("subscriber: %p received %u\n", (void*)subscriber, ((struct CounterTopic*)chunk)->counter); fflush(stdout); @@ -76,15 +76,17 @@ int main() iox_sub_storage_t subscriberStorage[NUMBER_OF_SUBSCRIBERS]; // create subscriber and subscribe them to our service - const uint64_t historyRequest = 1U; - const uint64_t queueCapacity = 256U; - const char* const nodeName = "iox-c-ex-waitSet-gateway-node"; + + iox_sub_options_t options; + iox_sub_options_init(&options); + options.historyRequest = 1U; + options.queueCapacity = 256U; + options.nodeName = "iox-c-ex-waitSet-gateway-node"; for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) { iox_sub_t subscriber = iox_sub_init( - &(subscriberStorage[i]), "Radar", "FrontLeft", "Counter", queueCapacity, historyRequest, nodeName); + &(subscriberStorage[i]), "Radar", "FrontLeft", "Counter", &options); - iox_sub_subscribe(subscriber); iox_ws_attach_subscriber_event(waitSet, subscriber, SubscriberEvent_HAS_DATA, 1U, subscriberCallback); } @@ -121,7 +123,6 @@ int main() // cleanup all resources for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) { - iox_sub_unsubscribe((iox_sub_t) & (subscriberStorage[i])); iox_sub_deinit((iox_sub_t) & (subscriberStorage[i])); } 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 f1e98863228..b3836c43c8a 100644 --- a/iceoryx_examples/waitset_in_c/ice_c_waitset_grouping.c +++ b/iceoryx_examples/waitset_in_c/ice_c_waitset_grouping.c @@ -61,15 +61,15 @@ int main() iox_sub_t subscriber[NUMBER_OF_SUBSCRIBERS]; // create subscriber and subscribe them to our service - const uint64_t historyRequest = 1U; - const uint64_t queueCapacity = 256U; - const char* const nodeName = "iox-c-ex-waitset-grouping-node"; + iox_sub_options_t options; + iox_sub_options_init(&options); + options.historyRequest = 1U; + options.queueCapacity = 256U; + options.nodeName = "iox-c-ex-waitset-grouping-node"; for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) { subscriber[i] = iox_sub_init( - &(subscriberStorage[i]), "Radar", "FrontLeft", "Counter", queueCapacity, historyRequest, nodeName); - - iox_sub_subscribe(subscriber[i]); + &(subscriberStorage[i]), "Radar", "FrontLeft", "Counter", &options); } const uint64_t FIRST_GROUP_ID = 123U; @@ -114,7 +114,7 @@ int main() { iox_sub_t subscriber = iox_event_info_get_subscriber_origin(event); const void* chunk; - if (iox_sub_get_chunk(subscriber, &chunk)) + if (iox_sub_take_chunk(subscriber, &chunk)) { printf("received: %u\n", ((struct CounterTopic*)chunk)->counter); fflush(stdout); @@ -138,7 +138,6 @@ int main() // cleanup all resources for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) { - iox_sub_unsubscribe((iox_sub_t) & (subscriberStorage[i])); iox_sub_deinit((iox_sub_t) & (subscriberStorage[i])); } 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 049332f594a..87255820ab8 100644 --- a/iceoryx_examples/waitset_in_c/ice_c_waitset_individual.c +++ b/iceoryx_examples/waitset_in_c/ice_c_waitset_individual.c @@ -61,19 +61,18 @@ int main() iox_sub_t subscriber[NUMBER_OF_SUBSCRIBERS]; // create two subscribers, subscribe to the service and attach them to the waitset - const uint64_t historyRequest = 1U; - const uint64_t queueCapacity = 256U; - - const char* const nodeName1 = "iox-c-ex-waitset-individual-node1"; - const char* const nodeName2 = "iox-c-ex-waitset-individual-node2"; + iox_sub_options_t options; + iox_sub_options_init(&options); + options.historyRequest = 1U; + options.queueCapacity = 256U; + options.nodeName = "iox-c-ex-waitset-individual-node1"; subscriber[0] = iox_sub_init( - &(subscriberStorage[0]), "Radar", "FrontLeft", "Counter", queueCapacity, historyRequest, nodeName1); - subscriber[1] = iox_sub_init( - &(subscriberStorage[1]), "Radar", "FrontLeft", "Counter", queueCapacity, historyRequest, nodeName2); + &(subscriberStorage[0]), "Radar", "FrontLeft", "Counter", &options); - iox_sub_subscribe(subscriber[0]); - iox_sub_subscribe(subscriber[1]); + options.nodeName = "iox-c-ex-waitset-individual-node2"; + subscriber[1] = iox_sub_init( + &(subscriberStorage[1]), "Radar", "FrontLeft", "Counter", &options); iox_ws_attach_subscriber_event(waitSet, subscriber[0U], SubscriberEvent_HAS_DATA, 0U, NULL); iox_ws_attach_subscriber_event(waitSet, subscriber[1U], SubscriberEvent_HAS_DATA, 0U, NULL); @@ -104,7 +103,7 @@ int main() else if (iox_event_info_does_originate_from_subscriber(event, subscriber[0U])) { const void* chunk; - if (iox_sub_get_chunk(subscriber[0U], &chunk)) + if (iox_sub_take_chunk(subscriber[0U], &chunk)) { printf("subscriber 1 received: %u\n", ((struct CounterTopic*)chunk)->counter); fflush(stdout); @@ -128,7 +127,6 @@ int main() // cleanup all resources for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) { - iox_sub_unsubscribe((iox_sub_t) & (subscriberStorage[i])); iox_sub_deinit((iox_sub_t) & (subscriberStorage[i])); } 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 5e5c05b1c9a..047828bec27 100644 --- a/iceoryx_examples/waitset_in_c/ice_c_waitset_publisher.c +++ b/iceoryx_examples/waitset_in_c/ice_c_waitset_publisher.c @@ -37,17 +37,17 @@ void sending() { iox_runtime_init("iox-c-ex-waitset-publisher"); - const uint64_t historyCapacity = 0U; - const char* const nodeName = "iox-c-ex-waitset-publisher-node"; + iox_pub_options_t options; + iox_pub_options_init(&options); + options.historyCapacity = 0U; + options.nodeName = "iox-c-ex-waitset-publisher-node"; iox_pub_storage_t publisherStorage; - iox_pub_t publisher = iox_pub_init(&publisherStorage, "Radar", "FrontLeft", "Counter", historyCapacity, nodeName); - - iox_pub_offer(publisher); + iox_pub_t publisher = iox_pub_init(&publisherStorage, "Radar", "FrontLeft", "Counter", &options); for (uint32_t counter = 0U; !killswitch; ++counter) { void* chunk = NULL; - if (AllocationResult_SUCCESS == iox_pub_allocate_chunk(publisher, &chunk, sizeof(struct CounterTopic))) + if (AllocationResult_SUCCESS == iox_pub_loan_chunk(publisher, &chunk, sizeof(struct CounterTopic))) { struct CounterTopic* sample = (struct CounterTopic*)chunk; sample->counter = counter; @@ -55,7 +55,7 @@ void sending() printf("Sending: %u\n", counter); fflush(stdout); - iox_pub_send_chunk(publisher, chunk); + iox_pub_publish_chunk(publisher, chunk); sleep_for(1000); } @@ -65,7 +65,6 @@ void sending() } } - iox_pub_stop_offer(publisher); iox_pub_deinit(publisher); } diff --git a/iceoryx_meta/CMakeLists.txt b/iceoryx_meta/CMakeLists.txt index cfa90d82552..4ba927468fe 100644 --- a/iceoryx_meta/CMakeLists.txt +++ b/iceoryx_meta/CMakeLists.txt @@ -1,4 +1,5 @@ -# Copyright (c) 2019, 2020 by Robert Bosch GmbH, Apex.AI Inc. All rights reserved. +# Copyright (c) 2019 - 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. @@ -64,6 +65,7 @@ if(EXAMPLES) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../iceoryx_examples/iceperf ${CMAKE_BINARY_DIR}/iceoryx_examples/iceperf) endif() add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../iceoryx_examples/waitset ${CMAKE_BINARY_DIR}/iceoryx_examples/waitset) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../iceoryx_examples/callbacks ${CMAKE_BINARY_DIR}/iceoryx_examples/callbacks) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../iceoryx_examples/singleprocess ${CMAKE_BINARY_DIR}/iceoryx_examples/singleprocess) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../iceoryx_examples/ice_multi_publisher ${CMAKE_BINARY_DIR}/iceoryx_examples/ice_multi_publisher) endif() diff --git a/iceoryx_posh/CMakeLists.txt b/iceoryx_posh/CMakeLists.txt index 92b0bf7c05b..a57c4210464 100644 --- a/iceoryx_posh/CMakeLists.txt +++ b/iceoryx_posh/CMakeLists.txt @@ -102,12 +102,16 @@ add_library(iceoryx_posh 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/user_trigger.cpp + source/popo/active_call_set.cpp + source/popo/event_info.cpp source/popo/trigger.cpp source/popo/trigger_handle.cpp - source/popo/event_info.cpp + source/popo/user_trigger.cpp source/version/version_info.cpp source/runtime/ipc_interface_base.cpp source/runtime/ipc_interface_user.cpp diff --git a/iceoryx_posh/include/iceoryx_posh/iceoryx_posh_types.hpp b/iceoryx_posh/include/iceoryx_posh/iceoryx_posh_types.hpp index 443523c3682..800a1a9acbb 100644 --- a/iceoryx_posh/include/iceoryx_posh/iceoryx_posh_types.hpp +++ b/iceoryx_posh/include/iceoryx_posh/iceoryx_posh_types.hpp @@ -1,4 +1,5 @@ -// Copyright (c) 2019 by Robert Bosch GmbH, Apex.AI Inc. All rights reserved. +// 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. @@ -101,6 +102,9 @@ constexpr uint32_t MAX_REQUEST_QUEUE_CAPACITY = 1024; // Waitset constexpr uint32_t MAX_NUMBER_OF_CONDITION_VARIABLES = 1024U; constexpr uint32_t MAX_NUMBER_OF_EVENTS_PER_WAITSET = 128U; +// ActiveCallSet +constexpr uint8_t MAX_NUMBER_OF_EVENT_VARIABLES = 128U; +constexpr uint8_t MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET = 128U; //--------- Communication Resources End--------------------- constexpr uint32_t MAX_APPLICATION_CAPRO_FIFO_SIZE = 128U; diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/active_call_set.inl b/iceoryx_posh/include/iceoryx_posh/internal/popo/active_call_set.inl new file mode 100644 index 00000000000..bd1ed3f81a4 --- /dev/null +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/active_call_set.inl @@ -0,0 +1,85 @@ +// 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_ACTIVE_CALL_SET_INL +#define IOX_POSH_POPO_ACTIVE_CALL_SET_INL +namespace iox +{ +namespace popo +{ +namespace internal +{ +template +inline void translateAndCallTypelessCallback(void* const origin, void (*underlyingCallback)(void* const)) +{ + reinterpret_cast(underlyingCallback)(static_cast(origin)); +} +} // namespace internal + +template +inline cxx::expected ActiveCallSet::attachEvent(T& eventOrigin, + CallbackRef_t eventCallback) noexcept +{ + return addEvent(&eventOrigin, + static_cast(NoEnumUsed::PLACEHOLDER), + typeid(NoEnumUsed).hash_code(), + reinterpret_cast>(eventCallback), + internal::translateAndCallTypelessCallback, + EventAttorney::getInvalidateTriggerMethod(eventOrigin)) + .and_then([&](auto& eventId) { + EventAttorney::enableEvent( + eventOrigin, TriggerHandle(*m_eventVariable, {*this, &ActiveCallSet::removeTrigger}, eventId)); + }); +} + +template +inline cxx::expected +ActiveCallSet::attachEvent(T& eventOrigin, const EventType eventType, CallbackRef_t eventCallback) noexcept +{ + return addEvent(&eventOrigin, + static_cast(eventType), + typeid(EventType).hash_code(), + reinterpret_cast>(eventCallback), + internal::translateAndCallTypelessCallback, + EventAttorney::getInvalidateTriggerMethod(eventOrigin)) + .and_then([&](auto& eventId) { + EventAttorney::enableEvent(eventOrigin, + TriggerHandle(*m_eventVariable, {*this, &ActiveCallSet::removeTrigger}, eventId), + eventType); + }); +} + +template +inline void ActiveCallSet::detachEvent(T& eventOrigin, const EventType eventType) noexcept +{ + EventAttorney::disableEvent(eventOrigin, eventType); +} + +template +inline void ActiveCallSet::detachEvent(T& eventOrigin) noexcept +{ + EventAttorney::disableEvent(eventOrigin); +} + +inline constexpr uint64_t ActiveCallSet::capacity() noexcept +{ + return MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET; +} + + +} // namespace popo +} // namespace iox +#endif 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 08ece9f35cd..b5ca890f781 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/base_subscriber.inl +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/base_subscriber.inl @@ -88,22 +88,7 @@ inline bool BaseSubscriber::hasMissedData() noexcept template inline cxx::expected BaseSubscriber::takeChunk() noexcept { - auto result = m_port.tryGetChunk(); - if (result.has_error()) - { - return cxx::error(result.get_error()); - } - else - { - auto maybeHeader = result.value(); - if (maybeHeader.has_value()) - { - return cxx::success(maybeHeader.value()); - } - } - ///@todo: optimization - we could move this to a tryGetChunk but then we should remove expected> there in - /// the call chain - return cxx::error(ChunkReceiveResult::NO_CHUNK_AVAILABLE); + return m_port.tryGetChunk(); } template @@ -128,7 +113,15 @@ inline void BaseSubscriber::enableEvent(iox::popo::TriggerHandle&& trigg { m_trigger = std::move(triggerHandle); - m_port.setConditionVariable(m_trigger.getConditionVariableData()); + if (m_trigger.doesContainEventVariable()) + { + m_port.setEventVariable(*reinterpret_cast(m_trigger.getConditionVariableData()), + m_trigger.getUniqueId()); + } + else + { + m_port.setConditionVariable(m_trigger.getConditionVariableData()); + } } 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 dec6499559b..9dd23d7b764 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 @@ -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. @@ -42,6 +43,7 @@ struct ChunkQueueData : public LockingPolicy std::atomic_bool m_queueHasOverflown{false}; relative_ptr m_conditionVariableDataPtr{nullptr}; + cxx::optional m_eventVariableIndex; }; } // 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 57f210420bf..024a39329b7 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 @@ -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. @@ -19,6 +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_utils/cxx/helplets.hpp" #include "iceoryx_utils/cxx/optional.hpp" @@ -83,6 +85,10 @@ class ChunkQueuePopper /// @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; + /// @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 613d62453e3..cc654234a93 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 @@ -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. @@ -126,11 +127,20 @@ inline void ChunkQueuePopper::setConditionVariable( 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); +} + template inline void ChunkQueuePopper::unsetConditionVariable() noexcept { typename MemberType_t::LockGuard_t lock(*getMembers()); getMembers()->m_conditionVariableDataPtr = nullptr; + getMembers()->m_eventVariableIndex.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 967797be369..7540d122a4b 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 @@ -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. @@ -19,6 +20,8 @@ #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_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 9b912ccd15b..080cdb47000 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 @@ -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. @@ -16,8 +17,6 @@ #ifndef IOX_POSH_POPO_BUILDING_BLOCKS_CHUNK_QUEUE_PUSHER_INL #define IOX_POSH_POPO_BUILDING_BLOCKS_CHUNK_QUEUE_PUSHER_INL -#include "iceoryx_posh/internal/log/posh_logging.hpp" -#include "iceoryx_posh/internal/popo/building_blocks/condition_variable_signaler.hpp" namespace iox { @@ -69,8 +68,17 @@ inline void ChunkQueuePusher::push(mepoo::SharedChunk chunk) typename MemberType_t::LockGuard_t lock(*getMembers()); if (getMembers()->m_conditionVariableDataPtr) { - ConditionVariableSignaler condVarSignaler(getMembers()->m_conditionVariableDataPtr.get()); - condVarSignaler.notifyOne(); + if (getMembers()->m_eventVariableIndex) + { + EventNotifier(*reinterpret_cast(getMembers()->m_conditionVariableDataPtr.get()), + *getMembers()->m_eventVariableIndex) + .notify(); + } + else + { + ConditionVariableSignaler condVarSignaler(getMembers()->m_conditionVariableDataPtr.get()); + condVarSignaler.notifyOne(); + } } } } diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_receiver.hpp b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_receiver.hpp index d2ea98df812..d27b839aaf7 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_receiver.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_receiver.hpp @@ -59,9 +59,9 @@ class ChunkReceiver : public ChunkQueuePopper, ChunkReceiveResult> tryGet() noexcept; + /// @return New chunk header, ChunkReceiveResult on error + /// or if there are no new chunks in the underlying queue + cxx::expected tryGet() noexcept; /// @brief Release a chunk that was obtained with get /// @param[in] chunkHeader, pointer to the ChunkHeader to release diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_receiver.inl b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_receiver.inl index 0db74d629df..4227f16195a 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_receiver.inl +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/chunk_receiver.inl @@ -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. @@ -45,7 +46,7 @@ ChunkReceiver::getMembers() noexcept } template -inline cxx::expected, ChunkReceiveResult> +inline cxx::expected ChunkReceiver::tryGet() noexcept { auto popRet = this->tryPop(); @@ -57,7 +58,7 @@ ChunkReceiver::tryGet() noexcept // if the application holds too many chunks, don't provide more if (getMembers()->m_chunksInUse.insert(sharedChunk)) { - return cxx::success>( + return cxx::success( const_cast(sharedChunk.getChunkHeader())); } else @@ -67,11 +68,7 @@ ChunkReceiver::tryGet() noexcept return cxx::error(ChunkReceiveResult::TOO_MANY_CHUNKS_HELD_IN_PARALLEL); } } - else - { - // no new chunk - return cxx::success>(cxx::nullopt_t()); - } + return cxx::error(ChunkReceiveResult::NO_CHUNK_AVAILABLE); } template 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 79ec2e0c953..6481f25a690 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 @@ -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. @@ -28,6 +29,7 @@ namespace popo { struct ConditionVariableData { + ConditionVariableData() noexcept = default; ConditionVariableData(const ProcessName_t& process) noexcept; ConditionVariableData(const ConditionVariableData& rhs) = delete; ConditionVariableData(ConditionVariableData&& rhs) = delete; 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 new file mode 100644 index 00000000000..80752c8216c --- /dev/null +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/event_listener.hpp @@ -0,0 +1,67 @@ +// 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_ACTIVE_CALL_SET>; + + /// @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 new file mode 100644 index 00000000000..ea23ad1cb85 --- /dev/null +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/event_notifier.hpp @@ -0,0 +1,50 @@ +// 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_ACTIVE_CALL_SET) + 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 new file mode 100644 index 00000000000..14fac6a1f2f --- /dev/null +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/event_variable_data.hpp @@ -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 + + +#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_ACTIVE_CALL_SET]; +}; +} // namespace popo +} // namespace iox + +#endif + diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/ports/subscriber_port_user.hpp b/iceoryx_posh/include/iceoryx_posh/internal/popo/ports/subscriber_port_user.hpp index 4bb5abfefe4..b2fa70e190f 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/ports/subscriber_port_user.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/ports/subscriber_port_user.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. @@ -63,9 +64,9 @@ class SubscriberPortUser : public BasePort /// @brief Tries to get the next chunk from the queue. If there is a new one, the ChunkHeader of the oldest chunk in /// the queue is returned (FiFo queue) - /// @return optional that has a new chunk header or no value if there are no new chunks in the underlying queue, - /// ChunkReceiveResult on error - cxx::expected, ChunkReceiveResult> tryGetChunk() noexcept; + /// @return New chunk header, ChunkReceiveResult on error + /// or if there are no new chunks in the underlying queue + cxx::expected tryGetChunk() noexcept; /// @brief Release a chunk that was obtained with tryGetChunk /// @param[in] chunkHeader, pointer to the ChunkHeader to release @@ -85,6 +86,10 @@ class SubscriberPortUser : public BasePort /// @brief attach a condition variable (via its pointer) to subscriber void setConditionVariable(ConditionVariableData* conditionVariableDataPtr) noexcept; + /// @todo iox-#350 remove once ConditionVariable and EventVariable are combined + // do we need an unsetEventVariable method? + void setEventVariable(EventVariableData& eventVariableData, const uint64_t eventId) noexcept; + /// @brief detach a condition variable from subscriber void unsetConditionVariable() noexcept; diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/untyped_publisher.inl b/iceoryx_posh/include/iceoryx_posh/internal/popo/untyped_publisher.inl index 011385c05bc..e0393450428 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/untyped_publisher.inl +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/untyped_publisher.inl @@ -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. @@ -60,6 +61,13 @@ cxx::optional UntypedPublisherImpl::loanPreviousChunk() return cxx::nullopt; } +template +inline void UntypedPublisherImpl::release(const void* chunk) noexcept +{ + auto header = mepoo::ChunkHeader::fromPayload(chunk); + port().releaseChunk(header); +} + } // namespace popo } // namespace iox diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/untyped_subscriber.inl b/iceoryx_posh/include/iceoryx_posh/internal/popo/untyped_subscriber.inl index cd468d10987..d118c458341 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/untyped_subscriber.inl +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/untyped_subscriber.inl @@ -41,9 +41,9 @@ inline cxx::expected UntypedSubscriberImpl -inline void UntypedSubscriberImpl::releaseChunk(const void* payload) noexcept +inline void UntypedSubscriberImpl::release(const void* chunk) noexcept { - auto header = mepoo::ChunkHeader::fromPayload(payload); + auto header = mepoo::ChunkHeader::fromPayload(chunk); port().releaseChunk(header); } 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 bb9b673c4ff..16d8eeb1232 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/wait_set.inl +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/wait_set.inl @@ -88,7 +88,7 @@ inline cxx::expected WaitSet::attachEvent(T& eventOrigin return attachEventImpl(eventOrigin, hasTriggeredCallback, eventId, eventCallback).and_then([&](auto& uniqueId) { EventAttorney::enableEvent( eventOrigin, - TriggerHandle(m_conditionVariableDataPtr, {*this, &WaitSet::removeTrigger}, uniqueId), + TriggerHandle(*m_conditionVariableDataPtr, {*this, &WaitSet::removeTrigger}, uniqueId), eventType); }); } @@ -112,7 +112,7 @@ inline cxx::expected WaitSet::attachEvent(T& eventOrigin return attachEventImpl(eventOrigin, hasTriggeredCallback, eventId, eventCallback).and_then([&](auto& uniqueId) { EventAttorney::enableEvent( - eventOrigin, TriggerHandle(m_conditionVariableDataPtr, {*this, &WaitSet::removeTrigger}, uniqueId)); + eventOrigin, TriggerHandle(*m_conditionVariableDataPtr, {*this, &WaitSet::removeTrigger}, uniqueId)); }); } 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 57ab23e799e..0e316b1ee18 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/roudi/port_manager.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/roudi/port_manager.hpp @@ -21,6 +21,7 @@ #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" @@ -89,20 +90,27 @@ class PortManager cxx::expected acquireConditionVariableData(const ProcessName_t& process) noexcept; - void deletePortsOfProcess(const ProcessName_t& processName) noexcept; - - void destroyPublisherPort(PublisherPortRouDiType::MemberType_t* const publisherPortData) noexcept; + cxx::expected + acquireEventVariableData(const ProcessName_t& process) noexcept; - void destroySubscriberPort(SubscriberPortType::MemberType_t* const subscriberPortData) noexcept; + void deletePortsOfProcess(const ProcessName_t& processName) noexcept; const std::atomic* serviceRegistryChangeCounter() noexcept; runtime::IpcMessage findService(const capro::ServiceDescription& service) noexcept; protected: + void destroyPublisherPort(PublisherPortRouDiType::MemberType_t* const publisherPortData) noexcept; + + void destroySubscriberPort(SubscriberPortType::MemberType_t* const subscriberPortData) noexcept; + void handlePublisherPorts() noexcept; + void doDiscoveryForPublisherPort(PublisherPortRouDiType& publisherPort) noexcept; + void handleSubscriberPorts() noexcept; + void doDiscoveryForSubscriberPort(SubscriberPortType& subscriberPort) noexcept; + void handleInterfaces() noexcept; void handleApplications() noexcept; @@ -111,6 +119,8 @@ class PortManager void handleConditionVariables() noexcept; + void handleEventVariables() noexcept; + bool sendToAllMatchingPublisherPorts(const capro::CaproMessage& message, SubscriberPortType& subscriberSource) noexcept; @@ -123,8 +133,8 @@ class PortManager void removeEntryFromServiceRegistry(const capro::IdString_t& service, const capro::IdString_t& instance) noexcept; template ::value>* = nullptr> - cxx::optional doesViolateCommunicationPolicy(const capro::ServiceDescription& service) const - noexcept; + cxx::optional + doesViolateCommunicationPolicy(const capro::ServiceDescription& service) const noexcept; template ::value>* = nullptr> cxx::optional doesViolateCommunicationPolicy(const capro::ServiceDescription& service 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 57f17cfd88e..b921280d87f 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 @@ -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. @@ -18,6 +19,7 @@ #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" @@ -56,6 +58,7 @@ 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 de015605876..d814e40e138 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/roudi/roudi_process.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/roudi/roudi_process.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2019, 2021 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"); @@ -158,6 +158,8 @@ 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 48be51d65db..6bcc8365291 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,6 +61,8 @@ 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, @@ -85,9 +87,11 @@ enum class IpcMessageErrorType : int32_t REQUEST_PUBLISHER_WRONG_IPC_MESSAGE_RESPONSE, REQUEST_SUBSCRIBER_WRONG_IPC_MESSAGE_RESPONSE, REQUEST_CONDITION_VARIABLE_WRONG_IPC_MESSAGE_RESPONSE, + REQUEST_EVENT_VARIABLE_WRONG_IPC_MESSAGE_RESPONSE, PUBLISHER_LIST_FULL, SUBSCRIBER_LIST_FULL, CONDITION_VARIABLE_LIST_FULL, + EVENT_VARIABLE_LIST_FULL, NODE_DATA_LIST_FULL, END, }; diff --git a/iceoryx_posh/include/iceoryx_posh/popo/active_call_set.hpp b/iceoryx_posh/include/iceoryx_posh/popo/active_call_set.hpp new file mode 100644 index 00000000000..da5fa936eca --- /dev/null +++ b/iceoryx_posh/include/iceoryx_posh/popo/active_call_set.hpp @@ -0,0 +1,211 @@ +// 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_ACTIVE_CALL_SET_HPP +#define IOX_POSH_POPO_ACTIVE_CALL_SET_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/popo/event_attorney.hpp" +#include "iceoryx_posh/popo/trigger_handle.hpp" +#include "iceoryx_utils/cxx/expected.hpp" +#include "iceoryx_utils/cxx/method_callback.hpp" +#include "iceoryx_utils/cxx/type_traits.hpp" +#include "iceoryx_utils/internal/concurrent/loffli.hpp" +#include "iceoryx_utils/internal/concurrent/smart_lock.hpp" + +#include + +namespace iox +{ +namespace popo +{ +enum class ActiveCallSetError +{ + INVALID_STATE, + ACTIVE_CALL_SET_FULL, + EVENT_ALREADY_ATTACHED, +}; + +/// @brief The ActiveCallSet is a class which reacts to registered events by +/// executing a corresponding callback concurrently. This is achieved via +/// an encapsulated thread inside this class. +/// @note The ActiveCallSet is threadsafe and can be used without any restrictions concurrently. +/// @attention Calling detachEvent for the same event from multiple threads is supported but +/// can cause a race condition if you attach the same event again concurrently from +/// another thread. +/// Example: +/// 1. One calls detachEvent [1] from thread A, B and C +/// 2. thread B wins and detaches event [1] +/// 3. A new thread D spawns and would like to attach event [1] again while thread A and C are +/// still waiting to detach [1]. +/// 4. Thread A wins but cannot detach event [1] since it is not attached. +/// 5. Thread D wins and attaches event [1]. +/// 6. Finally thread C can continue and detaches event [1] again. +/// +/// If thread D is executed last then the event is attached. So depending on the operating +/// system defined execution order the event is either attached or detached. +/// +/// Best practice: Detach a specific event only from one specific thread and not +/// from multiple contexts. +class ActiveCallSet +{ + public: + template + using CallbackRef_t = void (&)(T* const); + using TranslationCallbackRef_t = void (&)(void* const, void (*const)(void* const)); + + template + using CallbackPtr_t = void (*)(T* const); + using TranslationCallbackPtr_t = void (*)(void* const, void (*const)(void* const)); + + ActiveCallSet() noexcept; + ActiveCallSet(const ActiveCallSet&) = delete; + ActiveCallSet(ActiveCallSet&&) = delete; + ~ActiveCallSet(); + + ActiveCallSet& operator=(const ActiveCallSet&) = delete; + ActiveCallSet& operator=(ActiveCallSet&&) = delete; + + /// @brief Attaches an event. Hereby the event is defined as a class T, the eventOrigin and + /// the corresponding callback which will be called when the event occurs. + /// @note This method can be called from any thread concurrently without any restrictions! + /// @tparam[in] T type of the class which will signal the event + /// @param[in] eventOrigin the object which will signal the event (the origin) + /// @param[in] eventCallback callback which will be executed concurrently when the event occurs + /// @return If an error occurs the enum packed inside an expected which describes the error. + template + cxx::expected attachEvent(T& eventOrigin, CallbackRef_t eventCallback) noexcept; + + /// @brief Attaches an event. Hereby the event is defined as a class T, the eventOrigin, an enum which further + /// defines the event inside the class and the corresponding callback which will be called when the event + /// occurs. + /// @note This method can be called from any thread concurrently without any restrictions! + /// @tparam[in] T type of the class which will signal the event + /// @param[in] eventOrigin the object which will signal the event (the origin) + /// @param[in] eventType enum required to specify the type of event inside of eventOrigin + /// @param[in] eventCallback callback which will be executed concurrently when the event occurs + /// @return If an error occurs the enum packed inside an expected which describes the error. + template ::value>> + cxx::expected + attachEvent(T& eventOrigin, const EventType eventType, CallbackRef_t eventCallback) noexcept; + + /// @brief Detaches an event. Hereby, the event is defined as a class T, the eventOrigin and + /// the eventType with further specifies the event inside of eventOrigin + /// @note This method can be called from any thread concurrently without any restrictions! + /// @tparam[in] T type of the class which will signal the event + /// @param[in] eventOrigin the object which will signal the event (the origin) + /// @param[in] eventType enum required to specify the type of event inside of eventOrigin + template ::value>> + void detachEvent(T& eventOrigin, const EventType eventType) noexcept; + + /// @brief Detaches an event. Hereby, the event is defined as a class T, the eventOrigin. + /// @note This method can be called from any thread concurrently without any restrictions! + /// @tparam[in] T type of the class which will signal the event + template + void detachEvent(T& eventOrigin) noexcept; + + /// @brief Returns the capacity of the ActiveCallSet + /// @return capacity of the ActiveCallSet + static constexpr uint64_t capacity() noexcept; + + /// @brief Returns the size of the ActiveCallSet + /// @return size of the ActiveCallSet + uint64_t size() const noexcept; + + protected: + ActiveCallSet(EventVariableData* eventVariable) noexcept; + + private: + class Event_t; + + void threadLoop() noexcept; + cxx::expected + addEvent(void* const origin, + const uint64_t eventType, + const uint64_t eventTypeHash, + CallbackRef_t callback, + TranslationCallbackRef_t translationCallback, + const cxx::MethodCallback invalidationCallback) noexcept; + + void removeTrigger(const uint64_t index) noexcept; + + private: + enum class NoEnumUsed + { + PLACEHOLDER = 0 + }; + + class Event_t + { + public: + ~Event_t(); + + bool isEqualTo(const void* const origin, const uint64_t eventType, const uint64_t eventTypeHash) const noexcept; + bool reset() noexcept; + void init(const uint64_t eventId, + void* const origin, + const uint64_t eventType, + const uint64_t eventTypeHash, + CallbackRef_t callback, + TranslationCallbackRef_t translationCallback, + const cxx::MethodCallback invalidationCallback) noexcept; + void executeCallback() noexcept; + bool isInitialized() const noexcept; + + private: + static constexpr uint64_t INVALID_ID = std::numeric_limits::max(); + + void* m_origin = nullptr; + uint64_t m_eventType = INVALID_ID; + uint64_t m_eventTypeHash = INVALID_ID; + + CallbackPtr_t m_callback = nullptr; + TranslationCallbackPtr_t m_translationCallback = nullptr; + + uint64_t m_eventId = INVALID_ID; + cxx::MethodCallback m_invalidationCallback; + }; + + class IndexManager_t + { + public: + IndexManager_t() noexcept; + bool pop(uint32_t& index) noexcept; + void push(const uint32_t index) noexcept; + uint64_t indicesInUse() const noexcept; + + uint32_t m_loffliStorage[concurrent::LoFFLi::requiredMemorySize(MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET) + / sizeof(uint32_t)]; + concurrent::LoFFLi m_loffli; + std::atomic m_indicesInUse{0U}; + } m_indexManager; + + + std::thread m_thread; + concurrent::smart_lock m_events[MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET]; + std::mutex m_addEventMutex; + + std::atomic_bool m_wasDtorCalled{false}; + EventVariableData* m_eventVariable = nullptr; + EventListener m_eventListener; +}; +} // namespace popo +} // namespace iox + +#include "iceoryx_posh/internal/popo/active_call_set.inl" + +#endif diff --git a/iceoryx_posh/include/iceoryx_posh/popo/event_attorney.hpp b/iceoryx_posh/include/iceoryx_posh/popo/event_attorney.hpp index 7dee00dd321..d0ac2a18322 100644 --- a/iceoryx_posh/include/iceoryx_posh/popo/event_attorney.hpp +++ b/iceoryx_posh/include/iceoryx_posh/popo/event_attorney.hpp @@ -32,6 +32,7 @@ class EventAttorney { template friend class WaitSet; + friend class ActiveCallSet; private: template diff --git a/iceoryx_posh/include/iceoryx_posh/popo/publisher_options.hpp b/iceoryx_posh/include/iceoryx_posh/popo/publisher_options.hpp index 9d76afaf4c4..e6bd024c001 100644 --- a/iceoryx_posh/include/iceoryx_posh/popo/publisher_options.hpp +++ b/iceoryx_posh/include/iceoryx_posh/popo/publisher_options.hpp @@ -32,6 +32,9 @@ struct PublisherOptions /// @brief The name of the node where the publisher should belong to iox::NodeName_t nodeName{""}; + + /// @brief The option whether the publisher should already be offered when creating it + bool offerOnCreate{true}; }; } // namespace popo diff --git a/iceoryx_posh/include/iceoryx_posh/popo/subscriber_options.hpp b/iceoryx_posh/include/iceoryx_posh/popo/subscriber_options.hpp index 4f67b7f0cad..d2e31cd8693 100644 --- a/iceoryx_posh/include/iceoryx_posh/popo/subscriber_options.hpp +++ b/iceoryx_posh/include/iceoryx_posh/popo/subscriber_options.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2021 by Apex.AI Inc. 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. @@ -35,8 +35,11 @@ struct SubscriberOptions /// @brief The max number of chunks received after subscription if chunks are available uint64_t historyRequest{0U}; - /// @ brief The name of the node where the subscriber should belong to + /// @brief The name of the node where the subscriber should belong to iox::NodeName_t nodeName{""}; + + /// @brief The option whether the subscriber shall try to subscribe when creating it + bool subscribeOnCreate{true}; }; } // namespace popo diff --git a/iceoryx_posh/include/iceoryx_posh/popo/trigger.hpp b/iceoryx_posh/include/iceoryx_posh/popo/trigger.hpp index 63a4f38ad24..13c44a91521 100644 --- a/iceoryx_posh/include/iceoryx_posh/popo/trigger.hpp +++ b/iceoryx_posh/include/iceoryx_posh/popo/trigger.hpp @@ -92,7 +92,6 @@ class Trigger /// @brief returns true if the Triggers are logical equal otherwise false. Two Triggers are logical equal when /// - origin == rhs.origin /// - hasTriggeredCallback == rhs.hasTriggeredCallback - /// - eventId == rhs.eventId bool isLogicalEqualTo(const Trigger& rhs) const noexcept; /// @brief sets a new origin of the trigger @@ -108,7 +107,7 @@ class Trigger cxx::ConstMethodCallback m_hasTriggeredCallback; cxx::MethodCallback m_resetCallback; - uint64_t m_uniqueId = 0U; + 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 c92f6e3a945..e6cb47e8c2c 100644 --- a/iceoryx_posh/include/iceoryx_posh/popo/trigger_handle.hpp +++ b/iceoryx_posh/include/iceoryx_posh/popo/trigger_handle.hpp @@ -18,9 +18,11 @@ #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" +#include #include namespace iox @@ -38,11 +40,20 @@ class TriggerHandle public: TriggerHandle() = default; /// @brief Creates a TriggerHandle - /// @param[in] conditionVariableDataPtr pointer to a condition variable data struct + /// @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* const conditionVariableDataPtr, + TriggerHandle(ConditionVariableData& conditionVariableDataRef, const cxx::MethodCallback resetCallback, const uint64_t uniqueTriggerId) noexcept; TriggerHandle(const TriggerHandle&) = delete; @@ -76,10 +87,14 @@ 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 = 0U; + 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/untyped_publisher.hpp b/iceoryx_posh/include/iceoryx_posh/popo/untyped_publisher.hpp index 061fe66ce19..3cd6d33872e 100644 --- a/iceoryx_posh/include/iceoryx_posh/popo/untyped_publisher.hpp +++ b/iceoryx_posh/include/iceoryx_posh/popo/untyped_publisher.hpp @@ -47,7 +47,7 @@ class UntypedPublisherImpl : public base_publisher_t cxx::expected loan(const uint32_t size) noexcept; /// - /// @brief loanPreviousChunk Get the previously loanded chunk if possible. + /// @brief loanPreviousChunk Get the previously loaned chunk if possible. /// @return A pointer to the previous chunk if available, nullopt otherwise. /// cxx::optional loanPreviousChunk() noexcept; @@ -59,6 +59,16 @@ class UntypedPublisherImpl : public base_publisher_t /// void publish(const void* chunk) noexcept; + /// + /// @brief Releases the ownership of the chunk provided by the payload pointer. + /// @param chunk pointer to the payload of the chunk to be released + /// @details The chunk must have been previously provided by loan or loanPreviousChunk + /// and not have been already released. + /// The chunk must not be accessed afterwards as its memory may have + /// been reclaimed. + /// + void release(const void* chunk) noexcept; + protected: using base_publisher_t::port; }; diff --git a/iceoryx_posh/include/iceoryx_posh/popo/untyped_subscriber.hpp b/iceoryx_posh/include/iceoryx_posh/popo/untyped_subscriber.hpp index f6e63c5714a..2aaee077340 100644 --- a/iceoryx_posh/include/iceoryx_posh/popo/untyped_subscriber.hpp +++ b/iceoryx_posh/include/iceoryx_posh/popo/untyped_subscriber.hpp @@ -55,12 +55,14 @@ class UntypedSubscriberImpl : public base_subscriber_t cxx::expected take() noexcept; /// - /// @brief Releases the chunk provided by the payload pointer. - /// @param payload pointer to the payload of the chunk to be released + /// @brief Releases the ownership of the chunk provided by the payload pointer. + /// @param chunk pointer to the payload of the chunk to be released /// @details The chunk must have been previously provided by take and /// not have been already released. + /// The chunk must not be accessed afterwards as its memory may have + /// been reclaimed. /// - void releaseChunk(const void* payload) noexcept; + void release(const void* chunk) noexcept; protected: using BaseSubscriber::port; diff --git a/iceoryx_posh/include/iceoryx_posh/roudi/port_pool.hpp b/iceoryx_posh/include/iceoryx_posh/roudi/port_pool.hpp index a899a6999be..a2395c07daf 100644 --- a/iceoryx_posh/include/iceoryx_posh/roudi/port_pool.hpp +++ b/iceoryx_posh/include/iceoryx_posh/roudi/port_pool.hpp @@ -19,6 +19,7 @@ #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" @@ -47,6 +48,7 @@ enum class PortPoolError : uint8_t APPLICATION_PORT_LIST_FULL, NODE_DATA_LIST_FULL, CONDITION_VARIABLE_LIST_FULL, + EVENT_VARIABLE_LIST_FULL, }; class PortPool @@ -66,6 +68,7 @@ class PortPool cxx::vector getNodeDataList() noexcept; cxx::vector getConditionVariableDataList() noexcept; + cxx::vector getEventVariableDataList() noexcept; cxx::expected addPublisherPort(const capro::ServiceDescription& serviceDescription, @@ -104,12 +107,15 @@ 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 7a5e52e28e6..bdd82c37e08 100644 --- a/iceoryx_posh/include/iceoryx_posh/runtime/posh_runtime.hpp +++ b/iceoryx_posh/include/iceoryx_posh/runtime/posh_runtime.hpp @@ -20,6 +20,7 @@ #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" @@ -51,6 +52,13 @@ namespace runtime class Node; class NodeData; +enum class FindServiceError +{ + INVALID_STATE, + UNABLE_TO_WRITE_TO_ROUDI_CHANNEL, + INSTANCE_CONTAINER_OVERFLOW +}; + /// @brief The runtime that is needed for each application to communicate with the RouDi daemon class PoshRuntime { @@ -73,12 +81,11 @@ class PoshRuntime /// @brief find all services that match the provided service description /// @param[in] serviceDescription service to search for - /// @return cxx::expected + /// @return cxx::expected /// InstanceContainer: on success, container that is filled with all matching instances - /// Error: if any, encountered during the operation - /// Error::kPOSH__SERVICE_DISCOVERY_INSTANCE_CONTAINER_OVERFLOW : Number of instances can't fit in instanceContainer - /// Error::kIPC_INTERFACE__REG_UNABLE_TO_WRITE_TO_ROUDI_CHANNEL : Find Service Request could not be sent to RouDi - cxx::expected findService(const capro::ServiceDescription& serviceDescription) noexcept; + /// FindServiceError: if any, encountered during the operation + cxx::expected + findService(const capro::ServiceDescription& serviceDescription) noexcept; /// @brief offer the provided service, sends the offer from application to RouDi daemon /// @param[in] serviceDescription service to offer @@ -122,10 +129,14 @@ class PoshRuntime /// @return pointer to a created application port data popo::ApplicationPortData* getMiddlewareApplication() noexcept; - /// @brief request the RouDi daemon to create an condition variable + /// @brief request the RouDi daemon to create a condition variable /// @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 @@ -189,6 +200,9 @@ 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/active_call_set.cpp b/iceoryx_posh/source/popo/active_call_set.cpp new file mode 100644 index 00000000000..d9883ec2e05 --- /dev/null +++ b/iceoryx_posh/source/popo/active_call_set.cpp @@ -0,0 +1,203 @@ +// 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/popo/active_call_set.hpp" +#include "iceoryx_posh/internal/popo/building_blocks/event_notifier.hpp" +#include "iceoryx_posh/runtime/posh_runtime.hpp" +#include "iceoryx_utils/cxx/helplets.hpp" + +namespace iox +{ +namespace popo +{ +ActiveCallSet::ActiveCallSet() noexcept + : ActiveCallSet(runtime::PoshRuntime::getInstance().getMiddlewareEventVariable()) +{ +} + +ActiveCallSet::ActiveCallSet(EventVariableData* eventVariable) noexcept + : m_eventVariable(eventVariable) + , m_eventListener(*eventVariable) +{ + m_thread = std::thread(&ActiveCallSet::threadLoop, this); +} + +ActiveCallSet::~ActiveCallSet() +{ + m_wasDtorCalled.store(true, std::memory_order_relaxed); + m_eventListener.destroy(); + + m_thread.join(); + m_eventVariable->m_toBeDestroyed.store(true, std::memory_order_relaxed); +} + +cxx::expected +ActiveCallSet::addEvent(void* const origin, + const uint64_t eventType, + const uint64_t eventTypeHash, + CallbackRef_t callback, + TranslationCallbackRef_t translationCallback, + const cxx::MethodCallback invalidationCallback) noexcept +{ + std::lock_guard lock(m_addEventMutex); + + for (uint32_t i = 0U; i < MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET; ++i) + { + if (m_events[i]->isEqualTo(origin, eventType, eventTypeHash)) + { + return cxx::error(ActiveCallSetError::EVENT_ALREADY_ATTACHED); + } + } + + uint32_t index = 0U; + if (!m_indexManager.pop(index)) + { + return cxx::error(ActiveCallSetError::ACTIVE_CALL_SET_FULL); + } + + m_events[index]->init(index, origin, eventType, eventTypeHash, callback, translationCallback, invalidationCallback); + return cxx::success(index); +} + +uint64_t ActiveCallSet::size() const noexcept +{ + return m_indexManager.indicesInUse(); +} + +void ActiveCallSet::threadLoop() noexcept +{ + while (m_wasDtorCalled.load(std::memory_order_relaxed) == false) + { + auto activateNotificationIds = m_eventListener.wait(); + + cxx::forEach(activateNotificationIds, [this](auto id) { m_events[id]->executeCallback(); }); + } +} + +void ActiveCallSet::removeTrigger(const uint64_t index) noexcept +{ + if (index >= MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET) + { + return; + } + + if (m_events[index]->reset()) + { + m_indexManager.push(static_cast(index)); + } +} + +//////////////// +// Event_t +//////////////// +ActiveCallSet::Event_t::~Event_t() +{ + reset(); +} + +void ActiveCallSet::Event_t::executeCallback() noexcept +{ + if (!isInitialized()) + { + return; + } + + m_translationCallback(m_origin, m_callback); +} + +void ActiveCallSet::Event_t::init(const uint64_t eventId, + void* const origin, + const uint64_t eventType, + const uint64_t eventTypeHash, + CallbackRef_t callback, + TranslationCallbackRef_t translationCallback, + const cxx::MethodCallback invalidationCallback) noexcept +{ + m_eventId = eventId; + m_origin = origin; + m_eventType = eventType; + m_eventTypeHash = eventTypeHash; + m_callback = &callback; + m_translationCallback = &translationCallback; + m_invalidationCallback = invalidationCallback; +} + +bool ActiveCallSet::Event_t::isEqualTo(const void* const origin, + const uint64_t eventType, + const uint64_t eventTypeHash) const noexcept +{ + return (m_origin == origin && m_eventType == eventType && m_eventTypeHash == eventTypeHash); +} + +bool ActiveCallSet::Event_t::reset() noexcept +{ + if (isInitialized()) + { + m_invalidationCallback(m_eventId); + + m_eventId = INVALID_ID; + m_origin = nullptr; + m_eventType = INVALID_ID; + m_eventTypeHash = INVALID_ID; + m_callback = nullptr; + m_translationCallback = nullptr; + m_invalidationCallback = cxx::MethodCallback(); + + return true; + } + return false; +} + +bool ActiveCallSet::Event_t::isInitialized() const noexcept +{ + return m_origin != nullptr && m_eventId != INVALID_ID && m_eventType != INVALID_ID && m_eventTypeHash != INVALID_ID + && m_callback != nullptr && m_translationCallback != nullptr + && m_invalidationCallback != cxx::MethodCallback(); +} + +////////////////// +// IndexManager_t +////////////////// + +ActiveCallSet::IndexManager_t::IndexManager_t() noexcept +{ + m_loffli.init(m_loffliStorage, MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET); +} + +bool ActiveCallSet::IndexManager_t::pop(uint32_t& value) noexcept +{ + if (m_loffli.pop(value)) + { + ++m_indicesInUse; + return true; + } + return false; +} + +void ActiveCallSet::IndexManager_t::push(const uint32_t index) noexcept +{ + cxx::Expects(m_loffli.push(index)); + --m_indicesInUse; +} + +uint64_t ActiveCallSet::IndexManager_t::indicesInUse() const noexcept +{ + return m_indicesInUse.load(std::memory_order_relaxed); +} + + +} // 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 new file mode 100644 index 00000000000..ac9b0e86863 --- /dev/null +++ b/iceoryx_posh/source/popo/building_blocks/event_listener.cpp @@ -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 + + +#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_ACTIVE_CALL_SET; 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_ACTIVE_CALL_SET) + { + 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 new file mode 100644 index 00000000000..de7d1327264 --- /dev/null +++ b/iceoryx_posh/source/popo/building_blocks/event_notifier.cpp @@ -0,0 +1,47 @@ +// 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_ACTIVE_CALL_SET) + { + LogError() << "The provided index " << index << " is too large. The index has to be in the range of [0, " + << MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET << "[."; + errorHandler(Error::kPOPO__EVENT_NOTIFIER_INDEX_TOO_LARGE, nullptr, ErrorLevel::MODERATE); + } +} + +void EventNotifier::notify() noexcept +{ + if (m_notificationIndex < MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET) + { + 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/building_blocks/event_variable_data.cpp b/iceoryx_posh/source/popo/building_blocks/event_variable_data.cpp new file mode 100644 index 00000000000..4cff60df575 --- /dev/null +++ b/iceoryx_posh/source/popo/building_blocks/event_variable_data.cpp @@ -0,0 +1,42 @@ +// 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_variable_data.hpp" + +namespace iox +{ +namespace popo +{ +EventVariableData::EventVariableData() noexcept +{ + for (auto& id : m_activeNotifications) + { + id.store(false, std::memory_order_relaxed); + } +} + +EventVariableData::EventVariableData(const ProcessName_t& process) noexcept + : ConditionVariableData(process) +{ + for (auto& id : m_activeNotifications) + { + id.store(false, std::memory_order_relaxed); + } +} +} // namespace popo +} // namespace iox + diff --git a/iceoryx_posh/source/popo/ports/publisher_port_data.cpp b/iceoryx_posh/source/popo/ports/publisher_port_data.cpp index 8151f9e3b55..3e8d4dd0c11 100644 --- a/iceoryx_posh/source/popo/ports/publisher_port_data.cpp +++ b/iceoryx_posh/source/popo/ports/publisher_port_data.cpp @@ -1,4 +1,5 @@ -// Copyright (c) 2020, 2021 by Robert Bosch GmbH, Apex.AI Inc. All rights reserved. +// 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. @@ -27,6 +28,7 @@ PublisherPortData::PublisherPortData(const capro::ServiceDescription& serviceDes const mepoo::MemoryInfo& memoryInfo) noexcept : BasePortData(serviceDescription, processName, publisherOptions.nodeName) , m_chunkSenderData(memoryManager, publisherOptions.historyCapacity, memoryInfo) + , m_offeringRequested(publisherOptions.offerOnCreate) { } diff --git a/iceoryx_posh/source/popo/ports/publisher_port_roudi.cpp b/iceoryx_posh/source/popo/ports/publisher_port_roudi.cpp index e05c685760c..5fa87dcbb69 100644 --- a/iceoryx_posh/source/popo/ports/publisher_port_roudi.cpp +++ b/iceoryx_posh/source/popo/ports/publisher_port_roudi.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. diff --git a/iceoryx_posh/source/popo/ports/subscriber_port_data.cpp b/iceoryx_posh/source/popo/ports/subscriber_port_data.cpp index 44cdea33323..ff73c427faa 100644 --- a/iceoryx_posh/source/popo/ports/subscriber_port_data.cpp +++ b/iceoryx_posh/source/popo/ports/subscriber_port_data.cpp @@ -1,4 +1,5 @@ -// Copyright (c) 2020, 2021 by Robert Bosch GmbH, Apex.AI Inc. All rights reserved. +// 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. @@ -29,6 +30,7 @@ SubscriberPortData::SubscriberPortData(const capro::ServiceDescription& serviceD : BasePortData(serviceDescription, processName, subscriberOptions.nodeName) , m_chunkReceiverData(queueType, memoryInfo) , m_historyRequest(subscriberOptions.historyRequest) + , m_subscribeRequested(subscriberOptions.subscribeOnCreate) { m_chunkReceiverData.m_queue.setCapacity(subscriberOptions.queueCapacity); } diff --git a/iceoryx_posh/source/popo/ports/subscriber_port_user.cpp b/iceoryx_posh/source/popo/ports/subscriber_port_user.cpp index 90d5dfb8600..f260b62188f 100644 --- a/iceoryx_posh/source/popo/ports/subscriber_port_user.cpp +++ b/iceoryx_posh/source/popo/ports/subscriber_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. @@ -63,7 +64,7 @@ SubscribeState SubscriberPortUser::getSubscriptionState() const noexcept return getMembers()->m_subscriptionState; } -cxx::expected, ChunkReceiveResult> SubscriberPortUser::tryGetChunk() noexcept +cxx::expected SubscriberPortUser::tryGetChunk() noexcept { return m_chunkReceiver.tryGet(); } @@ -93,6 +94,11 @@ void SubscriberPortUser::setConditionVariable(ConditionVariableData* conditionVa m_chunkReceiver.setConditionVariable(conditionVariableDataPtr); } +void SubscriberPortUser::setEventVariable(EventVariableData& eventVariableData, const uint64_t eventId) noexcept +{ + m_chunkReceiver.setEventVariable(eventVariableData, eventId); +} + void SubscriberPortUser::unsetConditionVariable() noexcept { m_chunkReceiver.unsetConditionVariable(); diff --git a/iceoryx_posh/source/popo/trigger.cpp b/iceoryx_posh/source/popo/trigger.cpp index 95a945d66c5..8dff1a108f3 100644 --- a/iceoryx_posh/source/popo/trigger.cpp +++ b/iceoryx_posh/source/popo/trigger.cpp @@ -22,6 +22,7 @@ namespace iox namespace popo { std::atomic Trigger::uniqueIdCounter{0U}; +constexpr uint64_t Trigger::INVALID_TRIGGER_ID; Trigger::~Trigger() { @@ -57,6 +58,7 @@ void Trigger::invalidate() noexcept { m_hasTriggeredCallback = cxx::ConstMethodCallback(); m_resetCallback = cxx::MethodCallback(); + m_uniqueId = INVALID_TRIGGER_ID; } Trigger::operator bool() const noexcept @@ -72,8 +74,7 @@ bool Trigger::isValid() const noexcept bool Trigger::isLogicalEqualTo(const Trigger& rhs) const noexcept { return (isValid() && rhs.isValid() && m_eventInfo.m_eventOrigin == rhs.m_eventInfo.m_eventOrigin - && m_hasTriggeredCallback == rhs.m_hasTriggeredCallback - && m_eventInfo.m_eventId == rhs.m_eventInfo.m_eventId); + && m_hasTriggeredCallback == rhs.m_hasTriggeredCallback); } Trigger::Trigger(Trigger&& rhs) noexcept diff --git a/iceoryx_posh/source/popo/trigger_handle.cpp b/iceoryx_posh/source/popo/trigger_handle.cpp index ffe17f894b9..4bf97bdc15c 100644 --- a/iceoryx_posh/source/popo/trigger_handle.cpp +++ b/iceoryx_posh/source/popo/trigger_handle.cpp @@ -16,15 +16,24 @@ #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" namespace iox { namespace popo { -TriggerHandle::TriggerHandle(ConditionVariableData* const conditionVariableDataPtr, +TriggerHandle::TriggerHandle(EventVariableData& eventVariableDataRef, const cxx::MethodCallback resetCallback, const uint64_t uniqueTriggerId) noexcept - : m_conditionVariableDataPtr(conditionVariableDataPtr) + : 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_resetCallback(resetCallback) , m_uniqueTriggerId(uniqueTriggerId) { @@ -48,6 +57,7 @@ TriggerHandle& TriggerHandle::operator=(TriggerHandle&& rhs) noexcept m_conditionVariableDataPtr = std::move(rhs.m_conditionVariableDataPtr); m_resetCallback = std::move(rhs.m_resetCallback); m_uniqueTriggerId = rhs.m_uniqueTriggerId; + m_doesContainEventVariable = rhs.m_doesContainEventVariable; rhs.invalidate(); } @@ -78,7 +88,15 @@ void TriggerHandle::trigger() noexcept if (isValid()) { - ConditionVariableSignaler(m_conditionVariableDataPtr).notifyOne(); + if (m_doesContainEventVariable) + { + EventNotifier(*reinterpret_cast(m_conditionVariableDataPtr), m_uniqueTriggerId) + .notify(); + } + else + { + ConditionVariableSignaler(m_conditionVariableDataPtr).notifyOne(); + } } } @@ -102,7 +120,7 @@ void TriggerHandle::invalidate() noexcept m_conditionVariableDataPtr = nullptr; m_resetCallback = cxx::MethodCallback(); - m_uniqueTriggerId = 0U; + m_uniqueTriggerId = Trigger::INVALID_TRIGGER_ID; } ConditionVariableData* TriggerHandle::getConditionVariableData() noexcept @@ -119,5 +137,10 @@ 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 16a6b6bdb0f..00f12308011 100644 --- a/iceoryx_posh/source/roudi/port_manager.cpp +++ b/iceoryx_posh/source/roudi/port_manager.cpp @@ -129,6 +129,8 @@ void PortManager::doDiscovery() noexcept handleNodes(); handleConditionVariables(); + + handleEventVariables(); } void PortManager::handlePublisherPorts() noexcept @@ -138,30 +140,7 @@ void PortManager::handlePublisherPorts() noexcept { PublisherPortRouDiType publisherPort(publisherPortData); - publisherPort.tryGetCaProMessage().and_then([this, &publisherPort](auto caproMessage) { - m_portIntrospection.reportMessage(caproMessage); - if (capro::CaproMessageType::OFFER == caproMessage.m_type) - { - this->addEntryToServiceRegistry(caproMessage.m_serviceDescription.getServiceIDString(), - caproMessage.m_serviceDescription.getInstanceIDString()); - } - else if (capro::CaproMessageType::STOP_OFFER == caproMessage.m_type) - { - this->removeEntryFromServiceRegistry(caproMessage.m_serviceDescription.getServiceIDString(), - caproMessage.m_serviceDescription.getInstanceIDString()); - } - else - { - // protocol error - errorHandler(Error::kPORT_MANAGER__HANDLE_PUBLISHER_PORTS_INVALID_CAPRO_MESSAGE, - nullptr, - iox::ErrorLevel::MODERATE); - } - - this->sendToAllMatchingSubscriberPorts(caproMessage, publisherPort); - // forward to interfaces - this->sendToAllMatchingInterfacePorts(caproMessage); - }); + doDiscoveryForPublisherPort(publisherPort); // check if we have to destroy this publisher port if (publisherPort.toBeDestroyed()) @@ -171,6 +150,33 @@ void PortManager::handlePublisherPorts() noexcept } } +void PortManager::doDiscoveryForPublisherPort(PublisherPortRouDiType& publisherPort) noexcept +{ + publisherPort.tryGetCaProMessage().and_then([this, &publisherPort](auto caproMessage) { + m_portIntrospection.reportMessage(caproMessage); + if (capro::CaproMessageType::OFFER == caproMessage.m_type) + { + this->addEntryToServiceRegistry(caproMessage.m_serviceDescription.getServiceIDString(), + caproMessage.m_serviceDescription.getInstanceIDString()); + } + else if (capro::CaproMessageType::STOP_OFFER == caproMessage.m_type) + { + this->removeEntryFromServiceRegistry(caproMessage.m_serviceDescription.getServiceIDString(), + caproMessage.m_serviceDescription.getInstanceIDString()); + } + else + { + // protocol error + errorHandler( + Error::kPORT_MANAGER__HANDLE_PUBLISHER_PORTS_INVALID_CAPRO_MESSAGE, nullptr, iox::ErrorLevel::MODERATE); + } + + this->sendToAllMatchingSubscriberPorts(caproMessage, publisherPort); + // forward to interfaces + this->sendToAllMatchingInterfacePorts(caproMessage); + }); +} + void PortManager::handleSubscriberPorts() noexcept { // get requests for change of subscription state of subscribers @@ -178,29 +184,7 @@ void PortManager::handleSubscriberPorts() noexcept { SubscriberPortType subscriberPort(subscriberPortData); - subscriberPort.tryGetCaProMessage().and_then([this, &subscriberPort](auto caproMessage) { - if ((capro::CaproMessageType::SUB == caproMessage.m_type) - || (capro::CaproMessageType::UNSUB == caproMessage.m_type)) - { - m_portIntrospection.reportMessage(caproMessage, subscriberPort.getUniqueID()); - if (!this->sendToAllMatchingPublisherPorts(caproMessage, subscriberPort)) - { - LogDebug() << "capro::SUB/UNSUB, no matching publisher!!"; - capro::CaproMessage nackMessage(capro::CaproMessageType::NACK, - subscriberPort.getCaProServiceDescription()); - auto returnMessage = subscriberPort.dispatchCaProMessageAndGetPossibleResponse(nackMessage); - // No response on NACK messages - cxx::Ensures(!returnMessage.has_value()); - } - } - else - { - // protocol error - errorHandler(Error::kPORT_MANAGER__HANDLE_SUBSCRIBER_PORTS_INVALID_CAPRO_MESSAGE, - nullptr, - iox::ErrorLevel::MODERATE); - } - }); + doDiscoveryForSubscriberPort(subscriberPort); // check if we have to destroy this subscriber port if (subscriberPort.toBeDestroyed()) @@ -210,6 +194,34 @@ void PortManager::handleSubscriberPorts() noexcept } } +void PortManager::doDiscoveryForSubscriberPort(SubscriberPortType& subscriberPort) noexcept +{ + subscriberPort.tryGetCaProMessage().and_then([this, &subscriberPort](auto caproMessage) { + if ((capro::CaproMessageType::SUB == caproMessage.m_type) + || (capro::CaproMessageType::UNSUB == caproMessage.m_type)) + { + m_portIntrospection.reportMessage(caproMessage, subscriberPort.getUniqueID()); + if (!this->sendToAllMatchingPublisherPorts(caproMessage, subscriberPort)) + { + LogDebug() << "capro::SUB/UNSUB, no matching publisher!!"; + capro::CaproMessage nackMessage(capro::CaproMessageType::NACK, + subscriberPort.getCaProServiceDescription()); + auto returnMessage = subscriberPort.dispatchCaProMessageAndGetPossibleResponse(nackMessage); + // No response on NACK messages + cxx::Ensures(!returnMessage.has_value()); + } + } + else + { + // protocol error + errorHandler(Error::kPORT_MANAGER__HANDLE_SUBSCRIBER_PORTS_INVALID_CAPRO_MESSAGE, + nullptr, + iox::ErrorLevel::MODERATE); + } + }); +} + + void PortManager::handleInterfaces() noexcept { // check if there are new interfaces that must get an initial offer information @@ -352,6 +364,18 @@ 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 { @@ -489,6 +513,15 @@ 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 @@ -600,6 +633,10 @@ PortManager::acquirePublisherPortData(const capro::ServiceDescription& service, if (publisherPortData) { m_portIntrospection.addPublisher(*publisherPortData); + + // we do discovery here for trying to connect the waiting subscribers if offer on create is desired + PublisherPortRouDiType publisherPort(publisherPortData); + doDiscoveryForPublisherPort(publisherPort); } } @@ -620,6 +657,10 @@ PortManager::acquireSubscriberPortData(const capro::ServiceDescription& service, if (subscriberPortData) { m_portIntrospection.addSubscriber(*subscriberPortData); + + // we do discovery here for trying to connect with publishers if subscribe on create is desired + SubscriberPortType subscriberPort(subscriberPortData); + doDiscoveryForSubscriberPort(subscriberPort); } } @@ -683,5 +724,11 @@ 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 d97452e123f..a5cbcb86fc3 100644 --- a/iceoryx_posh/source/roudi/port_pool.cpp +++ b/iceoryx_posh/source/roudi/port_pool.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. @@ -48,6 +49,11 @@ 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 { @@ -109,6 +115,21 @@ 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); @@ -129,6 +150,11 @@ 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 d02113644dc..44495405a3f 100644 --- a/iceoryx_posh/source/roudi/roudi.cpp +++ b/iceoryx_posh/source/roudi/roudi.cpp @@ -24,6 +24,7 @@ #include "iceoryx_posh/roudi/memory/roudi_memory_manager.hpp" #include "iceoryx_posh/runtime/port_config_info.hpp" #include "iceoryx_utils/cxx/convert.hpp" +#include "iceoryx_utils/cxx/helplets.hpp" #include "iceoryx_utils/posix_wrapper/thread.hpp" namespace iox @@ -46,6 +47,10 @@ RouDi::RouDi(RouDiMemoryInterface& roudiMemoryInterface, , m_monitoringMode(roudiStartupParameters.m_monitoringMode) , m_processKillDelay(roudiStartupParameters.m_processKillDelay) { + if (cxx::isCompiledOn32BitSystem()) + { + LogWarn() << "Runnning RouDi on 32-bit architectures is not supported! Use at your own risk!"; + } m_processIntrospection.registerPublisherPort(PublisherPortUserType( m_prcMgr.addIntrospectionPublisherPort(IntrospectionProcessService, IPC_CHANNEL_ROUDI_NAME))); m_prcMgr.initIntrospection(&m_processIntrospection); @@ -181,7 +186,7 @@ void RouDi::processMessage(const runtime::IpcMessage& message, } case runtime::IpcMessageType::CREATE_PUBLISHER: { - if (message.getNumberOfElements() != 6) + if (message.getNumberOfElements() != 7) { LogError() << "Wrong number of parameters for \"IpcMessageType::CREATE_PUBLISHER\" from \"" << processName << "\"received!"; @@ -189,11 +194,12 @@ void RouDi::processMessage(const runtime::IpcMessage& message, else { capro::ServiceDescription service(cxx::Serialization(message.getElementAtIndex(2))); - cxx::Serialization portConfigInfoSerialization(message.getElementAtIndex(5)); + cxx::Serialization portConfigInfoSerialization(message.getElementAtIndex(6)); popo::PublisherOptions options; options.historyCapacity = std::stoull(message.getElementAtIndex(3)); options.nodeName = NodeName_t(cxx::TruncateToCapacity, message.getElementAtIndex(4)); + options.offerOnCreate = (0U == std::stoull(message.getElementAtIndex(5))) ? false : true; m_prcMgr.addPublisherForProcess( processName, service, options, iox::runtime::PortConfigInfo(portConfigInfoSerialization)); @@ -202,7 +208,7 @@ void RouDi::processMessage(const runtime::IpcMessage& message, } case runtime::IpcMessageType::CREATE_SUBSCRIBER: { - if (message.getNumberOfElements() != 7) + if (message.getNumberOfElements() != 8) { LogError() << "Wrong number of parameters for \"IpcMessageType::CREATE_SUBSCRIBER\" from \"" << processName << "\"received!"; @@ -210,13 +216,15 @@ void RouDi::processMessage(const runtime::IpcMessage& message, else { capro::ServiceDescription service(cxx::Serialization(message.getElementAtIndex(2))); - cxx::Serialization portConfigInfoSerialization(message.getElementAtIndex(6)); + cxx::Serialization portConfigInfoSerialization(message.getElementAtIndex(7)); popo::SubscriberOptions options; options.historyRequest = std::stoull(message.getElementAtIndex(3)); options.queueCapacity = std::stoull(message.getElementAtIndex(4)); options.nodeName = NodeName_t(cxx::TruncateToCapacity, message.getElementAtIndex(5)); + options.subscribeOnCreate = (0U == std::stoull(message.getElementAtIndex(6))) ? false : true; + m_prcMgr.addSubscriberForProcess( processName, service, options, iox::runtime::PortConfigInfo(portConfigInfoSerialization)); @@ -229,8 +237,6 @@ void RouDi::processMessage(const runtime::IpcMessage& message, { LogError() << "Wrong number of parameters for \"IpcMessageType::CREATE_CONDITION_VARIABLE\" from \"" << processName << "\"received!"; - errorHandler( - Error::kPORT_MANAGER__INTROSPECTION_MEMORY_MANAGER_UNAVAILABLE, nullptr, iox::ErrorLevel::MODERATE); } else { @@ -238,6 +244,19 @@ 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_process.cpp b/iceoryx_posh/source/roudi/roudi_process.cpp index b53d21693b5..29d61b8069e 100644 --- a/iceoryx_posh/source/roudi/roudi_process.cpp +++ b/iceoryx_posh/source/roudi/roudi_process.cpp @@ -751,6 +751,44 @@ 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 = RelativePointer::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 a6c48a4e937..2c926b0ee6a 100644 --- a/iceoryx_posh/source/runtime/posh_runtime.cpp +++ b/iceoryx_posh/source/runtime/posh_runtime.cpp @@ -1,4 +1,5 @@ -// Copyright (c) 2019, 2021 by Robert Bosch GmbH, Apex.AI Inc. All rights reserved. +// Copyright (c) 2019 - 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. @@ -22,6 +23,7 @@ #include "iceoryx_posh/runtime/node.hpp" #include "iceoryx_posh/runtime/port_config_info.hpp" #include "iceoryx_utils/cxx/convert.hpp" +#include "iceoryx_utils/cxx/helplets.hpp" #include "iceoryx_utils/internal/relocatable_pointer/relative_ptr.hpp" #include "iceoryx_utils/posix_wrapper/timer.hpp" @@ -81,6 +83,10 @@ PoshRuntime::PoshRuntime(cxx::optional name, const bool do m_ipcChannelInterface.getSegmentManagerAddressOffset()) , m_applicationPort(getMiddlewareApplication()) { + if (cxx::isCompiledOn32BitSystem()) + { + LogWarn() << "Running applications on 32-bit architectures is not supported! Use at your own risk!"; + } /// @todo here we could get the LogLevel and LogMode and set it on the LogManager } @@ -166,7 +172,8 @@ PublisherPortUserType::MemberType_t* PoshRuntime::getMiddlewarePublisher(const c IpcMessage sendBuffer; sendBuffer << IpcMessageTypeToString(IpcMessageType::CREATE_PUBLISHER) << m_appName << static_cast(service).toString() << std::to_string(options.historyCapacity) - << options.nodeName << static_cast(portConfigInfo).toString(); + << options.nodeName << std::to_string(options.offerOnCreate) + << static_cast(portConfigInfo).toString(); auto maybePublisher = requestPublisherFromRoudi(sendBuffer); if (maybePublisher.has_error()) @@ -255,6 +262,12 @@ PoshRuntime::getMiddlewareSubscriber(const capro::ServiceDescription& service, << ", limiting from " << subscriberOptions.queueCapacity << " to " << MAX_QUEUE_CAPACITY; options.queueCapacity = MAX_QUEUE_CAPACITY; } + else if (0U == options.queueCapacity) + { + LogWarn() << "Requested queue capacity of 0 doesn't make sense as no data would be received," + << " the capacity is set to 1"; + options.queueCapacity = 1U; + } if (options.nodeName.empty()) { @@ -264,7 +277,7 @@ PoshRuntime::getMiddlewareSubscriber(const capro::ServiceDescription& service, IpcMessage sendBuffer; sendBuffer << IpcMessageTypeToString(IpcMessageType::CREATE_SUBSCRIBER) << m_appName << static_cast(service).toString() << std::to_string(options.historyRequest) - << std::to_string(options.queueCapacity) << options.nodeName + << std::to_string(options.queueCapacity) << options.nodeName << std::to_string(options.subscribeOnCreate) << static_cast(portConfigInfo).toString(); auto maybeSubscriber = requestSubscriberFromRoudi(sendBuffer); @@ -389,12 +402,11 @@ NodeData* PoshRuntime::createNode(const NodeProperty& nodeProperty) noexcept } LogError() << "Got wrong response from RouDi while creating node:'" << receiveBuffer.getMessage() << "'"; - errorHandler( - Error::kPOSH__RUNTIME_ROUDI_CREATE_NODE_WRONG_IPC_MESSAGE_RESPONSE, nullptr, iox::ErrorLevel::SEVERE); + errorHandler(Error::kPOSH__RUNTIME_ROUDI_CREATE_NODE_WRONG_IPC_MESSAGE_RESPONSE, nullptr, iox::ErrorLevel::SEVERE); return nullptr; } -cxx::expected +cxx::expected PoshRuntime::findService(const capro::ServiceDescription& serviceDescription) noexcept { IpcMessage sendBuffer; @@ -407,7 +419,7 @@ PoshRuntime::findService(const capro::ServiceDescription& serviceDescription) no { LogError() << "Could not send FIND_SERVICE request to RouDi\n"; errorHandler(Error::kIPC_INTERFACE__REG_UNABLE_TO_WRITE_TO_ROUDI_CHANNEL, nullptr, ErrorLevel::MODERATE); - return cxx::error(Error::kIPC_INTERFACE__REG_UNABLE_TO_WRITE_TO_ROUDI_CHANNEL); + return cxx::error(FindServiceError::UNABLE_TO_WRITE_TO_ROUDI_CHANNEL); } InstanceContainer instanceContainer; @@ -427,7 +439,7 @@ PoshRuntime::findService(const capro::ServiceDescription& serviceDescription) no LogWarn() << numberOfElements << " instances found for service \"" << serviceDescription.getServiceIDString() << "\" which is more than supported number of instances(" << MAX_NUMBER_OF_INSTANCES << "\n"; errorHandler(Error::kPOSH__SERVICE_DISCOVERY_INSTANCE_CONTAINER_OVERFLOW, nullptr, ErrorLevel::MODERATE); - return cxx::error(Error::kPOSH__SERVICE_DISCOVERY_INSTANCE_CONTAINER_OVERFLOW); + return cxx::error(FindServiceError::INSTANCE_CONTAINER_OVERFLOW); } return {cxx::success(instanceContainer)}; } @@ -519,6 +531,43 @@ 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) + { + RelativePointer::id_t segmentId{0U}; + cxx::convert::fromString(receiveBuffer.getElementAtIndex(2U).c_str(), segmentId); + RelativePointer::offset_t offset{0U}; + cxx::convert::fromString(receiveBuffer.getElementAtIndex(1U).c_str(), offset); + auto ptr = RelativePointer::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; @@ -551,6 +600,35 @@ 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/test/integrationtests/test_popo_chunk_building_blocks.cpp b/iceoryx_posh/test/integrationtests/test_popo_chunk_building_blocks.cpp index 7ecf2f87717..82cc8245668 100644 --- a/iceoryx_posh/test/integrationtests/test_popo_chunk_building_blocks.cpp +++ b/iceoryx_posh/test/integrationtests/test_popo_chunk_building_blocks.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. @@ -159,32 +160,34 @@ class ChunkBuildingBlocks_IntegrationTest : public Test while (!finished) { m_chunkReceiver.tryGet() - .and_then([&](iox::cxx::optional& maybeChunkHeader) { - if (maybeChunkHeader.has_value()) - { - auto chunkHeader = maybeChunkHeader.value(); - auto dummySample = *reinterpret_cast(chunkHeader->payload()); - // Check if monotonically increasing - EXPECT_THAT(dummySample.m_dummy, Eq(m_receiveCounter)); - m_receiveCounter++; - m_chunkReceiver.release(chunkHeader); - newChunkReceivedInLastIteration = true; - } - else if (!m_forwarderRun.load(std::memory_order_relaxed)) + .and_then([&](auto& chunkHeader) { + auto dummySample = *reinterpret_cast(chunkHeader->payload()); + // Check if monotonically increasing + EXPECT_THAT(dummySample.m_dummy, Eq(m_receiveCounter)); + m_receiveCounter++; + m_chunkReceiver.release(chunkHeader); + newChunkReceivedInLastIteration = true; + }) + .or_else([&](auto& result) { + if (result == ChunkReceiveResult::NO_CHUNK_AVAILABLE) { - if (newChunkReceivedInLastIteration) - { - newChunkReceivedInLastIteration = false; - } - else + if (!m_forwarderRun.load(std::memory_order_relaxed)) { - finished = true; + if (newChunkReceivedInLastIteration) + { + newChunkReceivedInLastIteration = false; + } + else + { + finished = true; + } } } - }) - .or_else([](ChunkReceiveResult) { - // Errors shall never occur - FAIL(); + else + { + // Errors shall never occur + FAIL(); + } }); } } @@ -242,4 +245,4 @@ TEST_F(ChunkBuildingBlocks_IntegrationTest, TwoHopsThreeThreadsNoSoFi) ASSERT_FALSE(m_popper.hasOverflown()); ASSERT_FALSE(m_chunkReceiver.hasOverflown()); EXPECT_EQ(m_sendCounter, m_receiveCounter); -} +} \ No newline at end of file diff --git a/iceoryx_posh/test/integrationtests/test_popo_port_user_building_blocks.cpp b/iceoryx_posh/test/integrationtests/test_popo_port_user_building_blocks.cpp index ed49e64894a..6931dccf28e 100644 --- a/iceoryx_posh/test/integrationtests/test_popo_port_user_building_blocks.cpp +++ b/iceoryx_posh/test/integrationtests/test_popo_port_user_building_blocks.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. @@ -205,14 +206,12 @@ class PortUser_IntegrationTest : public Test { // Try to receive chunk subscriberPortUser.tryGetChunk() - .and_then([&](optional& maybeChunkHeader) { - if (maybeChunkHeader.has_value()) - { - auto chunkHeader = maybeChunkHeader.value(); - m_receiveCounter++; - subscriberPortUser.releaseChunk(chunkHeader); - } - else + .and_then([&](auto& chunkHeader) { + m_receiveCounter++; + subscriberPortUser.releaseChunk(chunkHeader); + }) + .or_else([&](auto& result) { + if (result == ChunkReceiveResult::NO_CHUNK_AVAILABLE) { // Nothing received -> check if publisher(s) still running if (m_publisherRunFinished.load(std::memory_order_relaxed) == numberOfPublishers) @@ -220,10 +219,11 @@ class PortUser_IntegrationTest : public Test finished = true; } } - }) - .or_else([](auto error) { - // Errors shall never occur - FAIL() << "Error in tryGetChunk(): " << static_cast(error); + else + { + // Errors shall never occur + FAIL() << "Error in tryGetChunk(): " << static_cast(result); + } }); } } diff --git a/iceoryx_posh/test/mocks/subscriber_mock.hpp b/iceoryx_posh/test/mocks/subscriber_mock.hpp index d80d7d27a3f..2520d768632 100644 --- a/iceoryx_posh/test/mocks/subscriber_mock.hpp +++ b/iceoryx_posh/test/mocks/subscriber_mock.hpp @@ -50,14 +50,13 @@ class MockSubscriberPortUser MOCK_METHOD0(subscribe, void()); MOCK_METHOD0(unsubscribe, void()); MOCK_CONST_METHOD0(getSubscriptionState, iox::SubscribeState()); - MOCK_METHOD0( - tryGetChunk, - iox::cxx::expected, iox::popo::ChunkReceiveResult>()); + MOCK_METHOD0(tryGetChunk, iox::cxx::expected()); MOCK_METHOD1(releaseChunk, void(iox::mepoo::ChunkHeader*)); 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_METHOD0(isConditionVariableSet, bool()); MOCK_METHOD0(unsetConditionVariable, bool()); MOCK_METHOD0(destroy, bool()); diff --git a/iceoryx_posh/test/moduletests/test_mepoo_handler.cpp b/iceoryx_posh/test/moduletests/test_mepoo_memory_manager.cpp similarity index 100% rename from iceoryx_posh/test/moduletests/test_mepoo_handler.cpp rename to iceoryx_posh/test/moduletests/test_mepoo_memory_manager.cpp diff --git a/iceoryx_posh/test/moduletests/test_popo_active_call_set.cpp b/iceoryx_posh/test/moduletests/test_popo_active_call_set.cpp new file mode 100644 index 00000000000..0f136477859 --- /dev/null +++ b/iceoryx_posh/test/moduletests/test_popo_active_call_set.cpp @@ -0,0 +1,986 @@ +// 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/iceoryx_posh_types.hpp" +#include "iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp" +#include "iceoryx_posh/popo/active_call_set.hpp" +#include "iceoryx_posh/popo/user_trigger.hpp" +#include "iceoryx_utils/cxx/optional.hpp" +#include "iceoryx_utils/cxx/vector.hpp" +#include "iceoryx_utils/internal/concurrent/smart_lock.hpp" +#include "iceoryx_utils/posix_wrapper/semaphore.hpp" +#include "mocks/wait_set_mock.hpp" +#include "test.hpp" +#include "testutils/timing_test.hpp" +#include "testutils/watch_dog.hpp" + +#include +#include +#include + +using namespace ::testing; +using ::testing::Return; +using namespace iox::popo; +using namespace iox::cxx; +using namespace iox::units::duration_literals; + +namespace +{ +enum SimpleEvent +{ + StoepselBachelorParty, + Hypnotoad +}; + +uint64_t g_invalidateTriggerId = 0U; +class SimpleEventClass +{ + public: + SimpleEventClass() = default; + SimpleEventClass(const SimpleEventClass&) = delete; + SimpleEventClass(SimpleEventClass&&) = delete; + + SimpleEventClass& operator=(const SimpleEventClass&) = delete; + SimpleEventClass& operator=(SimpleEventClass&&) = delete; + + ~SimpleEventClass() + { + m_handleStoepsel.reset(); + m_handleHypnotoad.reset(); + } + + void enableEvent(iox::popo::TriggerHandle&& handle, const SimpleEvent event) noexcept + { + switch (event) + { + case SimpleEvent::StoepselBachelorParty: + m_handleStoepsel = std::move(handle); + break; + case SimpleEvent::Hypnotoad: + m_handleHypnotoad = std::move(handle); + break; + } + } + + void enableEvent(iox::popo::TriggerHandle&& handle) noexcept + { + m_handleNoEventEnum = std::move(handle); + } + + void invalidateTrigger(const uint64_t id) noexcept + { + g_invalidateTriggerId = id; + if (m_handleHypnotoad.getUniqueId() == id) + { + m_handleHypnotoad.invalidate(); + } + else if (m_handleStoepsel.getUniqueId() == id) + { + m_handleStoepsel.invalidate(); + } + else if (m_handleNoEventEnum.getUniqueId() == id) + { + m_handleNoEventEnum.invalidate(); + } + } + + void disableEvent(const SimpleEvent event) noexcept + { + switch (event) + { + case SimpleEvent::StoepselBachelorParty: + m_handleStoepsel.reset(); + break; + case SimpleEvent::Hypnotoad: + m_handleHypnotoad.reset(); + break; + } + } + + void disableEvent() noexcept + { + m_handleNoEventEnum.reset(); + } + void triggerStoepsel() noexcept + { + m_handleStoepsel.trigger(); + } + + iox::popo::TriggerHandle m_handleHypnotoad; + iox::popo::TriggerHandle m_handleStoepsel; + iox::popo::TriggerHandle m_handleNoEventEnum; +}; + +class TestActiveCallSet : public ActiveCallSet +{ + public: + TestActiveCallSet(EventVariableData* data) noexcept + : ActiveCallSet(data) + { + } +}; + +struct EventAndSutPair_t +{ + SimpleEventClass* object; + TestActiveCallSet* sut; +}; + +struct TriggerSourceAndCount +{ + std::atomic m_source{nullptr}; + std::atomic m_count{0U}; +}; + +iox::concurrent::smart_lock> g_toBeAttached; +iox::concurrent::smart_lock> g_toBeDetached; +std::array g_triggerCallbackArg; +uint64_t g_triggerCallbackRuntimeInMs = 0U; +iox::cxx::optional g_callbackBlocker; + +class ActiveCallSet_test : public Test +{ + public: + template + static void triggerCallback(SimpleEventClass* const event) noexcept + { + g_triggerCallbackArg[N].m_source = event; + ++g_triggerCallbackArg[N].m_count; + + if (g_callbackBlocker) + { + g_callbackBlocker->wait(); + } + std::this_thread::sleep_for(std::chrono::milliseconds(g_triggerCallbackRuntimeInMs)); + } + + static void attachCallback(SimpleEventClass* const) noexcept + { + for (auto& e : g_toBeAttached.GetCopy()) + { + e.sut->attachEvent(*e.object, SimpleEvent::StoepselBachelorParty, triggerCallback<0U>); + } + } + + static void detachCallback(SimpleEventClass* const) noexcept + { + for (auto& e : g_toBeDetached.GetCopy()) + { + e.sut->detachEvent(*e.object, SimpleEvent::StoepselBachelorParty); + } + } + + static void notifyAndThenDetachStoepselCallback(SimpleEventClass* const) noexcept + { + for (auto& e : g_toBeDetached.GetCopy()) + { + e.object->triggerStoepsel(); + e.sut->detachEvent(*e.object, SimpleEvent::StoepselBachelorParty); + } + } + + + void SetUp() + { + g_callbackBlocker.reset(); + for (auto& e : g_triggerCallbackArg) + { + e.m_source = nullptr; + e.m_count = 0U; + } + m_sut.emplace(&m_eventVarData); + g_invalidateTriggerId = 0U; + g_triggerCallbackRuntimeInMs = 0U; + g_toBeAttached->clear(); + g_toBeDetached->clear(); + }; + + void activateTriggerCallbackBlocker() noexcept + { + g_callbackBlocker.emplace( + iox::posix::Semaphore::create(iox::posix::CreateUnnamedSingleProcessSemaphore, 0U).value()); + } + + void unblockTriggerCallback(const uint64_t numberOfUnblocks) noexcept + { + for (uint64_t i = 0U; i < numberOfUnblocks; ++i) + { + ASSERT_TRUE(static_cast(g_callbackBlocker)); + g_callbackBlocker->post(); + } + } + + void fillUpWithSimpleEvents() + { + for (uint64_t i = 0U; i < m_sut->capacity(); ++i) + { + EXPECT_FALSE(m_sut->attachEvent(m_simpleEvents[i], ActiveCallSet_test::triggerCallback<0U>).has_error()); + EXPECT_THAT(m_sut->size(), Eq(i + 1U)); + } + } + bool fillUpWithSimpleEventsWithEnum(const SimpleEvent eventType) + { + for (uint64_t i = 0U; i < m_sut->capacity(); ++i) + { + bool hasError = + m_sut->attachEvent(m_simpleEvents[i], eventType, ActiveCallSet_test::triggerCallback<0U>).has_error(); + EXPECT_FALSE(hasError); + if (hasError) + { + return false; + } + + EXPECT_THAT(m_sut->size(), Eq(i + 1U)); + } + return true; + } + + + void TearDown(){}; + + + static constexpr uint64_t OVERFLOW_TEST_APPENDIX = 1U; + using eventArray_t = SimpleEventClass[iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET + OVERFLOW_TEST_APPENDIX]; + eventArray_t m_simpleEvents; + EventVariableData m_eventVarData{"Maulbeerblättle"}; + iox::cxx::optional m_sut; + + const iox::units::Duration m_fatalTimeout = 2_s; + Watchdog m_watchdog{m_fatalTimeout}; + static constexpr uint64_t CALLBACK_WAIT_IN_MS = 100U; +}; + +constexpr uint64_t ActiveCallSet_test::CALLBACK_WAIT_IN_MS; +constexpr uint64_t ActiveCallSet_test::OVERFLOW_TEST_APPENDIX; + +template +struct AttachEvent +{ + template + static void doIt(TestActiveCallSet& sut, std::vector& events, const EventType event) + { + EXPECT_THAT(sut.attachEvent(events[N], event, ActiveCallSet_test::triggerCallback).has_error(), Eq(false)); + AttachEvent::doIt(sut, events, event); + } +}; + +template <> +struct AttachEvent<0U> +{ + template + static void doIt(TestActiveCallSet& sut, std::vector& events, const EventType event) + { + EXPECT_THAT(sut.attachEvent(events[0U], event, ActiveCallSet_test::triggerCallback<0U>).has_error(), Eq(false)); + } +}; +} // namespace + + +////////////////////////////////// +// BEGIN attach / detach +////////////////////////////////// +TEST_F(ActiveCallSet_test, CapacityIsEqualToMAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET) +{ + EXPECT_THAT(m_sut->capacity(), Eq(iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET)); +} + +TEST_F(ActiveCallSet_test, IsEmptyWhenConstructed) +{ + EXPECT_THAT(m_sut->size(), Eq(0U)); +} + +TEST_F(ActiveCallSet_test, AttachingWithoutEnumIfEnoughSpaceAvailableWorks) +{ + EXPECT_FALSE(m_sut->attachEvent(m_simpleEvents[0U], ActiveCallSet_test::triggerCallback<0U>).has_error()); + EXPECT_THAT(m_sut->size(), Eq(1U)); +} + +TEST_F(ActiveCallSet_test, AttachWithoutEnumTillCapacityIsFullWorks) +{ + fillUpWithSimpleEvents(); + EXPECT_THAT(m_sut->size(), Eq(m_sut->capacity())); +} + +TEST_F(ActiveCallSet_test, DetachDecreasesSize) +{ + fillUpWithSimpleEvents(); + m_sut->detachEvent(m_simpleEvents[0U]); + EXPECT_THAT(m_sut->size(), Eq(m_sut->capacity() - 1U)); +} + +TEST_F(ActiveCallSet_test, AttachWithoutEnumOneMoreThanCapacityFails) +{ + fillUpWithSimpleEvents(); + auto result = m_sut->attachEvent(m_simpleEvents[m_sut->capacity()], ActiveCallSet_test::triggerCallback<0U>); + + ASSERT_TRUE(result.has_error()); + EXPECT_THAT(result.get_error(), Eq(ActiveCallSetError::ACTIVE_CALL_SET_FULL)); +} + +TEST_F(ActiveCallSet_test, AttachingWithEnumIfEnoughSpaceAvailableWorks) +{ + EXPECT_FALSE(m_sut->attachEvent(m_simpleEvents[0U], SimpleEvent::Hypnotoad, ActiveCallSet_test::triggerCallback<0U>) + .has_error()); + EXPECT_THAT(m_sut->size(), Eq(1U)); +} + +TEST_F(ActiveCallSet_test, AttachWithEnumTillCapacityIsFullWorks) +{ + EXPECT_TRUE(fillUpWithSimpleEventsWithEnum(SimpleEvent::Hypnotoad)); +} + +TEST_F(ActiveCallSet_test, AttachWithEnumOneMoreThanCapacityFails) +{ + fillUpWithSimpleEventsWithEnum(SimpleEvent::Hypnotoad); + auto result = m_sut->attachEvent( + m_simpleEvents[m_sut->capacity()], SimpleEvent::Hypnotoad, ActiveCallSet_test::triggerCallback<0U>); + + ASSERT_TRUE(result.has_error()); + EXPECT_THAT(result.get_error(), Eq(ActiveCallSetError::ACTIVE_CALL_SET_FULL)); +} + +TEST_F(ActiveCallSet_test, DetachMakesSpaceForAnotherAttachWithEventEnum) +{ + fillUpWithSimpleEventsWithEnum(SimpleEvent::Hypnotoad); + + m_sut->detachEvent(m_simpleEvents[0U], SimpleEvent::Hypnotoad); + EXPECT_FALSE(m_sut + ->attachEvent(m_simpleEvents[m_sut->capacity()], + SimpleEvent::Hypnotoad, + ActiveCallSet_test::triggerCallback<0U>) + .has_error()); +} + +TEST_F(ActiveCallSet_test, DetachMakesSpaceForAnotherAttachWithoutEventEnum) +{ + fillUpWithSimpleEvents(); + + m_sut->detachEvent(m_simpleEvents[0U]); + EXPECT_FALSE( + m_sut->attachEvent(m_simpleEvents[m_sut->capacity()], ActiveCallSet_test::triggerCallback<0U>).has_error()); +} + +TEST_F(ActiveCallSet_test, AttachingEventWithoutEventTypeLeadsToAttachedNoEventEnumTriggerHandle) +{ + m_sut->attachEvent(m_simpleEvents[0U], ActiveCallSet_test::triggerCallback<0U>); + EXPECT_TRUE(m_simpleEvents[0U].m_handleNoEventEnum.isValid()); +} + +TEST_F(ActiveCallSet_test, AttachingEventWithEventTypeLeadsToAttachedTriggerHandle) +{ + m_sut->attachEvent(m_simpleEvents[0U], SimpleEvent::StoepselBachelorParty, ActiveCallSet_test::triggerCallback<0U>); + EXPECT_TRUE(m_simpleEvents[0U].m_handleStoepsel.isValid()); +} + +TEST_F(ActiveCallSet_test, OverridingAlreadyAttachedEventWithEnumFails) +{ + m_sut->attachEvent(m_simpleEvents[0U], SimpleEvent::StoepselBachelorParty, ActiveCallSet_test::triggerCallback<0U>); + + auto result = m_sut->attachEvent( + m_simpleEvents[0U], SimpleEvent::StoepselBachelorParty, ActiveCallSet_test::triggerCallback<0U>); + ASSERT_TRUE(result.has_error()); + EXPECT_THAT(result.get_error(), Eq(ActiveCallSetError::EVENT_ALREADY_ATTACHED)); +} + +TEST_F(ActiveCallSet_test, OverridingAlreadyAttachedEventWithoutEnumFails) +{ + m_sut->attachEvent(m_simpleEvents[0U], ActiveCallSet_test::triggerCallback<0U>); + + auto result = m_sut->attachEvent(m_simpleEvents[0U], ActiveCallSet_test::triggerCallback<0U>); + ASSERT_TRUE(result.has_error()); + EXPECT_THAT(result.get_error(), Eq(ActiveCallSetError::EVENT_ALREADY_ATTACHED)); +} + +TEST_F(ActiveCallSet_test, AttachingSameClassWithTwoDifferentEventsWorks) +{ + m_sut->attachEvent(m_simpleEvents[0U], SimpleEvent::Hypnotoad, ActiveCallSet_test::triggerCallback<0U>); + + EXPECT_FALSE(m_sut + ->attachEvent(m_simpleEvents[0U], + SimpleEvent::StoepselBachelorParty, + ActiveCallSet_test::triggerCallback<0U>) + .has_error()); +} + +TEST_F(ActiveCallSet_test, DetachingSameClassWithDifferentEventEnumChangesNothing) +{ + m_sut->attachEvent(m_simpleEvents[0U], SimpleEvent::Hypnotoad, ActiveCallSet_test::triggerCallback<0U>); + + m_sut->detachEvent(m_simpleEvents[0U], SimpleEvent::StoepselBachelorParty); + EXPECT_THAT(m_sut->size(), Eq(1U)); +} + +TEST_F(ActiveCallSet_test, DetachingDifferentClassWithSameEventEnumChangesNothing) +{ + m_sut->attachEvent(m_simpleEvents[0U], SimpleEvent::Hypnotoad, ActiveCallSet_test::triggerCallback<0U>); + + m_sut->detachEvent(m_simpleEvents[1U], SimpleEvent::Hypnotoad); + EXPECT_THAT(m_sut->size(), Eq(1U)); +} + +TEST_F(ActiveCallSet_test, AttachingWithoutEnumTillCapacityFilledSetsUpNoEventEnumTriggerHandle) +{ + fillUpWithSimpleEvents(); + + for (uint64_t i = 0U; i < m_sut->capacity(); ++i) + { + EXPECT_TRUE(m_simpleEvents[i].m_handleNoEventEnum.isValid()); + } +} + +TEST_F(ActiveCallSet_test, DTorDetachesAllAttachedEventsWithoutEnum) +{ + fillUpWithSimpleEvents(); + + auto capacity = m_sut->capacity(); + m_sut.reset(); + + for (uint64_t i = 0U; i < capacity; ++i) + { + EXPECT_FALSE(m_simpleEvents[i].m_handleNoEventEnum.isValid()); + } +} + +TEST_F(ActiveCallSet_test, DTorDetachesAllAttachedEventsWithEnum) +{ + fillUpWithSimpleEventsWithEnum(SimpleEvent::Hypnotoad); + + auto capacity = m_sut->capacity(); + m_sut.reset(); + + for (uint64_t i = 0U; i < capacity; ++i) + { + EXPECT_FALSE(m_simpleEvents[i].m_handleHypnotoad.isValid()); + } +} + +TEST_F(ActiveCallSet_test, AttachedEventDTorDetachesItself) +{ + { + SimpleEventClass fuu; + m_sut->attachEvent(fuu, ActiveCallSet_test::triggerCallback<0U>); + } + + EXPECT_THAT(m_sut->size(), Eq(0U)); +} + +TEST_F(ActiveCallSet_test, AttachingSimpleEventWithoutEnumSetsNoEventEnumTriggerHandle) +{ + SimpleEventClass fuu; + m_sut->attachEvent(fuu, ActiveCallSet_test::triggerCallback<0U>); + + EXPECT_TRUE(static_cast(fuu.m_handleNoEventEnum)); +} + +TEST_F(ActiveCallSet_test, DetachingSimpleEventResetsTriggerHandle) +{ + SimpleEventClass fuu; + m_sut->attachEvent(fuu, ActiveCallSet_test::triggerCallback<0U>); + m_sut->detachEvent(fuu); + + EXPECT_FALSE(static_cast(fuu.m_handleNoEventEnum)); +} + +TEST_F(ActiveCallSet_test, AttachingEventWithEnumSetsTriggerHandle) +{ + SimpleEventClass fuu; + m_sut->attachEvent(fuu, SimpleEvent::StoepselBachelorParty, ActiveCallSet_test::triggerCallback<0U>); + + EXPECT_TRUE(static_cast(fuu.m_handleStoepsel)); +} + +TEST_F(ActiveCallSet_test, DetachingEventWithEnumResetsTriggerHandle) +{ + SimpleEventClass fuu; + m_sut->attachEvent(fuu, SimpleEvent::StoepselBachelorParty, ActiveCallSet_test::triggerCallback<0U>); + m_sut->detachEvent(fuu, SimpleEvent::StoepselBachelorParty); + + EXPECT_FALSE(static_cast(fuu.m_handleStoepsel)); +} + +TEST_F(ActiveCallSet_test, DetachingNonAttachedEventResetsNothing) +{ + SimpleEventClass fuu; + m_sut->attachEvent(fuu, SimpleEvent::StoepselBachelorParty, ActiveCallSet_test::triggerCallback<0U>); + m_sut->detachEvent(fuu, SimpleEvent::Hypnotoad); + + EXPECT_TRUE(static_cast(fuu.m_handleStoepsel)); +} + +/////////////////////////////////// +// END +/////////////////////////////////// + +/////////////////////////////////// +// BEGIN calling callbacks +/////////////////////////////////// +TIMING_TEST_F(ActiveCallSet_test, CallbackIsCalledAfterNotify, Repeat(5), [&] { + m_sut.emplace(&m_eventVarData); + SimpleEventClass fuu; + m_sut->attachEvent(fuu, SimpleEvent::StoepselBachelorParty, ActiveCallSet_test::triggerCallback<0U>); + + fuu.triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_source == &fuu); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_count == 1U); +}); + +TIMING_TEST_F(ActiveCallSet_test, CallbackIsCalledOnlyOnceWhenTriggered, Repeat(5), [&] { + m_sut.emplace(&m_eventVarData); + SimpleEventClass fuu1; + SimpleEventClass fuu2; + m_sut->attachEvent(fuu1, SimpleEvent::StoepselBachelorParty, ActiveCallSet_test::triggerCallback<0U>); + m_sut->attachEvent(fuu2, SimpleEvent::StoepselBachelorParty, ActiveCallSet_test::triggerCallback<1U>); + + fuu1.triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + fuu2.triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_source == &fuu1); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_count == 1U); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[1U].m_source == &fuu2); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[1U].m_count == 1U); +}); + +TIMING_TEST_F(ActiveCallSet_test, TriggerWhileInCallbackLeadsToAnotherCallback, Repeat(5), [&] { + m_sut.emplace(&m_eventVarData); + SimpleEventClass fuu; + m_sut->attachEvent(fuu, SimpleEvent::StoepselBachelorParty, ActiveCallSet_test::triggerCallback<0U>); + + constexpr uint64_t NUMBER_OF_TRIGGER_UNBLOCKS = 10U; + + activateTriggerCallbackBlocker(); + fuu.triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + fuu.triggerStoepsel(); + m_watchdog.watchAndActOnFailure([] { std::terminate(); }); + unblockTriggerCallback(NUMBER_OF_TRIGGER_UNBLOCKS); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_source == &fuu); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_count == 2U); +}); + +TIMING_TEST_F(ActiveCallSet_test, TriggerWhileInCallbackLeadsToAnotherCallbackOnce, Repeat(5), [&] { + m_sut.emplace(&m_eventVarData); + SimpleEventClass fuu; + SimpleEventClass bar; + m_sut->attachEvent(fuu, SimpleEvent::StoepselBachelorParty, ActiveCallSet_test::triggerCallback<0U>); + m_sut->attachEvent(bar, SimpleEvent::StoepselBachelorParty, ActiveCallSet_test::triggerCallback<1U>); + + constexpr uint64_t NUMBER_OF_TRIGGER_UNBLOCKS = 10U; + + activateTriggerCallbackBlocker(); + fuu.triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + fuu.triggerStoepsel(); + bar.triggerStoepsel(); + m_watchdog.watchAndActOnFailure([] { std::terminate(); }); + unblockTriggerCallback(NUMBER_OF_TRIGGER_UNBLOCKS); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_source == &fuu); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_count == 2U); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[1U].m_source == &bar); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[1U].m_count == 1U); +}); + +TIMING_TEST_F(ActiveCallSet_test, TriggerMultipleTimesWhileInCallbackLeadsToAnotherCallback, Repeat(5), [&] { + m_sut.emplace(&m_eventVarData); + SimpleEventClass fuu; + m_sut->attachEvent(fuu, SimpleEvent::StoepselBachelorParty, ActiveCallSet_test::triggerCallback<0U>); + + constexpr uint64_t NUMBER_OF_RETRIGGERS = 10U; + + activateTriggerCallbackBlocker(); + fuu.triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + for (uint64_t i = 0U; i < NUMBER_OF_RETRIGGERS; ++i) + { + fuu.triggerStoepsel(); + } + m_watchdog.watchAndActOnFailure([] { std::terminate(); }); + unblockTriggerCallback(NUMBER_OF_RETRIGGERS); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_source == &fuu); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_count == 2U); +}); + +TIMING_TEST_F(ActiveCallSet_test, TriggerMultipleTimesWhileInCallbackLeadsToAnotherCallbackOnce, Repeat(5), [&] { + m_sut.emplace(&m_eventVarData); + SimpleEventClass fuu; + SimpleEventClass bar; + m_sut->attachEvent(fuu, SimpleEvent::StoepselBachelorParty, ActiveCallSet_test::triggerCallback<0U>); + m_sut->attachEvent(bar, SimpleEvent::StoepselBachelorParty, ActiveCallSet_test::triggerCallback<1U>); + + constexpr uint64_t NUMBER_OF_RETRIGGERS = 10U; + + activateTriggerCallbackBlocker(); + fuu.triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + for (uint64_t i = 0U; i < NUMBER_OF_RETRIGGERS; ++i) + { + fuu.triggerStoepsel(); + } + bar.triggerStoepsel(); + m_watchdog.watchAndActOnFailure([] { std::terminate(); }); + unblockTriggerCallback(NUMBER_OF_RETRIGGERS + 1U); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_source == &fuu); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_count == 2U); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[1U].m_source == &bar); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[1U].m_count == 1U); +}); + +TIMING_TEST_F(ActiveCallSet_test, NoTriggerLeadsToNoCallback, Repeat(5), [&] { + m_sut.emplace(&m_eventVarData); + SimpleEventClass fuu; + m_sut->attachEvent(fuu, SimpleEvent::StoepselBachelorParty, ActiveCallSet_test::triggerCallback<0U>); + + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_source == nullptr); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_count == 0U); +}); + +TIMING_TEST_F(ActiveCallSet_test, TriggeringAllEventsCallsAllCallbacks, Repeat(5), [&] { + m_sut.emplace(&m_eventVarData); + std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET); + + AttachEvent::doIt( + *m_sut, events, SimpleEvent::StoepselBachelorParty); + + activateTriggerCallbackBlocker(); + events[0U].triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + for (auto& e : events) + { + e.triggerStoepsel(); + } + + // 10 times more callback runs allowed to allow potential overtriggering + m_watchdog.watchAndActOnFailure([] { std::terminate(); }); + unblockTriggerCallback(10U * iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0].m_source == &events[0U]); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0].m_count == 2U); + for (uint64_t i = 1U; i < iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET; ++i) + { + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[i].m_source == &events[i]); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[i].m_count == 1U); + } +}); + +TIMING_TEST_F(ActiveCallSet_test, TriggeringAllEventsCallsAllCallbacksOnce, Repeat(5), [&] { + m_sut.emplace(&m_eventVarData); + std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET); + + AttachEvent::doIt( + *m_sut, events, SimpleEvent::StoepselBachelorParty); + + activateTriggerCallbackBlocker(); + events[0U].triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + for (auto& e : events) + { + e.triggerStoepsel(); + } + + // 10 times more callback runs allowed to allow potential overtriggering + m_watchdog.watchAndActOnFailure([] { std::terminate(); }); + unblockTriggerCallback(10U * iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + events[0U].triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0].m_source == &events[0U]); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0].m_count == 3U); + for (uint64_t i = 1U; i < iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET; ++i) + { + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[i].m_source == &events[i]); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[i].m_count == 1U); + } +}); +////////////////////////////////// +// END +////////////////////////////////// + +////////////////////////////////// +// BEGIN concurrent attach / detach +////////////////////////////////// +TIMING_TEST_F(ActiveCallSet_test, AttachingWhileCallbackIsRunningWorks, Repeat(5), [&] { + m_sut.emplace(&m_eventVarData); + std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET); + + m_sut->attachEvent(events[0U], SimpleEvent::StoepselBachelorParty, triggerCallback<0U>); + + g_triggerCallbackRuntimeInMs = 3U * CALLBACK_WAIT_IN_MS / 2U; + events[0U].triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + m_sut->attachEvent(events[1U], SimpleEvent::StoepselBachelorParty, triggerCallback<1U>); + events[1U].triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS * 2U)); + + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[1U].m_source == &events[1U]); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[1U].m_count == 1U); +}); + +TIMING_TEST_F(ActiveCallSet_test, AttachingMultipleWhileCallbackIsRunningWorks, Repeat(5), [&] { + m_sut.emplace(&m_eventVarData); + std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET); + + m_sut->attachEvent(events[iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET - 1U], + SimpleEvent::StoepselBachelorParty, + triggerCallback); + + g_triggerCallbackRuntimeInMs = 3U * CALLBACK_WAIT_IN_MS / 2U; + events[iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET - 1U].triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + AttachEvent::doIt( + *m_sut, events, SimpleEvent::StoepselBachelorParty); + + g_triggerCallbackRuntimeInMs = 0U; + for (uint64_t i = 0U; i + 1U < iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET; ++i) + { + events[i].triggerStoepsel(); + } + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS / 2U)); + + for (uint64_t i = 0U; i + 1U < iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET; ++i) + { + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[i].m_source == &events[i]); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[i].m_count == 1U); + } +}); + +TIMING_TEST_F(ActiveCallSet_test, DetachingWhileCallbackIsRunningWorks, Repeat(5), [&] { + m_sut.emplace(&m_eventVarData); + std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET); + + m_sut->attachEvent(events[0U], SimpleEvent::StoepselBachelorParty, triggerCallback<0U>); + + g_triggerCallbackRuntimeInMs = 3U * CALLBACK_WAIT_IN_MS / 2U; + events[0U].triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + g_triggerCallbackArg[0U].m_source = nullptr; + m_sut->detachEvent(events[0U], SimpleEvent::StoepselBachelorParty); + events[0U].triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_count == 1U); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_source == nullptr); +}); + +TIMING_TEST_F(ActiveCallSet_test, DetachingWhileCallbackIsRunningBlocksDetach, Repeat(5), [&] { + m_sut.emplace(&m_eventVarData); + std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET); + m_sut->attachEvent(events[0U], SimpleEvent::StoepselBachelorParty, triggerCallback<0U>); + g_triggerCallbackRuntimeInMs = 3U * CALLBACK_WAIT_IN_MS / 2U; + events[0U].triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS / 4U)); + + auto begin = std::chrono::system_clock::now(); + m_sut->detachEvent(events[0U], SimpleEvent::StoepselBachelorParty); + auto end = std::chrono::system_clock::now(); + + auto elapsed = std::chrono::duration_cast(end - begin); + TIMING_TEST_EXPECT_TRUE(static_cast(elapsed.count()) > CALLBACK_WAIT_IN_MS / 2U); +}); + +TIMING_TEST_F(ActiveCallSet_test, EventDestructorBlocksWhenCallbackIsRunning, Repeat(5), [&] { + m_sut.emplace(&m_eventVarData); + SimpleEventClass* event = new SimpleEventClass(); + m_sut->attachEvent(*event, SimpleEvent::StoepselBachelorParty, triggerCallback<0U>); + g_triggerCallbackRuntimeInMs = 3U * CALLBACK_WAIT_IN_MS / 2U; + event->triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS / 4U)); + + auto begin = std::chrono::system_clock::now(); + delete event; + auto end = std::chrono::system_clock::now(); + + auto elapsed = std::chrono::duration_cast(end - begin); + TIMING_TEST_EXPECT_TRUE(static_cast(elapsed.count()) > CALLBACK_WAIT_IN_MS / 2U); +}); + + +TIMING_TEST_F(ActiveCallSet_test, DetachingMultipleWhileCallbackIsRunningWorks, Repeat(5), [&] { + m_sut.emplace(&m_eventVarData); + std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET); + AttachEvent::doIt( + *m_sut, events, SimpleEvent::StoepselBachelorParty); + + g_triggerCallbackRuntimeInMs = 3U * CALLBACK_WAIT_IN_MS / 2U; + events[0U].triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + g_triggerCallbackRuntimeInMs = 0U; + for (auto& e : events) + { + m_sut->detachEvent(e, SimpleEvent::StoepselBachelorParty); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + for (auto& t : g_triggerCallbackArg) + { + t.m_source = nullptr; + } + for (auto& e : events) + { + e.triggerStoepsel(); + } + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + for (uint64_t i = 0U; i < iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET; ++i) + { + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[i].m_source == nullptr); + } +}); + +TIMING_TEST_F(ActiveCallSet_test, AttachingDetachingRunsIndependentOfCallback, Repeat(5), [&] { + m_sut.emplace(&m_eventVarData); + std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET); + m_sut->attachEvent(events[iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET - 1U], + SimpleEvent::StoepselBachelorParty, + triggerCallback); + g_triggerCallbackRuntimeInMs = 3U * CALLBACK_WAIT_IN_MS / 2U; + events[iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET - 1U].triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS / 2U)); + + AttachEvent::doIt( + *m_sut, events, SimpleEvent::StoepselBachelorParty); + + for (auto& e : events) + { + m_sut->detachEvent(e, SimpleEvent::StoepselBachelorParty); + } + + // EXPECT_* (assert step) is inside of doIt call. We expect that every event can + // be attached + AttachEvent::doIt( + *m_sut, events, SimpleEvent::StoepselBachelorParty); +}); +////////////////////////////////// +// END +////////////////////////////////// + +////////////////////////////////// +// BEGIN attach / detach in callbacks +////////////////////////////////// +TIMING_TEST_F(ActiveCallSet_test, DetachingSelfInCallbackWorks, Repeat(5), [&] { + m_sut.emplace(&m_eventVarData); + g_toBeDetached->clear(); + + std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET); + g_toBeDetached->push_back({&events[0U], &*m_sut}); + m_sut->attachEvent(events[0U], SimpleEvent::StoepselBachelorParty, detachCallback); + + events[0U].triggerStoepsel(); + + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + TIMING_TEST_EXPECT_TRUE(m_sut->size() == 0U); +}); + +TIMING_TEST_F(ActiveCallSet_test, DetachingNonSelfEventInCallbackWorks, Repeat(5), [&] { + m_sut.emplace(&m_eventVarData); + g_toBeDetached->clear(); + + std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET); + g_toBeDetached->push_back({&events[1U], &*m_sut}); + m_sut->attachEvent(events[0U], SimpleEvent::StoepselBachelorParty, detachCallback); + m_sut->attachEvent(events[1U], SimpleEvent::StoepselBachelorParty, triggerCallback<1U>); + + events[0U].triggerStoepsel(); + + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + TIMING_TEST_EXPECT_TRUE(m_sut->size() == 1U); +}); + +TIMING_TEST_F(ActiveCallSet_test, DetachedCallbacksAreNotBeingCalledWhenTriggeredBefore, Repeat(5), [&] { + // idea of the test is that an event which was detached but is technically still attached + // since the detach blocks cannot be retriggered again so that the callback is called again. + // once detach is called either the callback is currently running and detach is blocked or + // the callback is removed and can never be called again. + // + // To test this we attach two events. events[0] detaches events[1] in his callback. + // events[1] is triggered and the callback has a certain runtime so that we make sure that the callback is + // 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); + g_toBeDetached->clear(); + + std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET); + g_toBeDetached->push_back({&events[1U], &*m_sut}); + m_sut->attachEvent(events[0U], SimpleEvent::StoepselBachelorParty, notifyAndThenDetachStoepselCallback); + m_sut->attachEvent(events[1U], SimpleEvent::StoepselBachelorParty, triggerCallback<1U>); + + g_triggerCallbackRuntimeInMs = 3U * CALLBACK_WAIT_IN_MS / 2U; + events[1U].triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS / 2U)); + g_triggerCallbackArg[1U].m_source = nullptr; + g_triggerCallbackRuntimeInMs = 0U; + + events[1U].triggerStoepsel(); + events[0U].triggerStoepsel(); + + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS)); + + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_source == nullptr); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[1U].m_source == nullptr); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_count == 0U); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[1U].m_count == 1U); +}); + +TIMING_TEST_F(ActiveCallSet_test, AttachingInCallbackWorks, Repeat(5), [&] { + m_sut.emplace(&m_eventVarData); + g_toBeAttached->clear(); + + std::vector events(iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET); + g_toBeAttached->push_back({&events[1U], &*m_sut}); + m_sut->attachEvent(events[0U], SimpleEvent::StoepselBachelorParty, attachCallback); + + events[0U].triggerStoepsel(); + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS / 2U)); + events[1U].triggerStoepsel(); + + std::this_thread::sleep_for(std::chrono::milliseconds(CALLBACK_WAIT_IN_MS / 2U)); + + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_source == &events[1U]); + TIMING_TEST_EXPECT_TRUE(g_triggerCallbackArg[0U].m_count == 1U); +}); +////////////////////////////////// +// END +////////////////////////////////// + diff --git a/iceoryx_posh/test/moduletests/test_popo_base_subscriber.cpp b/iceoryx_posh/test/moduletests/test_popo_base_subscriber.cpp index 55f8bc9d5f3..c7b54306898 100644 --- a/iceoryx_posh/test/moduletests/test_popo_base_subscriber.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_base_subscriber.cpp @@ -39,7 +39,6 @@ struct DummyData { uint64_t val = 42; }; -} template class StubbedBaseSubscriber : public iox::popo::BaseSubscriber @@ -122,7 +121,7 @@ TEST_F(BaseSubscriberTest, ReceiveReturnsAllocatedMemoryChunk) { // ===== Setup ===== // EXPECT_CALL(sut.port(), tryGetChunk) - .WillOnce(Return(ByMove(iox::cxx::success>( + .WillOnce(Return(ByMove(iox::cxx::success( const_cast(chunkMock.chunkHeader()))))); // ===== Test ===== // auto result = sut.takeChunk(); @@ -146,20 +145,6 @@ TEST_F(BaseSubscriberTest, ReceiveForwardsErrorsFromUnderlyingPort) // ===== Cleanup ===== // } -TEST_F(BaseSubscriberTest, ReceiveReturnsNoChunkAvailableIfUnderlyingPortReturnsEmptyOptional) -{ - // ===== Setup ===== // - EXPECT_CALL(sut.port(), tryGetChunk) - .WillOnce( - Return(ByMove(iox::cxx::success>(iox::cxx::nullopt)))); - // ===== Test ===== // - auto result = sut.takeChunk(); - // ===== Verify ===== // - ASSERT_EQ(true, result.has_error()); - EXPECT_EQ(iox::popo::ChunkReceiveResult::NO_CHUNK_AVAILABLE, result.get_error()); - // ===== Cleanup ===== // -} - TEST_F(BaseSubscriberTest, ClearReceiveBufferCallForwardedToUnderlyingSubscriberPort) { // ===== Setup ===== // @@ -255,3 +240,5 @@ TEST_F(BaseSubscriberTest, DestroysUnderlyingPortOnDestruction) // ===== Verify ===== // // ===== Cleanup ===== // } + +} // namespace diff --git a/iceoryx_posh/test/moduletests/test_popo_chunk_receiver.cpp b/iceoryx_posh/test/moduletests/test_popo_chunk_receiver.cpp index 411d3223522..dd10fd34800 100644 --- a/iceoryx_posh/test/moduletests/test_popo_chunk_receiver.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_chunk_receiver.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. @@ -80,8 +81,8 @@ class ChunkReceiver_test : public Test TEST_F(ChunkReceiver_test, getNoChunkFromEmptyQueue) { auto maybeChunkHeader = m_chunkReceiver.tryGet(); - EXPECT_FALSE(maybeChunkHeader.has_error()); - EXPECT_FALSE((*maybeChunkHeader).has_value()); + ASSERT_TRUE(maybeChunkHeader.has_error()); + EXPECT_EQ(maybeChunkHeader.get_error(), iox::popo::ChunkReceiveResult::NO_CHUNK_AVAILABLE); } TEST_F(ChunkReceiver_test, getAndReleaseOneChunk) @@ -94,11 +95,10 @@ TEST_F(ChunkReceiver_test, getAndReleaseOneChunk) m_chunkQueuePusher.push(sharedChunk); auto maybeChunkHeader = m_chunkReceiver.tryGet(); - EXPECT_FALSE(maybeChunkHeader.has_error()); - EXPECT_TRUE((*maybeChunkHeader).has_value()); + ASSERT_FALSE(maybeChunkHeader.has_error()); - EXPECT_TRUE(sharedChunk.getPayload() == (**maybeChunkHeader)->payload()); - m_chunkReceiver.release(**maybeChunkHeader); + EXPECT_TRUE(sharedChunk.getPayload() == (*maybeChunkHeader)->payload()); + m_chunkReceiver.release(*maybeChunkHeader); } EXPECT_THAT(m_memoryManager.getMemPoolInfo(0).m_usedChunks, Eq(0u)); @@ -119,9 +119,8 @@ TEST_F(ChunkReceiver_test, getAndReleaseMultipleChunks) m_chunkQueuePusher.push(sharedChunk); auto maybeChunkHeader = m_chunkReceiver.tryGet(); - EXPECT_FALSE(maybeChunkHeader.has_error()); - EXPECT_TRUE((*maybeChunkHeader).has_value()); - chunks.push_back(**maybeChunkHeader); + ASSERT_FALSE(maybeChunkHeader.has_error()); + chunks.push_back(*maybeChunkHeader); } EXPECT_THAT(m_memoryManager.getMemPoolInfo(0).m_usedChunks, Eq(iox::MAX_CHUNKS_HELD_PER_SUBSCRIBER_SIMULTANEOUSLY)); @@ -150,8 +149,7 @@ TEST_F(ChunkReceiver_test, getTooMuchWithoutRelease) m_chunkQueuePusher.push(sharedChunk); auto maybeChunkHeader = m_chunkReceiver.tryGet(); - EXPECT_FALSE(maybeChunkHeader.has_error()); - EXPECT_TRUE((*maybeChunkHeader).has_value()); + ASSERT_FALSE(maybeChunkHeader.has_error()); } // but now it breaks @@ -161,7 +159,7 @@ TEST_F(ChunkReceiver_test, getTooMuchWithoutRelease) m_chunkQueuePusher.push(sharedChunk); auto maybeChunkHeader = m_chunkReceiver.tryGet(); - EXPECT_TRUE(maybeChunkHeader.has_error()); + ASSERT_TRUE(maybeChunkHeader.has_error()); EXPECT_THAT(maybeChunkHeader.get_error(), Eq(iox::popo::ChunkReceiveResult::TOO_MANY_CHUNKS_HELD_IN_PARALLEL)); } @@ -175,10 +173,8 @@ TEST_F(ChunkReceiver_test, releaseInvalidChunk) m_chunkQueuePusher.push(sharedChunk); auto maybeChunkHeader = m_chunkReceiver.tryGet(); - EXPECT_FALSE(maybeChunkHeader.has_error()); - EXPECT_TRUE((*maybeChunkHeader).has_value()); - - EXPECT_TRUE(sharedChunk.getPayload() == (**maybeChunkHeader)->payload()); + ASSERT_FALSE(maybeChunkHeader.has_error()); + EXPECT_TRUE(sharedChunk.getPayload() == (*maybeChunkHeader)->payload()); } auto errorHandlerCalled{false}; @@ -206,8 +202,7 @@ TEST_F(ChunkReceiver_test, Cleanup) if (i < iox::MAX_CHUNKS_HELD_PER_SUBSCRIBER_SIMULTANEOUSLY) { auto maybeChunkHeader = m_chunkReceiver.tryGet(); - EXPECT_FALSE(maybeChunkHeader.has_error()); - EXPECT_TRUE((*maybeChunkHeader).has_value()); + ASSERT_FALSE(maybeChunkHeader.has_error()); } } diff --git a/iceoryx_posh/test/moduletests/test_popo_event_variable.cpp b/iceoryx_posh/test/moduletests/test_popo_event_variable.cpp new file mode 100644 index 00000000000..5cdf692d5f2 --- /dev/null +++ b/iceoryx_posh/test/moduletests/test_popo_event_variable.cpp @@ -0,0 +1,267 @@ +// 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_ACTIVE_CALL_SET - 1U; + EventNotifier sut(m_eventVarData, EVENT_INDEX); + sut.notify(); + for (Type_t i = 0U; i < iox::MAX_NUMBER_OF_EVENTS_PER_ACTIVE_CALL_SET; 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_ACTIVE_CALL_SET; + 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_ACTIVE_CALL_SET - 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_ACTIVE_CALL_SET - 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_ACTIVE_CALL_SET - 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_ACTIVE_CALL_SET - 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_ACTIVE_CALL_SET - 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_publisher_port.cpp b/iceoryx_posh/test/moduletests/test_popo_publisher_port.cpp index 2b309be7e59..5434dfa8283 100644 --- a/iceoryx_posh/test/moduletests/test_popo_publisher_port.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_publisher_port.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. @@ -72,53 +73,77 @@ 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_publisherOptions; + iox::popo::PublisherOptions m_noOfferOnCreatePublisherOptions{0U, "", false}; + ; - // publisher port w/o history + // publisher port w/o offer on create iox::popo::PublisherPortData m_publisherPortData{ - iox::capro::ServiceDescription("a", "b", "c"), "myApp", &m_memoryManager, m_publisherOptions}; - iox::popo::PublisherPortRouDi m_sutRouDiSide{&m_publisherPortData}; - iox::popo::PublisherPortUser m_sutUserSide{&m_publisherPortData}; + iox::capro::ServiceDescription("a", "b", "c"), "myApp", &m_memoryManager, m_noOfferOnCreatePublisherOptions}; + iox::popo::PublisherPortRouDi m_sutNoOfferOnCreateRouDiSide{&m_publisherPortData}; + iox::popo::PublisherPortUser m_sutNoOfferOnCreateUserSide{&m_publisherPortData}; // publisher port w/ history - iox::popo::PublisherOptions m_publisherPortOptions{iox::MAX_PUBLISHER_HISTORY}; + iox::popo::PublisherOptions m_withHistoryPublisherOptions{iox::MAX_PUBLISHER_HISTORY, "", true}; iox::popo::PublisherPortData m_publisherPortDataHistory{ - iox::capro::ServiceDescription("x", "y", "z"), "myApp", &m_memoryManager, m_publisherPortOptions}; - iox::popo::PublisherPortUser m_sutWithHistoryUseriSide{&m_publisherPortDataHistory}; + iox::capro::ServiceDescription("x", "y", "z"), "myApp", &m_memoryManager, m_withHistoryPublisherOptions}; + iox::popo::PublisherPortUser m_sutWithHistoryUserSide{&m_publisherPortDataHistory}; iox::popo::PublisherPortRouDi m_sutWithHistoryRouDiSide{&m_publisherPortDataHistory}; + + // publisher port w/ default options + iox::popo::PublisherOptions m_withDefaultPublisherOptions{}; + iox::popo::PublisherPortData m_publisherPortDataDefault{ + iox::capro::ServiceDescription("x", "y", "z"), "myApp", &m_memoryManager, m_withDefaultPublisherOptions}; + iox::popo::PublisherPortUser m_sutWithDefaultOptionsUseriSide{&m_publisherPortDataDefault}; + iox::popo::PublisherPortRouDi m_sutWithDefaultOptionsRouDiSide{&m_publisherPortDataDefault}; }; -TEST_F(PublisherPort_test, initialStateIsNotOffered) +TEST_F(PublisherPort_test, initialStateIsOfferedWithDefaultOptions) +{ + EXPECT_TRUE(m_sutWithDefaultOptionsUseriSide.isOffered()); +} + +TEST_F(PublisherPort_test, initialStateIsNotOfferedWhenNoOfferOnCreate) { - EXPECT_FALSE(m_sutUserSide.isOffered()); + EXPECT_FALSE(m_sutNoOfferOnCreateUserSide.isOffered()); } + TEST_F(PublisherPort_test, initialStateIsNoSubscribers) { - EXPECT_FALSE(m_sutUserSide.hasSubscribers()); + EXPECT_FALSE(m_sutNoOfferOnCreateUserSide.hasSubscribers()); } -TEST_F(PublisherPort_test, initialStateReturnsNoCaProMessage) +TEST_F(PublisherPort_test, initialStateReturnsOfferCaProMessageWithDefaultOptions) { - auto maybeCaproMessage = m_sutRouDiSide.tryGetCaProMessage(); + auto maybeCaproMessage = m_sutWithDefaultOptionsRouDiSide.tryGetCaProMessage(); + + ASSERT_TRUE(maybeCaproMessage.has_value()); + auto caproMessage = maybeCaproMessage.value(); + EXPECT_THAT(caproMessage.m_type, Eq(iox::capro::CaproMessageType::OFFER)); +} + +TEST_F(PublisherPort_test, initialStateReturnsNoCaProMessageWhenNoOfferOnCreate) +{ + auto maybeCaproMessage = m_sutNoOfferOnCreateRouDiSide.tryGetCaProMessage(); EXPECT_FALSE(maybeCaproMessage.has_value()); } + TEST_F(PublisherPort_test, offerCallResultsInOfferedState) { - m_sutUserSide.offer(); + m_sutNoOfferOnCreateUserSide.offer(); - EXPECT_TRUE(m_sutUserSide.isOffered()); + EXPECT_TRUE(m_sutNoOfferOnCreateUserSide.isOffered()); } TEST_F(PublisherPort_test, offerCallResultsInOfferCaProMessage) { - m_sutUserSide.offer(); + m_sutNoOfferOnCreateUserSide.offer(); - auto maybeCaproMessage = m_sutRouDiSide.tryGetCaProMessage(); + auto maybeCaproMessage = m_sutNoOfferOnCreateRouDiSide.tryGetCaProMessage(); - EXPECT_TRUE(maybeCaproMessage.has_value()); + ASSERT_TRUE(maybeCaproMessage.has_value()); auto caproMessage = maybeCaproMessage.value(); EXPECT_THAT(caproMessage.m_type, Eq(iox::capro::CaproMessageType::OFFER)); EXPECT_THAT(caproMessage.m_serviceDescription, Eq(iox::capro::ServiceDescription("a", "b", "c"))); @@ -128,24 +153,24 @@ TEST_F(PublisherPort_test, offerCallResultsInOfferCaProMessage) TEST_F(PublisherPort_test, stopOfferCallResultsInNotOfferedState) { - m_sutUserSide.offer(); + m_sutNoOfferOnCreateUserSide.offer(); - m_sutUserSide.stopOffer(); + m_sutNoOfferOnCreateUserSide.stopOffer(); - EXPECT_FALSE(m_sutUserSide.isOffered()); + EXPECT_FALSE(m_sutNoOfferOnCreateUserSide.isOffered()); } TEST_F(PublisherPort_test, stopOfferCallResultsInStopOfferCaProMessage) { // arrange, we need a transition from offer to stop offer, also form a RouDi point of view // therefore we must also get the offer CapPro message (but ignore it here) - m_sutUserSide.offer(); - m_sutRouDiSide.tryGetCaProMessage(); - m_sutUserSide.stopOffer(); + m_sutNoOfferOnCreateUserSide.offer(); + m_sutNoOfferOnCreateRouDiSide.tryGetCaProMessage(); + m_sutNoOfferOnCreateUserSide.stopOffer(); - auto maybeCaproMessage = m_sutRouDiSide.tryGetCaProMessage(); + auto maybeCaproMessage = m_sutNoOfferOnCreateRouDiSide.tryGetCaProMessage(); - EXPECT_TRUE(maybeCaproMessage.has_value()); + ASSERT_TRUE(maybeCaproMessage.has_value()); auto caproMessage = maybeCaproMessage.value(); EXPECT_THAT(caproMessage.m_type, Eq(iox::capro::CaproMessageType::STOP_OFFER)); EXPECT_THAT(caproMessage.m_serviceDescription, Eq(iox::capro::ServiceDescription("a", "b", "c"))); @@ -153,10 +178,10 @@ TEST_F(PublisherPort_test, stopOfferCallResultsInStopOfferCaProMessage) TEST_F(PublisherPort_test, offerStateChangesThatEndUpInTheSameStateDoNotReturnACaProMessage) { - m_sutUserSide.offer(); - m_sutUserSide.stopOffer(); + m_sutNoOfferOnCreateUserSide.offer(); + m_sutNoOfferOnCreateUserSide.stopOffer(); - auto maybeCaproMessage = m_sutRouDiSide.tryGetCaProMessage(); + auto maybeCaproMessage = m_sutNoOfferOnCreateRouDiSide.tryGetCaProMessage(); EXPECT_FALSE(maybeCaproMessage.has_value()); } @@ -164,10 +189,10 @@ TEST_F(PublisherPort_test, offerStateChangesThatEndUpInTheSameStateDoNotReturnAC TEST_F(PublisherPort_test, offerCallWhenHavingHistoryResultsInOfferCaProMessageWithSubTypeFieldAndCorrectHistoryCapacity) { - m_sutWithHistoryUseriSide.offer(); + m_sutWithHistoryUserSide.offer(); auto maybeCaproMessage = m_sutWithHistoryRouDiSide.tryGetCaProMessage(); - EXPECT_TRUE(maybeCaproMessage.has_value()); + ASSERT_TRUE(maybeCaproMessage.has_value()); auto caproMessage = maybeCaproMessage.value(); EXPECT_THAT(caproMessage.m_type, Eq(iox::capro::CaproMessageType::OFFER)); EXPECT_THAT(caproMessage.m_serviceDescription, Eq(iox::capro::ServiceDescription("x", "y", "z"))); @@ -177,7 +202,7 @@ TEST_F(PublisherPort_test, TEST_F(PublisherPort_test, allocatingAChunk) { - auto maybeChunkHeader = m_sutUserSide.tryAllocateChunk(10u); + auto maybeChunkHeader = m_sutNoOfferOnCreateUserSide.tryAllocateChunk(10u); EXPECT_FALSE(maybeChunkHeader.has_error()); EXPECT_THAT(m_memoryManager.getMemPoolInfo(0).m_usedChunks, Eq(1u)); @@ -185,10 +210,10 @@ TEST_F(PublisherPort_test, allocatingAChunk) TEST_F(PublisherPort_test, releasingAnAllocatedChunkReleasesTheMemory) { - auto maybeChunkHeader = m_sutUserSide.tryAllocateChunk(10u); + auto maybeChunkHeader = m_sutNoOfferOnCreateUserSide.tryAllocateChunk(10u); auto chunkHeader = maybeChunkHeader.value(); - m_sutUserSide.releaseChunk(chunkHeader); + m_sutNoOfferOnCreateUserSide.releaseChunk(chunkHeader); // this one is not stored in the last chunk, so all chunks must be free again EXPECT_THAT(m_memoryManager.getMemPoolInfo(0).m_usedChunks, Eq(0u)); @@ -196,19 +221,19 @@ TEST_F(PublisherPort_test, releasingAnAllocatedChunkReleasesTheMemory) TEST_F(PublisherPort_test, allocatedChunkContainsPublisherIdAsOriginId) { - auto maybeChunkHeader = m_sutUserSide.tryAllocateChunk(10u); + auto maybeChunkHeader = m_sutNoOfferOnCreateUserSide.tryAllocateChunk(10u); auto chunkHeader = maybeChunkHeader.value(); - EXPECT_THAT(chunkHeader->originId, Eq(m_sutUserSide.getUniqueID())); - m_sutUserSide.releaseChunk(chunkHeader); + EXPECT_THAT(chunkHeader->originId, Eq(m_sutNoOfferOnCreateUserSide.getUniqueID())); + m_sutNoOfferOnCreateUserSide.releaseChunk(chunkHeader); } TEST_F(PublisherPort_test, allocateAndSendAChunkWithoutSubscriberHoldsTheLast) { - auto maybeChunkHeader = m_sutUserSide.tryAllocateChunk(10u); + auto maybeChunkHeader = m_sutNoOfferOnCreateUserSide.tryAllocateChunk(10u); auto chunkHeader = maybeChunkHeader.value(); - m_sutUserSide.sendChunk(chunkHeader); + m_sutNoOfferOnCreateUserSide.sendChunk(chunkHeader); // this one is stored in the last chunk, so this chunk is still in use EXPECT_THAT(m_memoryManager.getMemPoolInfo(0).m_usedChunks, Eq(1u)); @@ -216,15 +241,15 @@ TEST_F(PublisherPort_test, allocateAndSendAChunkWithoutSubscriberHoldsTheLast) TEST_F(PublisherPort_test, allocateAndSendMultipleChunksWithoutSubscriberHoldsOnlyTheLast) { - auto maybeChunkHeader = m_sutUserSide.tryAllocateChunk(10u); + auto maybeChunkHeader = m_sutNoOfferOnCreateUserSide.tryAllocateChunk(10u); auto chunkHeader = maybeChunkHeader.value(); - m_sutUserSide.sendChunk(chunkHeader); - maybeChunkHeader = m_sutUserSide.tryAllocateChunk(10u); + m_sutNoOfferOnCreateUserSide.sendChunk(chunkHeader); + maybeChunkHeader = m_sutNoOfferOnCreateUserSide.tryAllocateChunk(10u); chunkHeader = maybeChunkHeader.value(); - m_sutUserSide.sendChunk(chunkHeader); - maybeChunkHeader = m_sutUserSide.tryAllocateChunk(10u); + m_sutNoOfferOnCreateUserSide.sendChunk(chunkHeader); + maybeChunkHeader = m_sutNoOfferOnCreateUserSide.tryAllocateChunk(10u); chunkHeader = maybeChunkHeader.value(); - m_sutUserSide.sendChunk(chunkHeader); + m_sutNoOfferOnCreateUserSide.sendChunk(chunkHeader); // the last is stored in the last chunk, so one chunk is still in use EXPECT_THAT(m_memoryManager.getMemPoolInfo(0).m_usedChunks, Eq(1u)); @@ -238,73 +263,73 @@ TEST_F(PublisherPort_test, subscribeWhenNotOfferedReturnsNACK) caproMessage.m_chunkQueueData = &m_chunkQueueData; caproMessage.m_historyCapacity = 0u; - auto maybeCaProMessage = m_sutRouDiSide.dispatchCaProMessageAndGetPossibleResponse(caproMessage); + auto maybeCaProMessage = m_sutNoOfferOnCreateRouDiSide.dispatchCaProMessageAndGetPossibleResponse(caproMessage); - EXPECT_TRUE(maybeCaProMessage.has_value()); + ASSERT_TRUE(maybeCaProMessage.has_value()); auto caproMessageResponse = maybeCaProMessage.value(); EXPECT_THAT(caproMessageResponse.m_type, Eq(iox::capro::CaproMessageType::NACK)); } TEST_F(PublisherPort_test, unsubscribeWhenNotSubscribedReturnsNACK) { - m_sutUserSide.offer(); - m_sutRouDiSide.tryGetCaProMessage(); + m_sutNoOfferOnCreateUserSide.offer(); + m_sutNoOfferOnCreateRouDiSide.tryGetCaProMessage(); ChunkQueueData_t m_chunkQueueData{iox::cxx::VariantQueueTypes::SoFi_SingleProducerSingleConsumer}; iox::capro::CaproMessage caproMessage(iox::capro::CaproMessageType::UNSUB, iox::capro::ServiceDescription("a", "b", "c")); caproMessage.m_chunkQueueData = &m_chunkQueueData; caproMessage.m_historyCapacity = 0u; - auto maybeCaproMessage = m_sutRouDiSide.dispatchCaProMessageAndGetPossibleResponse(caproMessage); + auto maybeCaproMessage = m_sutNoOfferOnCreateRouDiSide.dispatchCaProMessageAndGetPossibleResponse(caproMessage); - EXPECT_TRUE(maybeCaproMessage.has_value()); + ASSERT_TRUE(maybeCaproMessage.has_value()); auto caproMessageResponse = maybeCaproMessage.value(); EXPECT_THAT(caproMessageResponse.m_type, Eq(iox::capro::CaproMessageType::NACK)); } TEST_F(PublisherPort_test, subscribeWhenOfferedReturnsACKAndWeHaveSubscribers) { - m_sutUserSide.offer(); - m_sutRouDiSide.tryGetCaProMessage(); + m_sutNoOfferOnCreateUserSide.offer(); + m_sutNoOfferOnCreateRouDiSide.tryGetCaProMessage(); ChunkQueueData_t m_chunkQueueData{iox::cxx::VariantQueueTypes::SoFi_SingleProducerSingleConsumer}; iox::capro::CaproMessage caproMessage(iox::capro::CaproMessageType::SUB, iox::capro::ServiceDescription("a", "b", "c")); caproMessage.m_chunkQueueData = &m_chunkQueueData; caproMessage.m_historyCapacity = 0u; - auto maybeCaProMessage = m_sutRouDiSide.dispatchCaProMessageAndGetPossibleResponse(caproMessage); + auto maybeCaProMessage = m_sutNoOfferOnCreateRouDiSide.dispatchCaProMessageAndGetPossibleResponse(caproMessage); EXPECT_TRUE(maybeCaProMessage.has_value()); auto caproMessageResponse = maybeCaProMessage.value(); EXPECT_THAT(caproMessageResponse.m_type, Eq(iox::capro::CaproMessageType::ACK)); - EXPECT_TRUE(m_sutUserSide.hasSubscribers()); + EXPECT_TRUE(m_sutNoOfferOnCreateUserSide.hasSubscribers()); } TEST_F(PublisherPort_test, unsubscribeWhenSubscribedReturnsACKAndWeHaveNoMoreSubscribers) { - m_sutUserSide.offer(); - m_sutRouDiSide.tryGetCaProMessage(); + m_sutNoOfferOnCreateUserSide.offer(); + m_sutNoOfferOnCreateRouDiSide.tryGetCaProMessage(); ChunkQueueData_t m_chunkQueueData{iox::cxx::VariantQueueTypes::SoFi_SingleProducerSingleConsumer}; iox::capro::CaproMessage caproMessage(iox::capro::CaproMessageType::SUB, iox::capro::ServiceDescription("a", "b", "c")); caproMessage.m_chunkQueueData = &m_chunkQueueData; caproMessage.m_historyCapacity = 0u; - auto maybeCaProMessage = m_sutRouDiSide.dispatchCaProMessageAndGetPossibleResponse(caproMessage); + auto maybeCaProMessage = m_sutNoOfferOnCreateRouDiSide.dispatchCaProMessageAndGetPossibleResponse(caproMessage); // set CaPro message to UNSUB, the other members are reused caproMessage.m_type = iox::capro::CaproMessageType::UNSUB; - maybeCaProMessage = m_sutRouDiSide.dispatchCaProMessageAndGetPossibleResponse(caproMessage); + maybeCaProMessage = m_sutNoOfferOnCreateRouDiSide.dispatchCaProMessageAndGetPossibleResponse(caproMessage); - EXPECT_TRUE(maybeCaProMessage.has_value()); + ASSERT_TRUE(maybeCaProMessage.has_value()); auto caproMessageResponse = maybeCaProMessage.value(); EXPECT_THAT(caproMessageResponse.m_type, Eq(iox::capro::CaproMessageType::ACK)); - EXPECT_FALSE(m_sutUserSide.hasSubscribers()); + EXPECT_FALSE(m_sutNoOfferOnCreateUserSide.hasSubscribers()); } TEST_F(PublisherPort_test, subscribeManyIsFine) { - m_sutUserSide.offer(); - m_sutRouDiSide.tryGetCaProMessage(); + m_sutNoOfferOnCreateUserSide.offer(); + m_sutNoOfferOnCreateRouDiSide.tryGetCaProMessage(); // using dummy pointers for the provided chunk queue data uint64_t dummy; uint64_t* dummyPtr = &dummy; @@ -316,8 +341,8 @@ TEST_F(PublisherPort_test, subscribeManyIsFine) for (size_t i = 0; i < iox::MAX_SUBSCRIBERS_PER_PUBLISHER; i++) { - auto maybeCaProMessage = m_sutRouDiSide.dispatchCaProMessageAndGetPossibleResponse(caproMessage); - EXPECT_TRUE(maybeCaProMessage.has_value()); + auto maybeCaProMessage = m_sutNoOfferOnCreateRouDiSide.dispatchCaProMessageAndGetPossibleResponse(caproMessage); + ASSERT_TRUE(maybeCaProMessage.has_value()); auto caproMessageResponse = maybeCaProMessage.value(); EXPECT_THAT(caproMessageResponse.m_type, Eq(iox::capro::CaproMessageType::ACK)); dummyPtr++; @@ -327,8 +352,8 @@ TEST_F(PublisherPort_test, subscribeManyIsFine) TEST_F(PublisherPort_test, subscribeTillOverflowReturnsNACK) { - m_sutUserSide.offer(); - m_sutRouDiSide.tryGetCaProMessage(); + m_sutNoOfferOnCreateUserSide.offer(); + m_sutNoOfferOnCreateRouDiSide.tryGetCaProMessage(); // using dummy pointers for the provided chunk queue data uint64_t dummy; uint64_t* dummyPtr = &dummy; @@ -339,39 +364,39 @@ TEST_F(PublisherPort_test, subscribeTillOverflowReturnsNACK) caproMessage.m_historyCapacity = 0u; for (size_t i = 0; i < iox::MAX_SUBSCRIBERS_PER_PUBLISHER; i++) { - m_sutRouDiSide.dispatchCaProMessageAndGetPossibleResponse(caproMessage); + m_sutNoOfferOnCreateRouDiSide.dispatchCaProMessageAndGetPossibleResponse(caproMessage); dummyPtr++; caproMessage.m_chunkQueueData = reinterpret_cast(dummyPtr); } - auto maybeCaProMessage = m_sutRouDiSide.dispatchCaProMessageAndGetPossibleResponse(caproMessage); + auto maybeCaProMessage = m_sutNoOfferOnCreateRouDiSide.dispatchCaProMessageAndGetPossibleResponse(caproMessage); - EXPECT_TRUE(maybeCaProMessage.has_value()); + ASSERT_TRUE(maybeCaProMessage.has_value()); auto caproMessageResponse = maybeCaProMessage.value(); EXPECT_THAT(caproMessageResponse.m_type, Eq(iox::capro::CaproMessageType::NACK)); } TEST_F(PublisherPort_test, sendWhenSubscribedDeliversAChunk) { - m_sutUserSide.offer(); - m_sutRouDiSide.tryGetCaProMessage(); + m_sutNoOfferOnCreateUserSide.offer(); + m_sutNoOfferOnCreateRouDiSide.tryGetCaProMessage(); ChunkQueueData_t m_chunkQueueData{iox::cxx::VariantQueueTypes::SoFi_SingleProducerSingleConsumer}; iox::capro::CaproMessage caproMessage(iox::capro::CaproMessageType::SUB, iox::capro::ServiceDescription("a", "b", "c")); caproMessage.m_chunkQueueData = &m_chunkQueueData; caproMessage.m_historyCapacity = 0u; - m_sutRouDiSide.dispatchCaProMessageAndGetPossibleResponse(caproMessage); - auto maybeChunkHeader = m_sutUserSide.tryAllocateChunk(sizeof(DummySample)); + m_sutNoOfferOnCreateRouDiSide.dispatchCaProMessageAndGetPossibleResponse(caproMessage); + auto maybeChunkHeader = m_sutNoOfferOnCreateUserSide.tryAllocateChunk(sizeof(DummySample)); auto chunkHeader = maybeChunkHeader.value(); auto sample = chunkHeader->payload(); new (sample) DummySample(); static_cast(sample)->dummy = 17; - m_sutUserSide.sendChunk(chunkHeader); + m_sutNoOfferOnCreateUserSide.sendChunk(chunkHeader); iox::popo::ChunkQueuePopper m_chunkQueuePopper(&m_chunkQueueData); auto maybeSharedChunk = m_chunkQueuePopper.tryPop(); - EXPECT_TRUE(maybeSharedChunk.has_value()); + ASSERT_TRUE(maybeSharedChunk.has_value()); auto sharedChunk = maybeSharedChunk.value(); auto dummySample = *reinterpret_cast(sharedChunk.getPayload()); EXPECT_THAT(dummySample.dummy, Eq(17U)); @@ -408,7 +433,7 @@ TEST_F(PublisherPort_test, subscribeWithHistoryLikeTheARAField) // 4. We get the history value on subscribe auto maybeSharedChunk = m_chunkQueuePopper.tryPop(); - EXPECT_TRUE(maybeSharedChunk.has_value()); + ASSERT_TRUE(maybeSharedChunk.has_value()); auto sharedChunk = maybeSharedChunk.value(); auto dummySample = *reinterpret_cast(sharedChunk.getPayload()); EXPECT_THAT(dummySample.dummy, Eq(17U)); @@ -416,21 +441,21 @@ TEST_F(PublisherPort_test, subscribeWithHistoryLikeTheARAField) TEST_F(PublisherPort_test, noLastChunkWhenNothingSent) { - auto maybeLastChunkHeader = m_sutUserSide.tryGetPreviousChunk(); + auto maybeLastChunkHeader = m_sutNoOfferOnCreateUserSide.tryGetPreviousChunk(); EXPECT_FALSE(maybeLastChunkHeader.has_value()); } TEST_F(PublisherPort_test, lastChunkAvailableAfterSend) { - auto maybeChunkHeader = m_sutUserSide.tryAllocateChunk(10u); + auto maybeChunkHeader = m_sutNoOfferOnCreateUserSide.tryAllocateChunk(10u); auto chunkHeader = maybeChunkHeader.value(); auto firstPayloadPtr = chunkHeader->payload(); - m_sutUserSide.sendChunk(chunkHeader); + m_sutNoOfferOnCreateUserSide.sendChunk(chunkHeader); - auto maybeLastChunkHeader = m_sutUserSide.tryGetPreviousChunk(); + auto maybeLastChunkHeader = m_sutNoOfferOnCreateUserSide.tryGetPreviousChunk(); - EXPECT_TRUE(maybeLastChunkHeader.has_value()); + ASSERT_TRUE(maybeLastChunkHeader.has_value()); EXPECT_THAT(maybeLastChunkHeader.value()->payload(), Eq(firstPayloadPtr)); } @@ -439,14 +464,14 @@ TEST_F(PublisherPort_test, cleanupReleasesAllChunks) // push some chunks to history for (size_t i = 0; i < iox::MAX_PUBLISHER_HISTORY; i++) { - auto maybeChunkHeader = m_sutWithHistoryUseriSide.tryAllocateChunk(sizeof(DummySample)); + auto maybeChunkHeader = m_sutWithHistoryUserSide.tryAllocateChunk(sizeof(DummySample)); auto chunkHeader = maybeChunkHeader.value(); - m_sutWithHistoryUseriSide.sendChunk(chunkHeader); + m_sutWithHistoryUserSide.sendChunk(chunkHeader); } // allocate some samples - auto maybeChunkHeader1 = m_sutWithHistoryUseriSide.tryAllocateChunk(sizeof(DummySample)); - auto maybeChunkHeader2 = m_sutWithHistoryUseriSide.tryAllocateChunk(sizeof(DummySample)); - auto maybeChunkHeader3 = m_sutWithHistoryUseriSide.tryAllocateChunk(sizeof(DummySample)); + auto maybeChunkHeader1 = m_sutWithHistoryUserSide.tryAllocateChunk(sizeof(DummySample)); + auto maybeChunkHeader2 = m_sutWithHistoryUserSide.tryAllocateChunk(sizeof(DummySample)); + auto maybeChunkHeader3 = m_sutWithHistoryUserSide.tryAllocateChunk(sizeof(DummySample)); m_sutWithHistoryRouDiSide.releaseAllChunks(); diff --git a/iceoryx_posh/test/moduletests/test_popo_subscriber_port.cpp b/iceoryx_posh/test/moduletests/test_popo_subscriber_port.cpp index 26642a5a606..2b47602d331 100644 --- a/iceoryx_posh/test/moduletests/test_popo_subscriber_port.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_subscriber_port.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. @@ -57,57 +58,78 @@ class SubscriberPortSingleProducer_test : public Test iox::cxx::GenericRAII m_uniqueRouDiId{[] { iox::popo::internal::setUniqueRouDiId(0); }, [] { iox::popo::internal::unsetUniqueRouDiId(); }}; + + iox::popo::SubscriberOptions m_noSubscribeOnCreateOptions{ + iox::popo::SubscriberPortData::ChunkQueueData_t::MAX_CAPACITY, 0U, "", false}; iox::popo::SubscriberPortData m_subscriberPortDataSingleProducer{ TEST_SERVICE_DESCRIPTION, "myApp", iox::cxx::VariantQueueTypes::SoFi_SingleProducerSingleConsumer, - iox::popo::SubscriberOptions()}; + m_noSubscribeOnCreateOptions}; iox::popo::SubscriberPortUser m_sutUserSideSingleProducer{&m_subscriberPortDataSingleProducer}; iox::popo::SubscriberPortSingleProducer m_sutRouDiSideSingleProducer{&m_subscriberPortDataSingleProducer}; + + iox::popo::SubscriberOptions m_defaultSubscriberOptions{}; + iox::popo::SubscriberPortData m_subscriberPortDataDefaultOptions{ + TEST_SERVICE_DESCRIPTION, + "myApp", + iox::cxx::VariantQueueTypes::SoFi_SingleProducerSingleConsumer, + m_defaultSubscriberOptions}; + iox::popo::SubscriberPortUser m_sutUserSideDefaultOptions{&m_subscriberPortDataDefaultOptions}; + iox::popo::SubscriberPortSingleProducer m_sutRouDiSideDefaultOptions{&m_subscriberPortDataDefaultOptions}; }; const iox::capro::ServiceDescription SubscriberPortSingleProducer_test::TEST_SERVICE_DESCRIPTION("x", "y", "z"); -TEST_F(SubscriberPortSingleProducer_test, initialStateNotSubscribed) +TEST_F(SubscriberPortSingleProducer_test, InitialStateNotSubscribed) { EXPECT_THAT(m_sutUserSideSingleProducer.getSubscriptionState(), Eq(iox::SubscribeState::NOT_SUBSCRIBED)); } -TEST_F(SubscriberPortSingleProducer_test, initialStateNoChunksAvailable) +TEST_F(SubscriberPortSingleProducer_test, InitialStateNoChunksAvailable) { - auto maybeChunk = m_sutUserSideSingleProducer.tryGetChunk(); + auto maybeChunkHeader = m_sutUserSideSingleProducer.tryGetChunk(); - EXPECT_FALSE(maybeChunk.has_error()); - EXPECT_FALSE(maybeChunk.value().has_value()); + ASSERT_TRUE(maybeChunkHeader.has_error()); + EXPECT_EQ(maybeChunkHeader.get_error(), iox::popo::ChunkReceiveResult::NO_CHUNK_AVAILABLE); EXPECT_FALSE(m_sutUserSideSingleProducer.hasNewChunks()); } -TEST_F(SubscriberPortSingleProducer_test, initialStateNoChunksLost) +TEST_F(SubscriberPortSingleProducer_test, InitialStateNoChunksLost) { EXPECT_FALSE(m_sutUserSideSingleProducer.hasLostChunksSinceLastCall()); } -TEST_F(SubscriberPortSingleProducer_test, initialStateReturnsNoCaProMessage) +TEST_F(SubscriberPortSingleProducer_test, InitialStateReturnsNoCaProMessageWhenNoSubOnCreate) { auto maybeCaproMessage = m_sutRouDiSideSingleProducer.tryGetCaProMessage(); EXPECT_FALSE(maybeCaproMessage.has_value()); } -TEST_F(SubscriberPortSingleProducer_test, subscribeCallResultsInSubCaProMessage) +TEST_F(SubscriberPortSingleProducer_test, InitialStateReturnsSubCaProMessageWithDefaultOptions) +{ + auto maybeCaproMessage = m_sutRouDiSideDefaultOptions.tryGetCaProMessage(); + + ASSERT_TRUE(maybeCaproMessage.has_value()); + auto caproMessage = maybeCaproMessage.value(); + EXPECT_THAT(caproMessage.m_type, Eq(iox::capro::CaproMessageType::SUB)); +} + +TEST_F(SubscriberPortSingleProducer_test, SubscribeCallResultsInSubCaProMessage) { m_sutUserSideSingleProducer.subscribe(); auto maybeCaproMessage = m_sutRouDiSideSingleProducer.tryGetCaProMessage(); - EXPECT_TRUE(maybeCaproMessage.has_value()); + ASSERT_TRUE(maybeCaproMessage.has_value()); auto caproMessage = maybeCaproMessage.value(); EXPECT_THAT(caproMessage.m_type, Eq(iox::capro::CaproMessageType::SUB)); EXPECT_THAT(caproMessage.m_serviceDescription, Eq(SubscriberPortSingleProducer_test::TEST_SERVICE_DESCRIPTION)); EXPECT_THAT(caproMessage.m_historyCapacity, Eq(0u)); } -TEST_F(SubscriberPortSingleProducer_test, subscribeRequestedWhenCallingSubscribe) +TEST_F(SubscriberPortSingleProducer_test, SubscribeRequestedWhenCallingSubscribe) { m_sutUserSideSingleProducer.subscribe(); m_sutRouDiSideSingleProducer.tryGetCaProMessage(); // only RouDi changes state @@ -117,7 +139,7 @@ TEST_F(SubscriberPortSingleProducer_test, subscribeRequestedWhenCallingSubscribe EXPECT_THAT(subscriptionState, Eq(iox::SubscribeState::SUBSCRIBE_REQUESTED)); } -TEST_F(SubscriberPortSingleProducer_test, nackResponseOnSubResultsInWaitForOffer) +TEST_F(SubscriberPortSingleProducer_test, NackResponseOnSubResultsInWaitForOffer) { m_sutUserSideSingleProducer.subscribe(); m_sutRouDiSideSingleProducer.tryGetCaProMessage(); // only RouDi changes state @@ -130,7 +152,7 @@ TEST_F(SubscriberPortSingleProducer_test, nackResponseOnSubResultsInWaitForOffer EXPECT_THAT(subscriptionState, Eq(iox::SubscribeState::WAIT_FOR_OFFER)); } -TEST_F(SubscriberPortSingleProducer_test, ackResponseOnSubResultsInSubscribed) +TEST_F(SubscriberPortSingleProducer_test, AckResponseOnSubResultsInSubscribed) { m_sutUserSideSingleProducer.subscribe(); m_sutRouDiSideSingleProducer.tryGetCaProMessage(); // only RouDi changes state @@ -143,7 +165,7 @@ TEST_F(SubscriberPortSingleProducer_test, ackResponseOnSubResultsInSubscribed) EXPECT_THAT(subscriptionState, Eq(iox::SubscribeState::SUBSCRIBED)); } -TEST_F(SubscriberPortSingleProducer_test, offerInWaitForOfferTriggersSubMessage) +TEST_F(SubscriberPortSingleProducer_test, OfferInWaitForOfferTriggersSubMessage) { m_sutUserSideSingleProducer.subscribe(); m_sutRouDiSideSingleProducer.tryGetCaProMessage(); // only RouDi changes state @@ -154,7 +176,7 @@ TEST_F(SubscriberPortSingleProducer_test, offerInWaitForOfferTriggersSubMessage) auto maybeCaproMessage = m_sutRouDiSideSingleProducer.dispatchCaProMessageAndGetPossibleResponse(caproMessage); - EXPECT_TRUE(maybeCaproMessage.has_value()); + ASSERT_TRUE(maybeCaproMessage.has_value()); auto caproMessageResponse = maybeCaproMessage.value(); EXPECT_THAT(caproMessageResponse.m_type, Eq(iox::capro::CaproMessageType::SUB)); EXPECT_THAT(caproMessageResponse.m_serviceDescription, @@ -162,7 +184,7 @@ TEST_F(SubscriberPortSingleProducer_test, offerInWaitForOfferTriggersSubMessage) EXPECT_THAT(caproMessageResponse.m_historyCapacity, Eq(0u)); } -TEST_F(SubscriberPortSingleProducer_test, offerInWaitForOfferResultsInSubscribeRequested) +TEST_F(SubscriberPortSingleProducer_test, OfferInWaitForOfferResultsInSubscribeRequested) { m_sutUserSideSingleProducer.subscribe(); m_sutRouDiSideSingleProducer.tryGetCaProMessage(); // only RouDi changes state @@ -177,7 +199,7 @@ TEST_F(SubscriberPortSingleProducer_test, offerInWaitForOfferResultsInSubscribeR EXPECT_THAT(subscriptionState, Eq(iox::SubscribeState::SUBSCRIBE_REQUESTED)); } -TEST_F(SubscriberPortSingleProducer_test, unsubscribeInWaitForOfferResultsInNotSubscribed) +TEST_F(SubscriberPortSingleProducer_test, UnsubscribeInWaitForOfferResultsInNotSubscribed) { m_sutUserSideSingleProducer.subscribe(); m_sutRouDiSideSingleProducer.tryGetCaProMessage(); // only RouDi changes state @@ -207,7 +229,7 @@ TEST_F(SubscriberPortSingleProducer_test, StopOfferInSubscribedResultsInWaitForO EXPECT_THAT(subscriptionState, Eq(iox::SubscribeState::WAIT_FOR_OFFER)); } -TEST_F(SubscriberPortSingleProducer_test, unsubscribeInSubscribedTriggersUnsubMessage) +TEST_F(SubscriberPortSingleProducer_test, UnsubscribeInSubscribedTriggersUnsubMessage) { m_sutUserSideSingleProducer.subscribe(); m_sutRouDiSideSingleProducer.tryGetCaProMessage(); // only RouDi changes state @@ -218,14 +240,14 @@ TEST_F(SubscriberPortSingleProducer_test, unsubscribeInSubscribedTriggersUnsubMe auto maybeCaproMessage = m_sutRouDiSideSingleProducer.tryGetCaProMessage(); - EXPECT_TRUE(maybeCaproMessage.has_value()); + ASSERT_TRUE(maybeCaproMessage.has_value()); auto caproMessageResponse = maybeCaproMessage.value(); EXPECT_THAT(caproMessageResponse.m_type, Eq(iox::capro::CaproMessageType::UNSUB)); EXPECT_THAT(caproMessageResponse.m_serviceDescription, Eq(SubscriberPortSingleProducer_test::TEST_SERVICE_DESCRIPTION)); } -TEST_F(SubscriberPortSingleProducer_test, unsubscribeInSubscribedResultsInUnsubscribeRequested) +TEST_F(SubscriberPortSingleProducer_test, UnsubscribeInSubscribedResultsInUnsubscribeRequested) { m_sutUserSideSingleProducer.subscribe(); m_sutRouDiSideSingleProducer.tryGetCaProMessage(); // only RouDi changes state @@ -240,7 +262,7 @@ TEST_F(SubscriberPortSingleProducer_test, unsubscribeInSubscribedResultsInUnsubs EXPECT_THAT(subscriptionState, Eq(iox::SubscribeState::UNSUBSCRIBE_REQUESTED)); } -TEST_F(SubscriberPortSingleProducer_test, ackInUnsubscribeRequestedResultsInNotSubscribed) +TEST_F(SubscriberPortSingleProducer_test, AckInUnsubscribeRequestedResultsInNotSubscribed) { m_sutUserSideSingleProducer.subscribe(); m_sutRouDiSideSingleProducer.tryGetCaProMessage(); // only RouDi changes state @@ -256,7 +278,7 @@ TEST_F(SubscriberPortSingleProducer_test, ackInUnsubscribeRequestedResultsInNotS EXPECT_THAT(subscriptionState, Eq(iox::SubscribeState::NOT_SUBSCRIBED)); } -TEST_F(SubscriberPortSingleProducer_test, nackInUnsubscribeRequestedResultsInNotSubscribed) +TEST_F(SubscriberPortSingleProducer_test, NackInUnsubscribeRequestedResultsInNotSubscribed) { m_sutUserSideSingleProducer.subscribe(); m_sutRouDiSideSingleProducer.tryGetCaProMessage(); // only RouDi changes state @@ -273,7 +295,7 @@ TEST_F(SubscriberPortSingleProducer_test, nackInUnsubscribeRequestedResultsInNot EXPECT_THAT(subscriptionState, Eq(iox::SubscribeState::NOT_SUBSCRIBED)); } -TEST_F(SubscriberPortSingleProducer_test, invalidMessageResultsInError) +TEST_F(SubscriberPortSingleProducer_test, InvalidMessageResultsInError) { auto errorHandlerCalled{false}; auto errorHandlerGuard = iox::ErrorHandler::SetTemporaryErrorHandler( @@ -289,7 +311,7 @@ TEST_F(SubscriberPortSingleProducer_test, invalidMessageResultsInError) EXPECT_TRUE(errorHandlerCalled); } -TEST_F(SubscriberPortSingleProducer_test, ackWhenNotWaitingForResultsInError) +TEST_F(SubscriberPortSingleProducer_test, AckWhenNotWaitingForResultsInError) { auto errorHandlerCalled{false}; auto errorHandlerGuard = iox::ErrorHandler::SetTemporaryErrorHandler( @@ -305,7 +327,7 @@ TEST_F(SubscriberPortSingleProducer_test, ackWhenNotWaitingForResultsInError) EXPECT_TRUE(errorHandlerCalled); } -TEST_F(SubscriberPortSingleProducer_test, nackWhenNotWaitingForResultsInError) +TEST_F(SubscriberPortSingleProducer_test, NackWhenNotWaitingForResultsInError) { auto errorHandlerCalled{false}; iox::Error receivedError; @@ -355,32 +377,34 @@ class SubscriberPortMultiProducer_test : public Test iox::popo::SubscriberPortMultiProducer m_sutRouDiSideMultiProducer{&m_subscriberPortDataMultiProducer}; }; -TEST_F(SubscriberPortMultiProducer_test, initialStateNotSubscribed) +TEST_F(SubscriberPortMultiProducer_test, InitialStateNotSubscribed) { EXPECT_THAT(m_sutUserSideMultiProducer.getSubscriptionState(), Eq(iox::SubscribeState::NOT_SUBSCRIBED)); } -TEST_F(SubscriberPortMultiProducer_test, initialStateReturnsNoCaProMessage) +TEST_F(SubscriberPortMultiProducer_test, InitialStateReturnsSubCaProMessageWithDefaultOptions) { auto maybeCaproMessage = m_sutRouDiSideMultiProducer.tryGetCaProMessage(); - EXPECT_FALSE(maybeCaproMessage.has_value()); + ASSERT_TRUE(maybeCaproMessage.has_value()); + auto caproMessage = maybeCaproMessage.value(); + EXPECT_THAT(caproMessage.m_type, Eq(iox::capro::CaproMessageType::SUB)); } -TEST_F(SubscriberPortMultiProducer_test, subscribeCallResultsInSubCaProMessage) +TEST_F(SubscriberPortMultiProducer_test, SubscribeCallResultsInSubCaProMessage) { m_sutUserSideMultiProducer.subscribe(); auto maybeCaproMessage = m_sutRouDiSideMultiProducer.tryGetCaProMessage(); - EXPECT_TRUE(maybeCaproMessage.has_value()); + ASSERT_TRUE(maybeCaproMessage.has_value()); auto caproMessage = maybeCaproMessage.value(); EXPECT_THAT(caproMessage.m_type, Eq(iox::capro::CaproMessageType::SUB)); EXPECT_THAT(caproMessage.m_serviceDescription, Eq(SubscriberPortSingleProducer_test::TEST_SERVICE_DESCRIPTION)); EXPECT_THAT(caproMessage.m_historyCapacity, Eq(0u)); } -TEST_F(SubscriberPortMultiProducer_test, subscribedWhenCallingSubscribe) +TEST_F(SubscriberPortMultiProducer_test, SubscribedWhenCallingSubscribe) { m_sutUserSideMultiProducer.subscribe(); m_sutRouDiSideMultiProducer.tryGetCaProMessage(); // only RouDi changes state @@ -390,7 +414,7 @@ TEST_F(SubscriberPortMultiProducer_test, subscribedWhenCallingSubscribe) EXPECT_THAT(subscriptionState, Eq(iox::SubscribeState::SUBSCRIBED)); } -TEST_F(SubscriberPortMultiProducer_test, nackResponseOnSubStillSubscribed) +TEST_F(SubscriberPortMultiProducer_test, NackResponseOnSubStillSubscribed) { m_sutUserSideMultiProducer.subscribe(); m_sutRouDiSideMultiProducer.tryGetCaProMessage(); // only RouDi changes state @@ -403,7 +427,7 @@ TEST_F(SubscriberPortMultiProducer_test, nackResponseOnSubStillSubscribed) EXPECT_THAT(subscriptionState, Eq(iox::SubscribeState::SUBSCRIBED)); } -TEST_F(SubscriberPortMultiProducer_test, ackResponseOnSubStillSubscribed) +TEST_F(SubscriberPortMultiProducer_test, AckResponseOnSubStillSubscribed) { m_sutUserSideMultiProducer.subscribe(); m_sutRouDiSideMultiProducer.tryGetCaProMessage(); // only RouDi changes state @@ -416,7 +440,7 @@ TEST_F(SubscriberPortMultiProducer_test, ackResponseOnSubStillSubscribed) EXPECT_THAT(subscriptionState, Eq(iox::SubscribeState::SUBSCRIBED)); } -TEST_F(SubscriberPortMultiProducer_test, offerInSubscribedTriggersSubMessage) +TEST_F(SubscriberPortMultiProducer_test, OfferInSubscribedTriggersSubMessage) { m_sutUserSideMultiProducer.subscribe(); m_sutRouDiSideMultiProducer.tryGetCaProMessage(); // only RouDi changes state @@ -425,7 +449,7 @@ TEST_F(SubscriberPortMultiProducer_test, offerInSubscribedTriggersSubMessage) auto maybeCaproMessage = m_sutRouDiSideMultiProducer.dispatchCaProMessageAndGetPossibleResponse(caproMessage); - EXPECT_TRUE(maybeCaproMessage.has_value()); + ASSERT_TRUE(maybeCaproMessage.has_value()); auto caproMessageResponse = maybeCaproMessage.value(); EXPECT_THAT(caproMessageResponse.m_type, Eq(iox::capro::CaproMessageType::SUB)); EXPECT_THAT(caproMessageResponse.m_serviceDescription, @@ -433,7 +457,7 @@ TEST_F(SubscriberPortMultiProducer_test, offerInSubscribedTriggersSubMessage) EXPECT_THAT(caproMessageResponse.m_historyCapacity, Eq(0u)); } -TEST_F(SubscriberPortMultiProducer_test, unsubscribeInSubscribedResultsInNotSubscribed) +TEST_F(SubscriberPortMultiProducer_test, UnsubscribeInSubscribedResultsInNotSubscribed) { m_sutUserSideMultiProducer.subscribe(); m_sutRouDiSideMultiProducer.tryGetCaProMessage(); // only RouDi changes state @@ -460,7 +484,7 @@ TEST_F(SubscriberPortMultiProducer_test, StopOfferInSubscribedRemainsInSubscribe EXPECT_THAT(subscriptionState, Eq(iox::SubscribeState::SUBSCRIBED)); } -TEST_F(SubscriberPortMultiProducer_test, unsubscribeInSubscribedTriggersUnsubMessage) +TEST_F(SubscriberPortMultiProducer_test, UnsubscribeInSubscribedTriggersUnsubMessage) { m_sutUserSideMultiProducer.subscribe(); m_sutRouDiSideMultiProducer.tryGetCaProMessage(); // only RouDi changes state @@ -471,14 +495,14 @@ TEST_F(SubscriberPortMultiProducer_test, unsubscribeInSubscribedTriggersUnsubMes auto maybeCaproMessage = m_sutRouDiSideMultiProducer.tryGetCaProMessage(); - EXPECT_TRUE(maybeCaproMessage.has_value()); + ASSERT_TRUE(maybeCaproMessage.has_value()); auto caproMessageResponse = maybeCaproMessage.value(); EXPECT_THAT(caproMessageResponse.m_type, Eq(iox::capro::CaproMessageType::UNSUB)); EXPECT_THAT(caproMessageResponse.m_serviceDescription, Eq(SubscriberPortSingleProducer_test::TEST_SERVICE_DESCRIPTION)); } -TEST_F(SubscriberPortMultiProducer_test, invalidMessageResultsInError) +TEST_F(SubscriberPortMultiProducer_test, InvalidMessageResultsInError) { auto errorHandlerCalled{false}; iox::Error receivedError; diff --git a/iceoryx_posh/test/moduletests/test_popo_trigger.cpp b/iceoryx_posh/test/moduletests/test_popo_trigger.cpp index cd00b391a8d..369ec4c0d0b 100644 --- a/iceoryx_posh/test/moduletests/test_popo_trigger.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_trigger.cpp @@ -119,6 +119,25 @@ TEST_F(Trigger_test, MovedAssignedValidTriggerIsValid) EXPECT_TRUE(static_cast(sut)); } +TEST_F(Trigger_test, MovedConstructedOriginIsInvalidTriggerAfterMove) +{ + Trigger trigger = createValidTrigger(); + Trigger sut{std::move(trigger)}; + + EXPECT_FALSE(trigger.isValid()); + EXPECT_THAT(trigger.getUniqueId(), Eq(Trigger::INVALID_TRIGGER_ID)); +} + +TEST_F(Trigger_test, MovedAssignedOriginIsInvalidTriggerAfterMove) +{ + Trigger sut; + Trigger trigger = createValidTrigger(); + sut = std::move(trigger); + + EXPECT_FALSE(trigger.isValid()); + EXPECT_THAT(trigger.getUniqueId(), Eq(Trigger::INVALID_TRIGGER_ID)); +} + TEST_F(Trigger_test, TriggerWithNullptrOriginIsValid) { uint64_t eventId = 0U; @@ -179,9 +198,18 @@ TEST_F(Trigger_test, InvalidateInvalidatesTrigger) TEST_F(Trigger_test, ResetCallsResetcallbackWithCorrectTriggerOrigin) { Trigger sut = createValidTrigger(); + auto uniqueId = sut.getUniqueId(); sut.reset(); - EXPECT_EQ(m_triggerClass.m_resetCallTriggerArg, sut.getUniqueId()); + EXPECT_EQ(m_triggerClass.m_resetCallTriggerArg, uniqueId); +} + +TEST_F(Trigger_test, ResetSetsTriggerIdToInvalid) +{ + Trigger sut = createValidTrigger(); + sut.reset(); + + EXPECT_EQ(sut.getUniqueId(), Trigger::INVALID_TRIGGER_ID); } TEST_F(Trigger_test, TriggerWithEmptyResetInvalidatesTriggerWhenBeingResetted) @@ -233,11 +261,12 @@ TEST_F(Trigger_test, UpdateOriginLeadsToDifferentHasTriggeredCallback) TEST_F(Trigger_test, UpdateOriginDoesNotUpdateHasTriggeredIfItsNotOriginatingFromOrigin) { + constexpr uint64_t USER_DEFINED_EVENT_ID = 891U; TriggerClass secondTriggerClass, thirdTriggerClass; Trigger sut(&m_triggerClass, {thirdTriggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, - 891U, + USER_DEFINED_EVENT_ID, TriggerClass::callback); sut.updateOrigin(&secondTriggerClass); @@ -254,33 +283,37 @@ TEST_F(Trigger_test, UpdateOriginLeadsToDifferentResetCallback) TriggerClass secondTriggerClass; sut.updateOrigin(&secondTriggerClass); + auto uniqueId = sut.getUniqueId(); sut.reset(); - EXPECT_EQ(secondTriggerClass.m_resetCallTriggerArg, sut.getUniqueId()); + EXPECT_EQ(secondTriggerClass.m_resetCallTriggerArg, uniqueId); } TEST_F(Trigger_test, UpdateOriginDoesNotUpdateResetIfItsNotOriginatingFromOrigin) { + constexpr uint64_t USER_DEFINED_EVENT_ID = 892U; TriggerClass secondTriggerClass, thirdTriggerClass; Trigger sut(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {thirdTriggerClass, &TriggerClass::resetCall}, - 891U, + USER_DEFINED_EVENT_ID, TriggerClass::callback); sut.updateOrigin(&secondTriggerClass); + auto uniqueId = sut.getUniqueId(); sut.reset(); - EXPECT_EQ(thirdTriggerClass.m_resetCallTriggerArg, sut.getUniqueId()); + EXPECT_EQ(thirdTriggerClass.m_resetCallTriggerArg, uniqueId); } TEST_F(Trigger_test, UpdateOriginUpdatesOriginOfEventInfo) { + constexpr uint64_t USER_DEFINED_EVENT_ID = 893U; TriggerClass secondTriggerClass; Trigger sut(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, - 891U, + USER_DEFINED_EVENT_ID, TriggerClass::callback); sut.updateOrigin(&secondTriggerClass); @@ -292,10 +325,11 @@ TEST_F(Trigger_test, UpdateOriginUpdatesOriginOfEventInfo) /// - hasTriggeredCallback TEST_F(Trigger_test, TriggerIsLogicalEqualToItself) { + constexpr uint64_t USER_DEFINED_EVENT_ID = 894U; Trigger sut1(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, - 8911U, + USER_DEFINED_EVENT_ID, TriggerClass::callback); EXPECT_TRUE(sut1.isLogicalEqualTo(sut1)); @@ -303,16 +337,18 @@ TEST_F(Trigger_test, TriggerIsLogicalEqualToItself) TEST_F(Trigger_test, TwoTriggersAreLogicalEqualIfRequirementsAreFullfilled) { + constexpr uint64_t USER_DEFINED_EVENT_ID = 896U; + constexpr uint64_t ANOTHER_USER_DEFINED_EVENT_ID = 8961U; Trigger sut1(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, - 891U, + USER_DEFINED_EVENT_ID, TriggerClass::callback); Trigger sut2(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, - 891U, + ANOTHER_USER_DEFINED_EVENT_ID, TriggerClass::callback); @@ -320,38 +356,42 @@ TEST_F(Trigger_test, TwoTriggersAreLogicalEqualIfRequirementsAreFullfilled) EXPECT_TRUE(sut2.isLogicalEqualTo(sut1)); } -TEST_F(Trigger_test, TwoTriggersAreNotLogicalEqualIfTriggerIdDiffers) +TEST_F(Trigger_test, TwoTriggersAreLogicalEqualIfOnlyTriggerIdDiffers) { + constexpr uint64_t USER_DEFINED_EVENT_ID = 2896U; + constexpr uint64_t ANOTHER_USER_DEFINED_EVENT_ID = 28961U; Trigger sut1(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, - 2891U, + USER_DEFINED_EVENT_ID, TriggerClass::callback); Trigger sut2(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, - 3891U, + ANOTHER_USER_DEFINED_EVENT_ID, TriggerClass::callback); - EXPECT_FALSE(sut1.isLogicalEqualTo(sut2)); - EXPECT_FALSE(sut2.isLogicalEqualTo(sut1)); + EXPECT_TRUE(sut1.isLogicalEqualTo(sut2)); + EXPECT_TRUE(sut2.isLogicalEqualTo(sut1)); } TEST_F(Trigger_test, TwoTriggersAreNotLogicalEqualIfHasTriggeredCallbackDiffers) { + constexpr uint64_t USER_DEFINED_EVENT_ID = 4896U; + constexpr uint64_t ANOTHER_USER_DEFINED_EVENT_ID = 48961U; TriggerClass secondTriggerClass; Trigger sut1(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, - 4891U, + USER_DEFINED_EVENT_ID, TriggerClass::callback); Trigger sut2(&m_triggerClass, {secondTriggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, - 4891U, + ANOTHER_USER_DEFINED_EVENT_ID, TriggerClass::callback); @@ -361,17 +401,18 @@ TEST_F(Trigger_test, TwoTriggersAreNotLogicalEqualIfHasTriggeredCallbackDiffers) TEST_F(Trigger_test, TwoTriggersAreNotLogicalEqualIfOriginDiffers) { + constexpr uint64_t USER_DEFINED_EVENT_ID = 4896U; TriggerClass secondTriggerClass; Trigger sut1(&m_triggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, - 4891U, + USER_DEFINED_EVENT_ID, TriggerClass::callback); Trigger sut2(&secondTriggerClass, {m_triggerClass, &TriggerClass::hasTriggered}, {m_triggerClass, &TriggerClass::resetCall}, - 4891U, + USER_DEFINED_EVENT_ID, TriggerClass::callback); diff --git a/iceoryx_posh/test/moduletests/test_popo_trigger_handle.cpp b/iceoryx_posh/test/moduletests/test_popo_trigger_handle.cpp index c3b1d92658d..75ab8752901 100644 --- a/iceoryx_posh/test/moduletests/test_popo_trigger_handle.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_trigger_handle.cpp @@ -36,21 +36,22 @@ class TriggerHandle_test : public Test ConditionVariableData m_condVar{"Horscht"}; TriggerHandle_test* m_self = this; - TriggerHandle m_sut{&m_condVar, {*this, &TriggerHandle_test::resetCallback}, 12}; + TriggerHandle m_sut{m_condVar, {*this, &TriggerHandle_test::resetCallback}, 12U}; }; -TEST_F(TriggerHandle_test, isValidWhenConditionVariableIsNotNull) +TEST_F(TriggerHandle_test, IsValidWhenConditionVariableIsNotNull) { EXPECT_TRUE(m_sut.isValid()); EXPECT_TRUE(m_sut); } -TEST_F(TriggerHandle_test, isNotValidWhenConditionVariableIsNull) +TEST_F(TriggerHandle_test, DefaultCTorConstructsInvalidHandle) { - TriggerHandle sut2{nullptr, {*m_self, &TriggerHandle_test::resetCallback}, 12}; + TriggerHandle sut2; EXPECT_FALSE(sut2.isValid()); + EXPECT_THAT(sut2.getUniqueId(), Eq(Trigger::INVALID_TRIGGER_ID)); EXPECT_FALSE(sut2); } @@ -60,6 +61,7 @@ TEST_F(TriggerHandle_test, InvalidateCreatesInvalidTriggerHandle) EXPECT_FALSE(m_sut.isValid()); EXPECT_FALSE(m_sut); + EXPECT_THAT(m_sut.getUniqueId(), Eq(Trigger::INVALID_TRIGGER_ID)); } TEST_F(TriggerHandle_test, ResetCreatesInvalidTriggerHandle) @@ -68,19 +70,22 @@ TEST_F(TriggerHandle_test, ResetCreatesInvalidTriggerHandle) EXPECT_FALSE(m_sut.isValid()); EXPECT_FALSE(m_sut); + EXPECT_THAT(m_sut.getUniqueId(), Eq(Trigger::INVALID_TRIGGER_ID)); } TEST_F(TriggerHandle_test, ResetCallsResetCallbackWhenHandleIsValid) { m_sut.reset(); - EXPECT_EQ(m_resetCallbackId, 12); + EXPECT_EQ(m_resetCallbackId, 12U); + EXPECT_THAT(m_sut.getUniqueId(), Eq(Trigger::INVALID_TRIGGER_ID)); } TEST_F(TriggerHandle_test, ResetDoesNotCallResetCallbackWhenHandleIsInvalid) { m_sut.invalidate(); m_sut.reset(); - EXPECT_EQ(m_resetCallbackId, 0); + EXPECT_EQ(m_resetCallbackId, 0U); + EXPECT_THAT(m_sut.getUniqueId(), Eq(Trigger::INVALID_TRIGGER_ID)); } TEST_F(TriggerHandle_test, getConditionVariableDataReturnsCorrectVar) @@ -90,8 +95,8 @@ TEST_F(TriggerHandle_test, getConditionVariableDataReturnsCorrectVar) TEST_F(TriggerHandle_test, getUniqueIdReturnsCorrectId) { - TriggerHandle sut2{nullptr, {*m_self, &TriggerHandle_test::resetCallback}, 8912}; - EXPECT_EQ(sut2.getUniqueId(), 8912); + TriggerHandle sut2{m_condVar, {*m_self, &TriggerHandle_test::resetCallback}, 8912U}; + EXPECT_EQ(sut2.getUniqueId(), 8912U); } TEST_F(TriggerHandle_test, triggerNotifiesConditionVariable) diff --git a/iceoryx_posh/test/moduletests/test_popo_untyped_publisher.cpp b/iceoryx_posh/test/moduletests/test_popo_untyped_publisher.cpp index 0676be3263d..92736c25c69 100644 --- a/iceoryx_posh/test/moduletests/test_popo_untyped_publisher.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_untyped_publisher.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2021 by Apex.AI Inc. All rights reserved. -// Copyright (c) 2020 by Robert Bosch GmbH, Apex.AI Inc. All rights reserved. +// 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. @@ -94,6 +94,23 @@ TEST_F(UntypedPublisherTest, LoanPreviousChunkFails) // ===== Cleanup ===== // } +TEST_F(UntypedPublisherTest, ReleaseDelegatesCallToPort) +{ + constexpr uint32_t ALLOCATION_SIZE = 7U; + EXPECT_CALL(portMock, tryAllocateChunk(ALLOCATION_SIZE)) + .WillOnce(Return(ByMove(iox::cxx::success(chunkMock.chunkHeader())))); + + auto result = sut.loan(ALLOCATION_SIZE); + ASSERT_FALSE(result.has_error()); + auto chunk = result.value(); + + // ===== Test ===== // + EXPECT_CALL(portMock, releaseChunk(chunkMock.chunkHeader())).Times(1); + sut.release(chunk); + // ===== Verify ===== // + // ===== Cleanup ===== // +} + TEST_F(UntypedPublisherTest, PublishesPayloadViaUnderlyingPort) { // ===== Setup ===== // diff --git a/iceoryx_posh/test/moduletests/test_popo_untyped_subscriber.cpp b/iceoryx_posh/test/moduletests/test_popo_untyped_subscriber.cpp index da85cbac0f6..7253d6cf99a 100644 --- a/iceoryx_posh/test/moduletests/test_popo_untyped_subscriber.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_untyped_subscriber.cpp @@ -139,7 +139,7 @@ TEST_F(UntypedSubscriberTest, TakeReturnsAllocatedMemoryChunk) ASSERT_FALSE(maybeChunk.has_error()); EXPECT_EQ(maybeChunk.value(), chunkMock.chunkHeader()->payload()); // ===== Cleanup ===== // - sut.releaseChunk(maybeChunk.value()); + sut.release(maybeChunk.value()); } TEST_F(UntypedSubscriberTest, ReleasesQueuedDataViaBaseSubscriber) diff --git a/iceoryx_posh/test/moduletests/test_popo_waitset.cpp b/iceoryx_posh/test/moduletests/test_popo_waitset.cpp index c10e7b882a2..40b17adab65 100644 --- a/iceoryx_posh/test/moduletests/test_popo_waitset.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_waitset.cpp @@ -1,4 +1,5 @@ -// Copyright (c) 2020, 2021 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. @@ -164,8 +165,9 @@ TEST_F(WaitSet_test, AcquireMaximumAllowedPlusOneTriggerFails) TEST_F(WaitSet_test, AcquireSameTriggerTwiceResultsInError) { - m_sut.attachEvent(m_simpleEvents[0], 0U); - auto result2 = m_sut.attachEvent(m_simpleEvents[0], 0U); + constexpr uint64_t USER_DEFINED_EVENT_ID = 0U; + m_sut.attachEvent(m_simpleEvents[0], USER_DEFINED_EVENT_ID); + auto result2 = m_sut.attachEvent(m_simpleEvents[0], USER_DEFINED_EVENT_ID); ASSERT_TRUE(result2.has_error()); EXPECT_THAT(result2.get_error(), Eq(WaitSetError::EVENT_ALREADY_ATTACHED)); @@ -173,8 +175,20 @@ TEST_F(WaitSet_test, AcquireSameTriggerTwiceResultsInError) TEST_F(WaitSet_test, AcquireSameTriggerWithNonNullIdTwiceResultsInError) { - m_sut.attachEvent(m_simpleEvents[0], 121U); - auto result2 = m_sut.attachEvent(m_simpleEvents[0], 121U); + constexpr uint64_t USER_DEFINED_EVENT_ID = 121U; + m_sut.attachEvent(m_simpleEvents[0], USER_DEFINED_EVENT_ID); + auto result2 = m_sut.attachEvent(m_simpleEvents[0], USER_DEFINED_EVENT_ID); + + ASSERT_TRUE(result2.has_error()); + EXPECT_THAT(result2.get_error(), Eq(WaitSetError::EVENT_ALREADY_ATTACHED)); +} + +TEST_F(WaitSet_test, AcquireSameTriggerWithDifferentIdResultsInError) +{ + constexpr uint64_t USER_DEFINED_EVENT_ID = 2101U; + constexpr uint64_t ANOTHER_USER_DEFINED_EVENT_ID = 9121U; + m_sut.attachEvent(m_simpleEvents[0], USER_DEFINED_EVENT_ID); + auto result2 = m_sut.attachEvent(m_simpleEvents[0], ANOTHER_USER_DEFINED_EVENT_ID); ASSERT_TRUE(result2.has_error()); EXPECT_THAT(result2.get_error(), Eq(WaitSetError::EVENT_ALREADY_ATTACHED)); @@ -186,7 +200,8 @@ TEST_F(WaitSet_test, ResetCallbackIsCalledWhenWaitsetGoesOutOfScope) SimpleEventClass simpleEvent; { WaitSetMock sut{&m_condVarData}; - sut.attachEvent(simpleEvent, 421337U); + constexpr uint64_t USER_DEFINED_EVENT_ID = 421337U; + sut.attachEvent(simpleEvent, USER_DEFINED_EVENT_ID); uniqueTriggerId = simpleEvent.getUniqueId(); } EXPECT_THAT(SimpleEventClass::m_invalidateTriggerId, Eq(uniqueTriggerId)); @@ -200,40 +215,42 @@ TEST_F(WaitSet_test, TriggerRemovesItselfFromWaitsetWhenGoingOutOfScope) m_sut.attachEvent(m_simpleEvents[i], 100U + i); } + constexpr uint64_t USER_DEFINED_EVENT_ID = 0U; { SimpleEventClass temporaryTrigger; - m_sut.attachEvent(temporaryTrigger, 0U); + m_sut.attachEvent(temporaryTrigger, USER_DEFINED_EVENT_ID); // goes out of scope here and creates space again for an additional trigger // if this doesn't work we are unable to acquire another trigger since the // waitset is already full } - auto result = m_sut.attachEvent(m_simpleEvents[0], 0U); + auto result = m_sut.attachEvent(m_simpleEvents.back(), USER_DEFINED_EVENT_ID); EXPECT_FALSE(result.has_error()); } TEST_F(WaitSet_test, MultipleTimerRemovingThemselfFromWaitsetWhenGoingOutOfScope) { iox::cxx::vector*, iox::MAX_NUMBER_OF_EVENTS_PER_WAITSET> trigger; - for (uint64_t i = 0U; i + 3U < iox::MAX_NUMBER_OF_EVENTS_PER_WAITSET; ++i) + for (uint64_t i = 3U; i < iox::MAX_NUMBER_OF_EVENTS_PER_WAITSET; ++i) { m_sut.attachEvent(m_simpleEvents[i], 100U + i); } + constexpr uint64_t USER_DEFINED_EVENT_ID = 0U; { SimpleEventClass temporaryTrigger1, temporaryTrigger2, temporaryTrigger3; - m_sut.attachEvent(temporaryTrigger1, 0U); - m_sut.attachEvent(temporaryTrigger2, 0U); - m_sut.attachEvent(temporaryTrigger3, 0U); + m_sut.attachEvent(temporaryTrigger1, USER_DEFINED_EVENT_ID); + m_sut.attachEvent(temporaryTrigger2, USER_DEFINED_EVENT_ID); + m_sut.attachEvent(temporaryTrigger3, USER_DEFINED_EVENT_ID); // goes out of scope here and creates space again for an additional trigger // if this doesn't work we are unable to acquire another trigger since the // waitset is already full } - auto result0 = m_sut.attachEvent(m_simpleEvents[0], 0U); - auto result1 = m_sut.attachEvent(m_simpleEvents[1], 0U); - auto result2 = m_sut.attachEvent(m_simpleEvents[2], 0U); + auto result0 = m_sut.attachEvent(m_simpleEvents[0], USER_DEFINED_EVENT_ID); + auto result1 = m_sut.attachEvent(m_simpleEvents[1], USER_DEFINED_EVENT_ID); + auto result2 = m_sut.attachEvent(m_simpleEvents[2], USER_DEFINED_EVENT_ID); EXPECT_FALSE(result0.has_error()); EXPECT_FALSE(result1.has_error()); EXPECT_FALSE(result2.has_error()); diff --git a/iceoryx_posh/test/moduletests/test_posh_runtime.cpp b/iceoryx_posh/test/moduletests/test_posh_runtime.cpp index 7c2c9ca9a5a..7ce15e61638 100644 --- a/iceoryx_posh/test/moduletests/test_posh_runtime.cpp +++ b/iceoryx_posh/test/moduletests/test_posh_runtime.cpp @@ -356,6 +356,27 @@ TEST_F(PoshRuntime_test, GetMiddlewarePublisherWithSameServiceDescriptionsAndOne } } +TEST_F(PoshRuntime_test, GetMiddlewarePublisherWithoutOfferOnCreateLeadsToNotOfferedPublisherBeingCreated) +{ + iox::popo::PublisherOptions publisherOptions; + publisherOptions.offerOnCreate = false; + + const auto publisherPortData = m_runtime->getMiddlewarePublisher( + iox::capro::ServiceDescription(69U, 96U, 1893U), publisherOptions, iox::runtime::PortConfigInfo(11U, 22U, 33U)); + + EXPECT_FALSE(publisherPortData->m_offeringRequested); +} + +TEST_F(PoshRuntime_test, GetMiddlewarePublisherWithOfferOnCreateLeadsToOfferedPublisherBeingCreated) +{ + iox::popo::PublisherOptions publisherOptions; + publisherOptions.offerOnCreate = true; + + const auto publisherPortData = m_runtime->getMiddlewarePublisher( + iox::capro::ServiceDescription(17U, 4U, 21U), publisherOptions, iox::runtime::PortConfigInfo(11U, 22U, 33U)); + + EXPECT_TRUE(publisherPortData->m_offeringRequested); +} TEST_F(PoshRuntime_test, GetMiddlewareSubscriberIsSuccessful) { @@ -385,6 +406,17 @@ TEST_F(PoshRuntime_test, GetMiddlewareSubscriberWithQueueGreaterMaxCapacityClamp EXPECT_EQ(MAX_QUEUE_CAPACITY, subscriberPort->m_chunkReceiverData.m_queue.capacity()); } +TEST_F(PoshRuntime_test, GetMiddlewareSubscriberWithQueueCapacityZeroClampsQueueCapacityTo1) +{ + iox::popo::SubscriberOptions subscriberOptions; + subscriberOptions.queueCapacity = 0U; + + auto subscriberPort = m_runtime->getMiddlewareSubscriber( + iox::capro::ServiceDescription(34U, 4U, 4U), subscriberOptions, iox::runtime::PortConfigInfo(11U, 22U, 33U)); + + EXPECT_EQ(1U, subscriberPort->m_chunkReceiverData.m_queue.capacity()); +} + TEST_F(PoshRuntime_test, GetMiddlewareSubscriberDefaultArgs) { auto subscriberPort = m_runtime->getMiddlewareSubscriber(iox::capro::ServiceDescription(99U, 1U, 20U)); @@ -417,6 +449,28 @@ TEST_F(PoshRuntime_test, GetMiddlewareSubscriberSubscriberlistOverflow) EXPECT_TRUE(subscriberlistOverflowDetected); } +TEST_F(PoshRuntime_test, GetMiddlewareSubscriberWithoutSubscribeOnCreateLeadsToSubscriberThatDoesNotWantToBeSubscribed) +{ + iox::popo::SubscriberOptions subscriberOptions; + subscriberOptions.subscribeOnCreate = false; + + auto subscriberPortData = m_runtime->getMiddlewareSubscriber( + iox::capro::ServiceDescription(17U, 17U, 17U), subscriberOptions, iox::runtime::PortConfigInfo(11U, 22U, 33U)); + + EXPECT_FALSE(subscriberPortData->m_subscribeRequested); +} + +TEST_F(PoshRuntime_test, GetMiddlewareSubscriberWithSubscribeOnCreateLeadsToSubscriberThatWantsToBeSubscribed) +{ + iox::popo::SubscriberOptions subscriberOptions; + subscriberOptions.subscribeOnCreate = true; + + auto subscriberPortData = m_runtime->getMiddlewareSubscriber( + iox::capro::ServiceDescription(1U, 2U, 3U), subscriberOptions, iox::runtime::PortConfigInfo(11U, 22U, 33U)); + + EXPECT_TRUE(subscriberPortData->m_subscribeRequested); +} + TEST_F(PoshRuntime_test, GetMiddlewareConditionVariableIsSuccessful) { auto conditionVariable = m_runtime->getMiddlewareConditionVariable(); @@ -448,6 +502,45 @@ 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 40420e297fe..81cc13e70a1 100644 --- a/iceoryx_posh/test/moduletests/test_roudi_portmanager.cpp +++ b/iceoryx_posh/test/moduletests/test_roudi_portmanager.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2021 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2019 - 2021 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"); @@ -70,7 +70,7 @@ class PortManager_test : public Test void SetUp() override { testing::internal::CaptureStderr(); - m_instIdCounter = m_sIdCounter = 1; + m_instIdCounter = m_sIdCounter = 1U; m_eventIdCounter = 0; // starting at {1,1,1} @@ -108,11 +108,11 @@ class PortManager_test : public Test m_eventIdCounter++; if (m_eventIdCounter == std::numeric_limits::max()) { - m_eventIdCounter = 1; + m_eventIdCounter = 1U; m_instIdCounter++; // not using max (wildcard) if (m_instIdCounter == std::numeric_limits::max()) { - m_instIdCounter = 1; + m_instIdCounter = 1U; m_sIdCounter++; if (m_sIdCounter == std::numeric_limits::max()) { @@ -207,22 +207,22 @@ void setDestroyFlagAndClearContainer(vector& container) container.clear(); } -TEST_F(PortManager_test, doDiscovery_singleShotPublisherFirst) +TEST_F(PortManager_test, DoDiscoveryWithSingleShotPublisherFirst) { - PublisherOptions publisherOptions{1, "node"}; - SubscriberOptions subscriberOptions{1, 1, "node"}; + PublisherOptions publisherOptions{1U, "node", false}; + SubscriberOptions subscriberOptions{1U, 1U, "node", false}; PublisherPortUser publisher( m_portManager ->acquirePublisherPortData( - {1, 1, 1}, publisherOptions, "guiseppe", m_payloadMemoryManager, PortConfigInfo()) + {1U, 1U, 1U}, publisherOptions, "guiseppe", m_payloadMemoryManager, PortConfigInfo()) .value()); ASSERT_TRUE(publisher); publisher.offer(); // no doDiscovery() at this position is intentional SubscriberPortUser subscriber( - m_portManager->acquireSubscriberPortData({1, 1, 1}, subscriberOptions, "schlomo", PortConfigInfo()).value()); + m_portManager->acquireSubscriberPortData({1U, 1U, 1U}, subscriberOptions, "schlomo", PortConfigInfo()).value()); ASSERT_TRUE(subscriber); subscriber.subscribe(); @@ -232,13 +232,13 @@ TEST_F(PortManager_test, doDiscovery_singleShotPublisherFirst) EXPECT_THAT(subscriber.getSubscriptionState(), Eq(iox::SubscribeState::SUBSCRIBED)); } -TEST_F(PortManager_test, doDiscovery_singleShotSubscriberFirst) +TEST_F(PortManager_test, DoDiscoveryWithSingleShotSubscriberFirst) { - PublisherOptions publisherOptions{1, "node"}; - SubscriberOptions subscriberOptions{1, 1, "node"}; + PublisherOptions publisherOptions{1U, "node", false}; + SubscriberOptions subscriberOptions{1U, 1U, "node", false}; SubscriberPortUser subscriber( - m_portManager->acquireSubscriberPortData({1, 1, 1}, subscriberOptions, "schlomo", PortConfigInfo()).value()); + m_portManager->acquireSubscriberPortData({1U, 1U, 1U}, subscriberOptions, "schlomo", PortConfigInfo()).value()); ASSERT_TRUE(subscriber); subscriber.subscribe(); // no doDiscovery() at this position is intentional @@ -246,7 +246,7 @@ TEST_F(PortManager_test, doDiscovery_singleShotSubscriberFirst) PublisherPortUser publisher( m_portManager ->acquirePublisherPortData( - {1, 1, 1}, publisherOptions, "guiseppe", m_payloadMemoryManager, PortConfigInfo()) + {1U, 1U, 1U}, publisherOptions, "guiseppe", m_payloadMemoryManager, PortConfigInfo()) .value()); ASSERT_TRUE(publisher); publisher.offer(); @@ -257,13 +257,13 @@ TEST_F(PortManager_test, doDiscovery_singleShotSubscriberFirst) EXPECT_THAT(subscriber.getSubscriptionState(), Eq(iox::SubscribeState::SUBSCRIBED)); } -TEST_F(PortManager_test, doDiscovery_singleShotSubscriberFirstWithDiscovery) +TEST_F(PortManager_test, DoDiscoveryWithDiscoveryLoopInBetweenCreationOfSubscriberAndPublisher) { - PublisherOptions publisherOptions{1, "node"}; - SubscriberOptions subscriberOptions{1, 1, "node"}; + PublisherOptions publisherOptions{1U, "node", false}; + SubscriberOptions subscriberOptions{1U, 1U, "node", false}; SubscriberPortUser subscriber( - m_portManager->acquireSubscriberPortData({1, 1, 1}, subscriberOptions, "schlomo", PortConfigInfo()).value()); + m_portManager->acquireSubscriberPortData({1U, 1U, 1U}, subscriberOptions, "schlomo", PortConfigInfo()).value()); ASSERT_TRUE(subscriber); subscriber.subscribe(); m_portManager->doDiscovery(); @@ -271,7 +271,7 @@ TEST_F(PortManager_test, doDiscovery_singleShotSubscriberFirstWithDiscovery) PublisherPortUser publisher( m_portManager ->acquirePublisherPortData( - {1, 1, 1}, publisherOptions, "guiseppe", m_payloadMemoryManager, PortConfigInfo()) + {1U, 1U, 1U}, publisherOptions, "guiseppe", m_payloadMemoryManager, PortConfigInfo()) .value()); ASSERT_TRUE(publisher); publisher.offer(); @@ -282,13 +282,13 @@ TEST_F(PortManager_test, doDiscovery_singleShotSubscriberFirstWithDiscovery) EXPECT_THAT(subscriber.getSubscriptionState(), Eq(iox::SubscribeState::SUBSCRIBED)); } -TEST_F(PortManager_test, doDiscovery_rightOrdering) +TEST_F(PortManager_test, DoDiscoveryWithSubscribersCreatedBeforeAndAfterCreationOfPublisher) { - PublisherOptions publisherOptions{1, "node"}; - SubscriberOptions subscriberOptions{1, 1, "node"}; + PublisherOptions publisherOptions{1U, "node", false}; + SubscriberOptions subscriberOptions{1U, 1U, "node", false}; SubscriberPortUser subscriber1( - m_portManager->acquireSubscriberPortData({1, 1, 1}, subscriberOptions, "schlomo", PortConfigInfo()).value()); + m_portManager->acquireSubscriberPortData({1U, 1U, 1U}, subscriberOptions, "schlomo", PortConfigInfo()).value()); ASSERT_TRUE(subscriber1); subscriber1.subscribe(); @@ -297,13 +297,13 @@ TEST_F(PortManager_test, doDiscovery_rightOrdering) PublisherPortUser publisher( m_portManager ->acquirePublisherPortData( - {1, 1, 1}, publisherOptions, "guiseppe", m_payloadMemoryManager, PortConfigInfo()) + {1U, 1U, 1U}, publisherOptions, "guiseppe", m_payloadMemoryManager, PortConfigInfo()) .value()); ASSERT_TRUE(publisher); publisher.offer(); SubscriberPortUser subscriber2( - m_portManager->acquireSubscriberPortData({1, 1, 1}, subscriberOptions, "ingnatz", PortConfigInfo()).value()); + m_portManager->acquireSubscriberPortData({1U, 1U, 1U}, subscriberOptions, "ingnatz", PortConfigInfo()).value()); ASSERT_TRUE(subscriber2); subscriber2.subscribe(); @@ -314,10 +314,84 @@ TEST_F(PortManager_test, doDiscovery_rightOrdering) EXPECT_THAT(subscriber2.getSubscriptionState(), Eq(iox::SubscribeState::SUBSCRIBED)); } +TEST_F(PortManager_test, SubscribeOnCreateSubscribesWithoutDiscoveryLoopWhenPublisherAvailable) +{ + PublisherOptions publisherOptions{1U, "node", false}; + SubscriberOptions subscriberOptions{1U, 1U, "node", true}; + PublisherPortUser publisher( + m_portManager + ->acquirePublisherPortData( + {1U, 1U, 1U}, publisherOptions, "guiseppe", m_payloadMemoryManager, PortConfigInfo()) + .value()); + publisher.offer(); + m_portManager->doDiscovery(); + + SubscriberPortUser subscriber( + m_portManager->acquireSubscriberPortData({1U, 1U, 1U}, subscriberOptions, "schlomo", PortConfigInfo()).value()); + + ASSERT_TRUE(publisher.hasSubscribers()); + EXPECT_THAT(subscriber.getSubscriptionState(), Eq(iox::SubscribeState::SUBSCRIBED)); +} + +TEST_F(PortManager_test, OfferOnCreateSubscribesWithoutDiscoveryLoopWhenSubscriberAvailable) +{ + PublisherOptions publisherOptions{1U, "node", true}; + SubscriberOptions subscriberOptions{1U, 1U, "node", false}; + SubscriberPortUser subscriber( + m_portManager->acquireSubscriberPortData({1U, 1U, 1U}, subscriberOptions, "schlomo", PortConfigInfo()).value()); + subscriber.subscribe(); + m_portManager->doDiscovery(); + + PublisherPortUser publisher( + m_portManager + ->acquirePublisherPortData( + {1U, 1U, 1U}, publisherOptions, "guiseppe", m_payloadMemoryManager, PortConfigInfo()) + .value()); + + ASSERT_TRUE(publisher.hasSubscribers()); + EXPECT_THAT(subscriber.getSubscriptionState(), Eq(iox::SubscribeState::SUBSCRIBED)); +} + +TEST_F(PortManager_test, OfferOnCreateAndSubscribeOnCreateNeedsNoMoreDiscoveryLoopSubscriberFirst) +{ + PublisherOptions publisherOptions{1U, "node", true}; + SubscriberOptions subscriberOptions{1U, 1U, "node", true}; + SubscriberPortUser subscriber( + m_portManager->acquireSubscriberPortData({1U, 1U, 1U}, subscriberOptions, "schlomo", PortConfigInfo()).value()); + + PublisherPortUser publisher( + m_portManager + ->acquirePublisherPortData( + {1U, 1U, 1U}, publisherOptions, "guiseppe", m_payloadMemoryManager, PortConfigInfo()) + .value()); + + ASSERT_TRUE(publisher.hasSubscribers()); + EXPECT_THAT(subscriber.getSubscriptionState(), Eq(iox::SubscribeState::SUBSCRIBED)); +} + +TEST_F(PortManager_test, OfferOnCreateAndSubscribeOnCreateNeedsNoMoreDiscoveryLoopPublisherFirst) +{ + PublisherOptions publisherOptions{1U, "node", true}; + SubscriberOptions subscriberOptions{1U, 1U, "node", true}; + PublisherPortUser publisher( + m_portManager + ->acquirePublisherPortData( + {1U, 1U, 1U}, publisherOptions, "guiseppe", m_payloadMemoryManager, PortConfigInfo()) + .value()); + + SubscriberPortUser subscriber( + m_portManager->acquireSubscriberPortData({1U, 1U, 1U}, subscriberOptions, "schlomo", PortConfigInfo()).value()); + + + ASSERT_TRUE(publisher.hasSubscribers()); + EXPECT_THAT(subscriber.getSubscriptionState(), Eq(iox::SubscribeState::SUBSCRIBED)); +} + + TEST_F(PortManager_test, AcquiringOneMoreThanMaximumNumberOfPublishersFails) { iox::ProcessName_t processName = "test1"; - PublisherOptions publisherOptions{1, "run1"}; + PublisherOptions publisherOptions{1U, "run1"}; for (unsigned int i = 0; i < iox::MAX_PUBLISHERS; i++) { @@ -346,7 +420,7 @@ TEST_F(PortManager_test, AcquiringOneMoreThanMaximumNumberOfPublishersFails) TEST_F(PortManager_test, AcquiringOneMoreThanMaximumNumberOfSubscribersFails) { iox::ProcessName_t processName1 = "test1"; - SubscriberOptions subscriberOptions{1, 1, "run1"}; + SubscriberOptions subscriberOptions{1U, 1U, "run1"}; for (unsigned int i = 0; i < iox::MAX_SUBSCRIBERS; i++) { @@ -543,7 +617,66 @@ TEST_F(PortManager_test, AcquireConditionVariablesDataAfterDestroyingPreviouslyA acquireMaxNumberOfConditionVariables(processName); } -TEST_F(PortManager_test, AcquireMaxNumberOfNodePorts) +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"; @@ -623,8 +756,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{1, "node"}; - SubscriberOptions subscriberOptions{1, 1, "node"}; + PublisherOptions publisherOptions{1U, "node", false}; + SubscriberOptions subscriberOptions{1U, 1U, "node", false}; // two processes process1 and process2 each with a publisher and subscriber that match to the other process auto publisherData1 = @@ -743,7 +876,7 @@ TEST_F(PortManager_test, OfferPublisherServiceUpdatesServiceRegistryChangeCounte PublisherOptions publisherOptions{1}; auto publisherPortData = m_portManager->acquirePublisherPortData( - {1, 1, 1}, publisherOptions, m_ProcessName, m_payloadMemoryManager, PortConfigInfo()); + {1U, 1U, 1U}, publisherOptions, m_ProcessName, m_payloadMemoryManager, PortConfigInfo()); ASSERT_FALSE(publisherPortData.has_error()); PublisherPortUser publisher(publisherPortData.value()); diff --git a/iceoryx_utils/include/iceoryx_utils/cxx/helplets.hpp b/iceoryx_utils/include/iceoryx_utils/cxx/helplets.hpp index 704d63adc55..58d41acec4a 100644 --- a/iceoryx_utils/include/iceoryx_utils/cxx/helplets.hpp +++ b/iceoryx_utils/include/iceoryx_utils/cxx/helplets.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. @@ -20,7 +21,9 @@ #include "iceoryx_utils/platform/platform_correction.hpp" #include +#include #include +#include #include namespace iox @@ -39,6 +42,31 @@ Require(const bool condition, const char* file, const int line, const char* func std::terminate(); } } + +/// @brief struct to find the best fitting unsigned integer type +template +struct bestFittingTypeImpl +{ + using Type_t = uint64_t; +}; + +template <> +struct bestFittingTypeImpl +{ + using Type_t = uint8_t; +}; + +template <> +struct bestFittingTypeImpl +{ + using Type_t = uint16_t; +}; + +template <> +struct bestFittingTypeImpl +{ + using Type_t = uint32_t; +}; } // namespace internal // implementing C++ Core Guideline, I.6. Prefer Expects @@ -180,6 +208,20 @@ auto enumTypeAsUnderlyingType(enum_type const value) -> typename std::underlying return static_cast::type>(value); } +/// calls a given functor for every element in a given container +/// @tparam[in] Container type which must be iteratable +/// @tparam[in] Functor which has one argument, the element type of the container +/// @param[in] c container which should be iterated +/// @param[in] f functor which should be applied to every element +template +void forEach(Container& c, const Functor& f) noexcept +{ + for (auto& element : c) + { + f(element); + } +} + /// @brief Get the size of a string represented by a char array at compile time. /// @tparam The size of the char array filled out by the compiler. /// @param[in] The actual content of the char array is not of interest. Its just the size of the array that matters. @@ -190,6 +232,22 @@ static constexpr uint64_t strlen2(char const (&/*notInterested*/)[SizeValue]) return SizeValue - 1; } +/// @brief get the best fitting unsigned integer type for a given value at compile time +template +struct bestFittingType +{ +/// ignore the warnings because we need the comparisons to find the best fitting type +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wtype-limits" + using Type_t = typename internal::bestFittingTypeImpl<(Value > std::numeric_limits::max()), + (Value > std::numeric_limits::max()), + (Value > std::numeric_limits::max())>::Type_t; +#pragma GCC diagnostic pop +}; + +template +using BestFittingType_t = typename bestFittingType::Type_t; + /// @brief if a function has a return value which you do not want to use then you can wrap the function with that macro. /// Purpose is to suppress the unused compiler warning by adding an attribute to the return value /// @param[in] name name of the function where the return value is not used. @@ -203,6 +261,13 @@ static constexpr uint64_t strlen2(char const (&/*notInterested*/)[SizeValue]) #define DISCARD_RESULT(expr) auto DISCARD_RESULT_VARIABLE(unusedOnLine, __LINE__) [[gnu::unused]] = expr // clang-format on +/// @brief Returns info whether called on a 32-bit system +/// @return True if called on 32-bit, false if not 32-bit system +constexpr bool isCompiledOn32BitSystem() +{ + return INTPTR_MAX == INT32_MAX; +} + } // namespace cxx } // namespace iox diff --git a/iceoryx_utils/include/iceoryx_utils/cxx/type_traits.hpp b/iceoryx_utils/include/iceoryx_utils/cxx/type_traits.hpp index 2cfd0701e4f..f0e180d4e0e 100644 --- a/iceoryx_utils/include/iceoryx_utils/cxx/type_traits.hpp +++ b/iceoryx_utils/include/iceoryx_utils/cxx/type_traits.hpp @@ -72,6 +72,9 @@ template using not_same = typename std:: integral_constant::type, typename std::decay::type>::value)>; +/// @brief Maps a sequence of any types to the type void +template +using void_t = void; } // namespace cxx } // namespace iox 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 e783744dbb3..65dbcedafe1 100644 --- a/iceoryx_utils/include/iceoryx_utils/error_handling/error_handling.hpp +++ b/iceoryx_utils/include/iceoryx_utils/error_handling/error_handling.hpp @@ -41,13 +41,16 @@ namespace iox error(POSH__RUNTIME_ROUDI_PUBLISHER_LIST_FULL) \ error(POSH__RUNTIME_ROUDI_SUBSCRIBER_LIST_FULL) \ error(POSH__RUNTIME_ROUDI_CONDITION_VARIABLE_LIST_FULL) \ + error(POSH__RUNTIME_ROUDI_EVENT_VARIABLE_LIST_FULL) \ error(POSH__RUNTIME_ROUDI_REQUEST_PUBLISHER_WRONG_IPC_MESSAGE_RESPONSE) \ error(POSH__RUNTIME_ROUDI_REQUEST_SUBSCRIBER_WRONG_IPC_MESSAGE_RESPONSE) \ error(POSH__RUNTIME_ROUDI_REQUEST_CONDITION_VARIABLE_WRONG_IPC_MESSAGE_RESPONSE) \ + error(POSH__RUNTIME_ROUDI_REQUEST_EVENT_VARIABLE_WRONG_MESSAGE_QUEUE_RESPONSE) \ error(POSH__RUNTIME_ROUDI_GET_MW_INTERFACE_WRONG_IPC_MESSAGE_RESPONSE) \ error(POSH__RUNTIME_ROUDI_CREATE_NODE_WRONG_IPC_MESSAGE_RESPONSE) \ error(POSH__RUNTIME_ROUDI_GET_MW_APPLICATION_WRONG_IPC_MESSAGE_RESPONSE) \ error(POSH__RUNTIME_ROUDI_CONDITION_VARIABLE_CREATION_UNDEFINED_BEHAVIOR) \ + error(POSH__RUNTIME_ROUDI_EVENT_VARIABLE_CREATION_UNDEFINED_BEHAVIOR) \ error(POSH__PORT_MANAGER_PUBLISHERPORT_NOT_UNIQUE) \ error(POSH__MEMPOOL_POSSIBLE_DOUBLE_FREE) \ error(POSH__RECEIVERPORT_DELIVERYFIFO_OVERFLOW) \ @@ -80,6 +83,10 @@ namespace iox error(POPO__CONDITION_VARIABLE_WAITER_SEMAPHORE_CORRUPTED_IN_TIMED_WAIT) \ error(POPO__CONDITION_VARIABLE_WAITER_SEMAPHORE_CORRUPTED_IN_RESET) \ 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) \ @@ -104,6 +111,7 @@ namespace iox error(PORT_POOL__APPLICATIONLIST_OVERFLOW) \ error(PORT_POOL__NODELIST_OVERFLOW) \ error(PORT_POOL__CONDITION_VARIABLE_LIST_OVERFLOW) \ + error(PORT_POOL__EVENT_VARIABLE_LIST_OVERFLOW) \ error(PORT_MANAGER__PORT_POOL_UNAVAILABLE) \ error(PORT_MANAGER__INTROSPECTION_MEMORY_MANAGER_UNAVAILABLE) \ error(PORT_MANAGER__HANDLE_PUBLISHER_PORTS_INVALID_CAPRO_MESSAGE) \ @@ -127,7 +135,9 @@ namespace iox error(POSIX_TIMER__TIMERPOOL_OVERFLOW) \ error(POSIX_TIMER__INCONSISTENT_STATE) \ error(POSIX_TIMER__CALLBACK_RUNTIME_EXCEEDS_RETRIGGER_TIME) \ - error(VARIANT_QUEUE__UNSUPPORTED_QUEUE_TYPE) + error(VARIANT_QUEUE__UNSUPPORTED_QUEUE_TYPE) \ + error(BINDING_C__PUBLISHER_OPTIONS_NOT_INITIALIZED) \ + error(BINDING_C__SUBSCRIBER_OPTIONS_NOT_INITIALIZED) // clang-format on @@ -137,7 +147,6 @@ namespace iox enum class Error : uint32_t { - INVALID_STATE, ICEORYX_ERRORS(CREATE_ICEORYX_ERROR_ENUM) }; diff --git a/iceoryx_utils/include/iceoryx_utils/internal/concurrent/smart_lock.hpp b/iceoryx_utils/include/iceoryx_utils/internal/concurrent/smart_lock.hpp index bc11627bc33..47784752ec3 100644 --- a/iceoryx_utils/include/iceoryx_utils/internal/concurrent/smart_lock.hpp +++ b/iceoryx_utils/include/iceoryx_utils/internal/concurrent/smart_lock.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. @@ -47,18 +48,18 @@ namespace concurrent /// } /// } /// @endcode -template +template class smart_lock { private: class Proxy { public: - Proxy(T* base, MutexType* lock); - ~Proxy(); + Proxy(T* base, MutexType* lock) noexcept; + ~Proxy() noexcept; - T* operator->(); - T* operator->() const; + T* operator->() noexcept; + T* operator->() const noexcept; private: T* base; @@ -66,13 +67,13 @@ class smart_lock }; public: - smart_lock(); - smart_lock(const T& t); - smart_lock(const smart_lock& rhs); - smart_lock(smart_lock&& rhs); - smart_lock& operator=(const smart_lock& rhs); - smart_lock& operator=(smart_lock&& rhs); - ~smart_lock() = default; + smart_lock() noexcept; + smart_lock(const T& t) noexcept; + smart_lock(const smart_lock& rhs) noexcept; + smart_lock(smart_lock&& rhs) noexcept; + smart_lock& operator=(const smart_lock& rhs) noexcept; + smart_lock& operator=(smart_lock&& rhs) noexcept; + ~smart_lock() noexcept = default; /// @brief The arrow operator returns a proxy object which locks the mutex /// of smart_lock and has another arrow operator defined which @@ -83,7 +84,7 @@ class smart_lock /// iox::concurrent::smart_lock> threadSafeVector; /// threadSafeVector->push_back(123); // this call is secured by a mutex /// @endcode - Proxy operator->(); + Proxy operator->() noexcept; /// @brief The arrow operator returns a proxy object which locks the mutex /// of smart_lock and has another arrow operator defined which @@ -94,7 +95,7 @@ class smart_lock /// iox::concurrent::smart_lock> threadSafeVector; /// threadSafeVector->push_back(123); // this call is secured by a mutex /// @endcode - Proxy operator->() const; + Proxy operator->() const noexcept; /// @brief If you need to lock your object over multiple method calls you /// acquire a scope guard which locks the object as long as this @@ -117,7 +118,7 @@ class smart_lock /// if ( iter != vectorGuard->end() ) /// vectorGuard->erase(iter); /// } - Proxy GetScopeGuard(); + Proxy GetScopeGuard() noexcept; /// @brief If you need to lock your object over multiple method calls you /// acquire a scope guard which locks the object as long as this @@ -140,11 +141,14 @@ class smart_lock /// if ( iter != vectorGuard->end() ) /// vectorGuard->erase(iter); /// } - Proxy GetScopeGuard() const; + Proxy GetScopeGuard() const noexcept; + + /// @brief Returns a copy of the underlying object + T GetCopy() const noexcept; private: T base; - MutexType lock; + mutable MutexType lock; }; } // namespace concurrent } // namespace iox diff --git a/iceoryx_utils/include/iceoryx_utils/internal/concurrent/smart_lock.inl b/iceoryx_utils/include/iceoryx_utils/internal/concurrent/smart_lock.inl index 816b4a004bf..57f2e5f01fa 100644 --- a/iceoryx_utils/include/iceoryx_utils/internal/concurrent/smart_lock.inl +++ b/iceoryx_utils/include/iceoryx_utils/internal/concurrent/smart_lock.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. @@ -23,82 +24,98 @@ namespace iox namespace concurrent { template -smart_lock make_smart_lock(Targs&&... args) +smart_lock make_smart_lock(Targs&&... args) noexcept { return smart_lock(T(std::forward(args)...)); } template -smart_lock::smart_lock() +smart_lock::smart_lock() noexcept { } template -smart_lock::smart_lock(const T& t) +smart_lock::smart_lock(const T& t) noexcept : base(t) { } template -smart_lock::smart_lock(const smart_lock& rhs) +smart_lock::smart_lock(const smart_lock& rhs) noexcept { - std::lock_guard guard(rhs.lock); + std::lock_guard guard(rhs.lock); base = rhs.base; } template -smart_lock::smart_lock(smart_lock&& rhs) +smart_lock::smart_lock(smart_lock&& rhs) noexcept { - std::lock_guard guard(rhs.lock); + std::lock_guard guard(rhs.lock); base = std::move(rhs.base); } template -smart_lock& smart_lock::operator=(const smart_lock& rhs) +smart_lock& smart_lock::operator=(const smart_lock& rhs) noexcept { - std::lock(lock, rhs.lock); - std::lock_guard guard(lock, std::adopt_lock); - std::lock_guard guardRhs(rhs.lock, std::adopt_lock); - base = rhs.base; + if (this != rhs) + { + std::lock(lock, rhs.lock); + std::lock_guard guard(lock, std::adopt_lock); + std::lock_guard guardRhs(rhs.lock, std::adopt_lock); + base = rhs.base; + } + + return *this; } template -smart_lock& smart_lock::operator=(smart_lock&& rhs) -{ - std::lock(lock, rhs.lock); - std::lock_guard guard(lock, std::adopt_lock); - std::lock_guard guardRhs(rhs.lock, std::adopt_lock); - base = std::move(rhs.base); +smart_lock& smart_lock::operator=(smart_lock&& rhs) noexcept +{ + if (this != &rhs) + { + std::lock(lock, rhs.lock); + std::lock_guard guard(lock, std::adopt_lock); + std::lock_guard guardRhs(rhs.lock, std::adopt_lock); + base = std::move(rhs.base); + } + return *this; } template -typename smart_lock::Proxy smart_lock::operator->() +typename smart_lock::Proxy smart_lock::operator->() noexcept { return Proxy(&base, &lock); } template -typename smart_lock::Proxy smart_lock::operator->() const +typename smart_lock::Proxy smart_lock::operator->() const noexcept { return const_cast*>(this)->operator->(); } template -typename smart_lock::Proxy smart_lock::GetScopeGuard() +typename smart_lock::Proxy smart_lock::GetScopeGuard() noexcept { return Proxy(&base, &lock); } template -typename smart_lock::Proxy smart_lock::GetScopeGuard() const +typename smart_lock::Proxy smart_lock::GetScopeGuard() const noexcept { return const_cast*>(this)->GetScopeGuard(); } +template +inline T smart_lock::GetCopy() const noexcept +{ + std::lock_guard guard(lock); + return base; +} + // PROXY OBJECT template -smart_lock::Proxy::Proxy(T* base, MutexType* lock) +smart_lock::Proxy::Proxy(T* base, MutexType* lock) noexcept : base(base) , lock(lock) { @@ -106,19 +123,19 @@ smart_lock::Proxy::Proxy(T* base, MutexType* lock) } template -smart_lock::Proxy::~Proxy() +smart_lock::Proxy::~Proxy() noexcept { lock->unlock(); } template -T* smart_lock::Proxy::operator->() +T* smart_lock::Proxy::operator->() noexcept { return base; } template -T* smart_lock::Proxy::operator->() const +T* smart_lock::Proxy::operator->() const noexcept { return const_cast::Proxy*>(this)->operator->(); } diff --git a/iceoryx_utils/include/iceoryx_utils/internal/cxx/expected.inl b/iceoryx_utils/include/iceoryx_utils/internal/cxx/expected.inl index d9168657d35..a9495d17294 100644 --- a/iceoryx_utils/include/iceoryx_utils/internal/cxx/expected.inl +++ b/iceoryx_utils/include/iceoryx_utils/internal/cxx/expected.inl @@ -29,7 +29,7 @@ struct HasInvalidStateMember : std::false_type { }; template -struct HasInvalidStateMember> : std::true_type +struct HasInvalidStateMember> : std::true_type { }; template diff --git a/iceoryx_utils/test/moduletests/test_cxx_helplets.cpp b/iceoryx_utils/test/moduletests/test_cxx_helplets.cpp index ae912e95763..963996a12bb 100644 --- a/iceoryx_utils/test/moduletests/test_cxx_helplets.cpp +++ b/iceoryx_utils/test/moduletests/test_cxx_helplets.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. @@ -17,7 +18,10 @@ #include "iceoryx_utils/cxx/helplets.hpp" #include "test.hpp" +#include + using namespace ::testing; +using namespace iox::cxx; namespace { @@ -80,3 +84,53 @@ TEST_F(Helplets_test, maxAlignment) EXPECT_THAT(alignof(FooBar), Eq(alignof(FuBar))); EXPECT_THAT((iox::cxx::maxAlignment()), Eq(alignof(FooBar))); } + +TEST_F(Helplets_test, bestFittingTypeUsesUint8WhenValueSmaller256) +{ + EXPECT_TRUE((std::is_same, uint8_t>::value)); +} + +TEST_F(Helplets_test, bestFittingTypeUsesUint8WhenValueEqualTo255) +{ + EXPECT_TRUE((std::is_same, uint8_t>::value)); +} + +TEST_F(Helplets_test, bestFittingTypeUsesUint16WhenValueEqualTo256) +{ + EXPECT_TRUE((std::is_same, uint16_t>::value)); +} + +TEST_F(Helplets_test, bestFittingTypeUsesUint16WhenValueBetween256And65535) +{ + EXPECT_TRUE((std::is_same, uint16_t>::value)); +} + +TEST_F(Helplets_test, bestFittingTypeUsesUint16WhenValueEqualTo65535) +{ + EXPECT_TRUE((std::is_same, uint16_t>::value)); +} + +TEST_F(Helplets_test, bestFittingTypeUsesUint32WhenValueEqualTo65536) +{ + EXPECT_TRUE((std::is_same, uint32_t>::value)); +} + +TEST_F(Helplets_test, bestFittingTypeUsesUint32WhenValueBetween2p16And2p32) +{ + EXPECT_TRUE((std::is_same, uint32_t>::value)); +} + +TEST_F(Helplets_test, bestFittingTypeUsesUint32WhenValueEqualTo4294967295) +{ + EXPECT_TRUE((std::is_same, uint32_t>::value)); +} + +TEST_F(Helplets_test, bestFittingTypeUsesUint64WhenValueEqualTo4294967296) +{ + EXPECT_TRUE((std::is_same, uint64_t>::value)); +} + +TEST_F(Helplets_test, bestFittingTypeUsesUint32WhenValueGreater2p32) +{ + EXPECT_TRUE((std::is_same, uint64_t>::value)); +} diff --git a/iceoryx_utils/test/moduletests/test_cxx_type_traits.cpp b/iceoryx_utils/test/moduletests/test_cxx_type_traits.cpp index 8609d1e36e8..5f7e0c2ff80 100644 --- a/iceoryx_utils/test/moduletests/test_cxx_type_traits.cpp +++ b/iceoryx_utils/test/moduletests/test_cxx_type_traits.cpp @@ -59,3 +59,41 @@ TEST(TypeTraitsTest, NotSameIsFalse) auto sut = not_same::value; EXPECT_FALSE(sut); } + +namespace iox +{ +namespace cxx +{ +namespace test +{ +template +struct has_mytype_as_member : std::false_type +{ +}; + +template +struct has_mytype_as_member> : std::true_type +{ +}; +} // namespace test +} // namespace cxx +} // namespace iox + +TEST(TypeTraitsTest, NoTypeAsMemberIsFalse) +{ + struct Sut + { + }; + + EXPECT_FALSE(iox::cxx::test::has_mytype_as_member::value); +} + +TEST(TypeTraitsTest, MyTypeAsMemberIsTrue) +{ + struct Sut + { + using myType = int; + }; + + EXPECT_TRUE(iox::cxx::test::has_mytype_as_member::value); +} diff --git a/iceoryx_utils/testutils/watch_dog.hpp b/iceoryx_utils/testutils/watch_dog.hpp new file mode 100644 index 00000000000..b809b21a0d5 --- /dev/null +++ b/iceoryx_utils/testutils/watch_dog.hpp @@ -0,0 +1,69 @@ +// 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_TESTUTILS_WATCH_DOG_HPP +#define IOX_UTILS_TESTUTILS_WATCH_DOG_HPP + +#include "iceoryx_utils/internal/units/duration.hpp" +#include "iceoryx_utils/posix_wrapper/semaphore.hpp" + +#include +#include +#include + +using namespace iox::units::duration_literals; + +// class for killing the application if a test takes too much time to finish +class Watchdog +{ + public: + Watchdog(const iox::units::Duration& timeToWait) noexcept + : m_timeToWait(timeToWait) + { + } + + ~Watchdog() noexcept + { + if (m_watchdog.joinable()) + { + m_watchdogSemaphore.post(); + m_watchdog.join(); + } + } + + void watchAndActOnFailure(std::function f) noexcept + { + m_watchdog = std::thread([=] { + m_watchdogSemaphore.timedWait(m_timeToWait, false) + .and_then([&](auto& result) { + if (result == iox::posix::SemaphoreWaitState::TIMEOUT) + { + f(); + EXPECT_TRUE(false); + } + }) + .or_else([](auto&) { EXPECT_TRUE(false); }); + }); + } + + private: + iox::units::Duration m_timeToWait{0_s}; + iox::posix::Semaphore m_watchdogSemaphore{ + iox::posix::Semaphore::create(iox::posix::CreateUnnamedSingleProcessSemaphore, 0U).value()}; + std::thread m_watchdog; +}; + +#endif // IOX_UTILS_TESTUTILS_WATCH_DOG_HPP diff --git a/tools/iceoryx_build_test.sh b/tools/iceoryx_build_test.sh index 630f8c61f8d..69188cff302 100755 --- a/tools/iceoryx_build_test.sh +++ b/tools/iceoryx_build_test.sh @@ -49,7 +49,7 @@ EXAMPLE_FLAG="OFF" BUILD_ALL_FLAG="OFF" BUILD_SHARED="OFF" TOML_FLAG="ON" -EXAMPLES="ice_multi_publisher icedelivery singleprocess waitset" +EXAMPLES="callbacks ice_multi_publisher icedelivery singleprocess waitset" COMPONENTS="iceoryx_posh iceoryx_utils iceoryx_introspection iceoryx_binding_c iceoryx_component iceoryx_dds" while (( "$#" )); do diff --git a/tools/introspection/source/introspection_app.cpp b/tools/introspection/source/introspection_app.cpp index 97af0ba3210..1ab55295d5e 100644 --- a/tools/introspection/source/introspection_app.cpp +++ b/tools/introspection/source/introspection_app.cpp @@ -307,12 +307,13 @@ void IntrospectionApp::printPortIntrospectionData(const std::vectorm_caproEventMethodID).c_str()); wprintw(pad, " %s |", printEntry(processNameWidth, publisherPort.portData->m_name).c_str()); wprintw(pad, " %s |", printEntry(nodeNameWidth, publisherPort.portData->m_node).c_str()); - wprintw(pad, " %s |", printEntry(sampleSizeWidth, m_sampleSize).c_str()); - wprintw(pad, " %s |", printEntry(chunkSizeWidth, m_chunkSize).c_str()); - wprintw(pad, " %s |", printEntry(chunksWidth, m_chunksPerMinute).c_str()); - wprintw(pad, " %s |", printEntry(intervalWidth, sendInterval).c_str()); + // uncomment once this information is needed + // wprintw(pad, " %s |", printEntry(sampleSizeWidth, m_sampleSize).c_str()); + // wprintw(pad, " %s |", printEntry(chunkSizeWidth, m_chunkSize).c_str()); + // wprintw(pad, " %s |", printEntry(chunksWidth, m_chunksPerMinute).c_str()); + // wprintw(pad, " %s |", printEntry(intervalWidth, sendInterval).c_str()); wprintw( pad, " %s\n", @@ -415,17 +420,19 @@ void IntrospectionApp::printPortIntrospectionData(const std::vectorm_caproServiceID).c_str()); wprintw(pad, " %s |", printEntry(instanceWidth, subscriber.portData->m_caproInstanceID).c_str()); wprintw(pad, " %s |", printEntry(eventWidth, subscriber.portData->m_caproEventMethodID).c_str()); + wprintw(pad, " %s |", printEntry(processNameWidth, subscriber.portData->m_name).c_str()); wprintw(pad, " %s |", printEntry(nodeNameWidth, subscriber.portData->m_node).c_str()); wprintw(pad, " %s |", printEntry(subscriptionStateWidth, subscriptionStateToString(subscriber.subscriberPortChangingData->subscriptionState)) .c_str()); - if (currentLine == 0) - { - std::string fifoSize{"n/a"}; // std::to_string(subscriber.subscriberPortChangingData->fifoSize)) - std::string fifoCapacity{"n/a"}; // std::to_string(subscriber.subscriberPortChangingData->fifoCapacity)) - wprintw(pad, - " %s / %s |", - printEntry(((fifoWidth / 2) - 1), fifoSize).c_str(), - printEntry(((fifoWidth / 2) - 1), fifoCapacity).c_str()); - } - else - { - wprintw(pad, " %*s |", fifoWidth, ""); - } + // uncomment once this information is needed + // if (currentLine == 0) + //{ + // std::string fifoSize{"n/a"}; // std::to_string(subscriber.subscriberPortChangingData->fifoSize)) + // std::string fifoCapacity{"n/a"}; // std::to_string(subscriber.subscriberPortChangingData->fifoCapacity)) + // wprintw(pad, + //" %s / %s |", + // printEntry(((fifoWidth / 2) - 1), fifoSize).c_str(), + // printEntry(((fifoWidth / 2) - 1), fifoCapacity).c_str()); + //} + // else + //{ + // wprintw(pad, " %*s |", fifoWidth, ""); + //} wprintw(pad, " %s\n", printEntry(scopeWidth, @@ -490,9 +499,10 @@ void IntrospectionApp::printPortIntrospectionData(const std::vector