diff --git a/doc/website/release-notes/iceoryx-v2-0-0.md b/doc/website/release-notes/iceoryx-v2-0-0.md index e9b9c57299..7afcfcb454 100644 --- a/doc/website/release-notes/iceoryx-v2-0-0.md +++ b/doc/website/release-notes/iceoryx-v2-0-0.md @@ -46,7 +46,8 @@ - When posix mutex fails a correct error message is reported on the console [\#999](https://github.com/eclipse-iceoryx/iceoryx/issues/999) - Only use `std::result_of` for C++14 to be able to use iceoryx in C++20 projects [\#1076](https://github.com/eclipse-iceoryx/iceoryx/issues/1076) - Set stack size for windows in `singleprocess` example and posh tests [\#1082](https://github.com/eclipse-iceoryx/iceoryx/issues/1082) -- Roudi console timestamps is out of date [#1130](https://github.com/eclipse-iceoryx/iceoryx/issues/1130) +- Roudi console timestamps are out of date [#1130](https://github.com/eclipse-iceoryx/iceoryx/issues/1130) +- Application can't create publisher repeatedly with previous one already destroyed [\#938](https://github.com/eclipse-iceoryx/iceoryx/issues/938) **Refactoring:** 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 3466b37f56..308ae04780 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/roudi/port_manager.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/roudi/port_manager.hpp @@ -128,12 +128,11 @@ class PortManager void removeEntryFromServiceRegistry(const capro::ServiceDescription& service) noexcept; template ::value>* = nullptr> - cxx::optional - doesViolateCommunicationPolicy(const capro::ServiceDescription& service) const noexcept; + cxx::optional doesViolateCommunicationPolicy(const capro::ServiceDescription& service) noexcept; template ::value>* = nullptr> cxx::optional - doesViolateCommunicationPolicy(const capro::ServiceDescription& service IOX_MAYBE_UNUSED) const noexcept; + doesViolateCommunicationPolicy(const capro::ServiceDescription& service IOX_MAYBE_UNUSED) noexcept; void publishServiceRegistry() const noexcept; diff --git a/iceoryx_posh/include/iceoryx_posh/internal/roudi/port_manager.inl b/iceoryx_posh/include/iceoryx_posh/internal/roudi/port_manager.inl index 0c8ea9fe10..c1f8b51fe3 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/roudi/port_manager.inl +++ b/iceoryx_posh/include/iceoryx_posh/internal/roudi/port_manager.inl @@ -1,4 +1,5 @@ // Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2022 by Apex.AI Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,13 +17,15 @@ #ifndef IOX_POSH_INTERNAL_ROUDI_PORT_MANAGER_INL #define IOX_POSH_INTERNAL_ROUDI_PORT_MANAGER_INL +#include "iceoryx_posh/internal/roudi/port_manager.hpp" + namespace iox { namespace roudi { template ::value>*> inline cxx::optional -PortManager::doesViolateCommunicationPolicy(const capro::ServiceDescription& service) const noexcept +PortManager::doesViolateCommunicationPolicy(const capro::ServiceDescription& service) noexcept { // check if the publisher is already in the list for (auto publisherPortData : m_portPool->getPublisherPortDataList()) @@ -30,6 +33,11 @@ PortManager::doesViolateCommunicationPolicy(const capro::ServiceDescription& ser popo::PublisherPortRouDi publisherPort(publisherPortData); if (service == publisherPort.getCaProServiceDescription()) { + if (publisherPortData->m_toBeDestroyed) + { + destroyPublisherPort(publisherPortData); + continue; + } return cxx::make_optional(publisherPortData->m_runtimeName); } } @@ -38,7 +46,7 @@ PortManager::doesViolateCommunicationPolicy(const capro::ServiceDescription& ser template ::value>*> inline cxx::optional -PortManager::doesViolateCommunicationPolicy(const capro::ServiceDescription& service IOX_MAYBE_UNUSED) const noexcept +PortManager::doesViolateCommunicationPolicy(const capro::ServiceDescription&) noexcept { // Duplicates are allowed when using n:m policy return cxx::nullopt; diff --git a/iceoryx_posh/test/moduletests/test_roudi_portmanager.cpp b/iceoryx_posh/test/moduletests/test_roudi_portmanager.cpp index 17a25f54ab..87127a180a 100644 --- a/iceoryx_posh/test/moduletests/test_roudi_portmanager.cpp +++ b/iceoryx_posh/test/moduletests/test_roudi_portmanager.cpp @@ -442,6 +442,76 @@ TEST_F(PortManager_test, AcquiringOneMoreThanMaximumNumberOfPublishersFails) } } +constexpr bool IS_COMMUNICATION_POLICY_ONE_TO_MANY_ONLY{ + std::is_same::value}; + +TEST_F(PortManager_test, AcquirePublisherPortDataWithSameServiceDescriptionTwiceWorksAccordingCommunicationPolicy) +{ + ::testing::Test::RecordProperty("TEST_ID", "6b26220c-01a3-4f3a-8af4-06c66d6f98ef"); + const iox::capro::ServiceDescription sd{"hyp", "no", "toad"}; + const iox::RuntimeName_t runtimeName{"hypnotoad"}; + auto publisherOptions = createTestPubOptions(); + + // first call must be successful + m_portManager->acquirePublisherPortData(sd, publisherOptions, runtimeName, m_payloadDataSegmentMemoryManager, {}) + .or_else([&](const auto& error) { + GTEST_FAIL() << "Expected ClientPortData but got PortPoolError: " << static_cast(error); + }); + + iox::cxx::optional detectedError; + auto errorHandlerGuard = + iox::ErrorHandler::setTemporaryErrorHandler([&](const auto error, const auto, const auto errorLevel) { + EXPECT_THAT(error, Eq(iox::Error::kPOSH__PORT_MANAGER_PUBLISHERPORT_NOT_UNIQUE)); + EXPECT_THAT(errorLevel, Eq(iox::ErrorLevel::MODERATE)); + detectedError.emplace(error); + }); + + // second call + auto acquirePublisherPortResult = m_portManager->acquirePublisherPortData( + sd, publisherOptions, runtimeName, m_payloadDataSegmentMemoryManager, {}); + + if (IS_COMMUNICATION_POLICY_ONE_TO_MANY_ONLY) + { + ASSERT_TRUE(acquirePublisherPortResult.has_error()); + EXPECT_THAT(acquirePublisherPortResult.get_error(), Eq(PortPoolError::UNIQUE_PUBLISHER_PORT_ALREADY_EXISTS)); + EXPECT_TRUE(detectedError.has_value()); + } + else + { + EXPECT_FALSE(detectedError.has_value()); + } +} + +TEST_F(PortManager_test, + AcquirePublisherPortDataWithSameServiceDescriptionTwiceAndFirstPortMarkedToBeDestroyedReturnsPort) +{ + ::testing::Test::RecordProperty("TEST_ID", "0840c279-5429-4c93-a120-738735a89100"); + const iox::capro::ServiceDescription sd{"hyp", "no", "toad"}; + const iox::RuntimeName_t runtimeName{"hypnotoad"}; + auto publisherOptions = createTestPubOptions(); + + // first call must be successful + auto publisherPortDataResult = m_portManager->acquirePublisherPortData( + sd, publisherOptions, runtimeName, m_payloadDataSegmentMemoryManager, {}); + + ASSERT_FALSE(publisherPortDataResult.has_error()); + + publisherPortDataResult.value()->m_toBeDestroyed = true; + + iox::cxx::optional detectedError; + auto errorHandlerGuard = iox::ErrorHandler::setTemporaryErrorHandler( + [&](const auto error, const auto, const auto) { detectedError.emplace(error); }); + + // second call must now also succeed + m_portManager->acquirePublisherPortData(sd, publisherOptions, runtimeName, m_payloadDataSegmentMemoryManager, {}) + .or_else([&](const auto& error) { + GTEST_FAIL() << "Expected ClientPortData but got PortPoolError: " << static_cast(error); + }); + + detectedError.and_then( + [&](const auto& error) { GTEST_FAIL() << "Expected error handler to not be called but got: " << error; }); +} + TEST_F(PortManager_test, AcquiringOneMoreThanMaximumNumberOfSubscribersFails) { ::testing::Test::RecordProperty("TEST_ID", "5039eff5-f2d1-4f58-8bd2-fc9768a9bc92");