diff --git a/doc/website/advanced/configuration-guide.md b/doc/website/advanced/configuration-guide.md index 47d49687f2..bef12b6fd9 100644 --- a/doc/website/advanced/configuration-guide.md +++ b/doc/website/advanced/configuration-guide.md @@ -184,5 +184,33 @@ it as constructor argument to the RouDi instance. In the cmake file entry of the custom RouDi executable you need to ensure that it is **not** linking against `iceoryx_posh_config` to ensure using the static configuration. -An example of a static config can be found +```cpp +int main(int argc, char* argv[]) +{ + iox::RouDiConfig_t roudiConfig; + + // create mempools + iox::mepoo::MePooConfig mepooConfig; + mepooConfig.addMemPool({128, 10000}); // payload in bytes, chunk count + mepooConfig.addMemPool({265, 10000}); + + auto currentGroup = iox::posix::PosixGroup::getGroupOfCurrentProcess(); + roudiConfig.m_sharedMemorySegments.push_back({currentGroup.getName(), currentGroup.getName(), mepooConfig}); + + // configure the chunk count for the introspection; each introspection topic gets this number of chunks + roudiConfig.introspectionChunkCount = 10; + + // configure the chunk count for the service discovery + roudiConfig.discoveryChunkCount = 10; + + // create a roudi instance + iox::config::CmdLineParserConfigFileOption cmdLineParser; + IceOryxRouDiApp roudi(cmdLineParser.parse(argc, argv).expect("Valid CLI parameter"), roudiConfig); + + // run roudi + return roudi.run(); +} +``` + +A working example of a static config can be found [here](../../../iceoryx_examples/iceperf/roudi_main_static_config.cpp). diff --git a/doc/website/release-notes/iceoryx-unreleased.md b/doc/website/release-notes/iceoryx-unreleased.md index 98eab2f754..c10166e02b 100644 --- a/doc/website/release-notes/iceoryx-unreleased.md +++ b/doc/website/release-notes/iceoryx-unreleased.md @@ -94,6 +94,7 @@ - Fix `expected` is unusable due to `final` [\#1976](https://github.com/eclipse-iceoryx/iceoryx/issues/1976) - MacOS tests that use `EXPECT_DEATH` stuck [#898](https://github.com/eclipse-iceoryx/iceoryx/issues/898) - Remove `EXPECT_DEATH` [#1613](https://github.com/eclipse-iceoryx/iceoryx/issues/1613) +- ServiceDiscovery uses instrospection MemPools [#1359](https://github.com/eclipse-iceoryx/iceoryx/issues/1359) **Refactoring:** @@ -158,6 +159,7 @@ - Move `std::string` dependency from `iox::string` to `std_string_support.hpp` in `iceoryx_dust` [\#1612](https://github.com/eclipse-iceoryx/iceoryx/issues/1612) - Better align `iox::expected` with `std::expected` [\#1969](https://github.com/eclipse-iceoryx/iceoryx/issues/1969) - Use logger for "RouDi is ready for clients" message [\#1994](https://github.com/eclipse-iceoryx/iceoryx/issues/1994) +- Speed up posh tests [#1030](https://github.com/eclipse-iceoryx/iceoryx/issues/1030) **Workflow:** diff --git a/iceoryx_binding_c/test/moduletests/test_chunk.cpp b/iceoryx_binding_c/test/moduletests/test_chunk.cpp index 1bee6d09e7..cc0864db4e 100644 --- a/iceoryx_binding_c/test/moduletests/test_chunk.cpp +++ b/iceoryx_binding_c/test/moduletests/test_chunk.cpp @@ -22,6 +22,7 @@ extern "C" { #include "iceoryx_hoofs/error_handling/error_handling.hpp" #include "iceoryx_hoofs/testing/fatal_failure.hpp" +#include "iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp" #include "iceoryx_posh/testing/roudi_gtest.hpp" namespace @@ -33,6 +34,11 @@ using namespace iox::testing; class Chunk_test : public RouDi_GTest { public: + Chunk_test() + : RouDi_GTest(MinimalRouDiConfigBuilder().create()) + { + } + void SetUp() override { iox_runtime_init("hypnotoad"); diff --git a/iceoryx_binding_c/test/moduletests/test_node.cpp b/iceoryx_binding_c/test/moduletests/test_node.cpp index 7c0627e6c2..7dc156b68f 100644 --- a/iceoryx_binding_c/test/moduletests/test_node.cpp +++ b/iceoryx_binding_c/test/moduletests/test_node.cpp @@ -18,6 +18,7 @@ #include "iceoryx_hoofs/error_handling/error_handling.hpp" #include "iceoryx_hoofs/testing/fatal_failure.hpp" #include "iceoryx_posh/runtime/posh_runtime.hpp" +#include "iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp" #include "iceoryx_posh/testing/roudi_gtest.hpp" #include @@ -35,6 +36,11 @@ using namespace iox::testing; class iox_node_test : public RouDi_GTest { public: + iox_node_test() + : RouDi_GTest(MinimalRouDiConfigBuilder().create()) + { + } + void SetUp() { iox_runtime_init(m_runtimeName.c_str()); diff --git a/iceoryx_binding_c/test/moduletests/test_publisher.cpp b/iceoryx_binding_c/test/moduletests/test_publisher.cpp index 2bcaa2efbd..7b0ddf7f86 100644 --- a/iceoryx_binding_c/test/moduletests/test_publisher.cpp +++ b/iceoryx_binding_c/test/moduletests/test_publisher.cpp @@ -24,6 +24,7 @@ #include "iceoryx_posh/internal/popo/ports/publisher_port_roudi.hpp" #include "iceoryx_posh/internal/popo/ports/publisher_port_user.hpp" #include "iceoryx_posh/mepoo/mepoo_config.hpp" +#include "iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp" #include "iceoryx_posh/testing/roudi_environment/roudi_environment.hpp" using namespace iox; @@ -142,7 +143,7 @@ TEST(iox_pub_test_DeathTest, initPublisherWithNotInitializedPublisherOptionsTerm TEST_F(iox_pub_test, initPublisherWithDefaultOptionsWorks) { ::testing::Test::RecordProperty("TEST_ID", "d2e677cd-2fcc-47a2-80e6-2d08245b7c1a"); - iox::roudi::RouDiEnvironment roudiEnv; + iox::roudi::RouDiEnvironment roudiEnv{MinimalRouDiConfigBuilder().create()}; iox_runtime_init("hypnotoad"); @@ -464,13 +465,8 @@ TEST(iox_pub_options_test, publisherOptionsInitializationCheckReturnsFalseWithou TEST(iox_pub_options_test, publisherOptionInitializationWithNullptrDoesNotCrash) { ::testing::Test::RecordProperty("TEST_ID", "fe415d38-eaaf-466e-b7d8-d220612cb344"); - EXPECT_EXIT( - { - iox_pub_options_init(nullptr); - exit(0); - }, - ::testing::ExitedWithCode(0), - ".*"); + + IOX_EXPECT_NO_FATAL_FAILURE([&] { iox_pub_options_init(nullptr); }); } } // namespace diff --git a/iceoryx_binding_c/test/moduletests/test_runtime.cpp b/iceoryx_binding_c/test/moduletests/test_runtime.cpp index 75ddc4b64d..11fb1ce848 100644 --- a/iceoryx_binding_c/test/moduletests/test_runtime.cpp +++ b/iceoryx_binding_c/test/moduletests/test_runtime.cpp @@ -21,6 +21,7 @@ extern "C" { #include "iceoryx_hoofs/error_handling/error_handling.hpp" #include "iceoryx_hoofs/testing/fatal_failure.hpp" #include "iceoryx_posh/iceoryx_posh_types.hpp" +#include "iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp" #include "iceoryx_posh/testing/roudi_gtest.hpp" namespace @@ -32,6 +33,11 @@ using namespace iox::testing; class BindingC_Runtime_test : public RouDi_GTest { public: + BindingC_Runtime_test() + : RouDi_GTest(MinimalRouDiConfigBuilder().create()) + { + } + void SetUp() { } diff --git a/iceoryx_binding_c/test/moduletests/test_service_discovery.cpp b/iceoryx_binding_c/test/moduletests/test_service_discovery.cpp index 9518ab360f..db97aa233d 100644 --- a/iceoryx_binding_c/test/moduletests/test_service_discovery.cpp +++ b/iceoryx_binding_c/test/moduletests/test_service_discovery.cpp @@ -17,6 +17,7 @@ #include "iceoryx_hoofs/error_handling/error_handling.hpp" #include "iceoryx_hoofs/testing/fatal_failure.hpp" #include "iceoryx_posh/runtime/service_discovery.hpp" +#include "iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp" #include "iceoryx_posh/testing/roudi_gtest.hpp" using namespace iox; @@ -39,6 +40,10 @@ using description_vector = vector([&] { iox_sub_options_init(nullptr); }); } } // namespace diff --git a/iceoryx_examples/callbacks_in_c/ice_c_callbacks_publisher.c b/iceoryx_examples/callbacks_in_c/ice_c_callbacks_publisher.c index 7c7e3080ad..508f765719 100644 --- a/iceoryx_examples/callbacks_in_c/ice_c_callbacks_publisher.c +++ b/iceoryx_examples/callbacks_in_c/ice_c_callbacks_publisher.c @@ -23,13 +23,13 @@ #include #include -bool killswitch = false; +volatile bool keepRunning = true; -static void sigHandler(int f_sig) +static void sigHandler(int sig) { // ignore unused parameter - (void)f_sig; - killswitch = true; + (void)sig; + keepRunning = false; } void sending(void) @@ -47,7 +47,7 @@ void sending(void) iox_pub_t publisherRight = iox_pub_init(&publisherRightStorage, "Radar", "FrontRight", "Counter", &options); struct CounterTopic* userPayload; - for (uint32_t counter = 0U; !killswitch; ++counter) + for (uint32_t counter = 0U; keepRunning; ++counter) { if (counter % 3 == 0) { diff --git a/iceoryx_examples/callbacks_in_c/ice_c_callbacks_subscriber.c b/iceoryx_examples/callbacks_in_c/ice_c_callbacks_subscriber.c index 16112ddcf1..a12c5a274a 100644 --- a/iceoryx_examples/callbacks_in_c/ice_c_callbacks_subscriber.c +++ b/iceoryx_examples/callbacks_in_c/ice_c_callbacks_subscriber.c @@ -31,7 +31,7 @@ #include #include -bool keepRunning = true; +volatile bool keepRunning = true; iox_user_trigger_t heartbeat; diff --git a/iceoryx_examples/callbacks_in_c/ice_c_callbacks_with_context_data.c b/iceoryx_examples/callbacks_in_c/ice_c_callbacks_with_context_data.c index 0ba83b09a8..1bb66f4c71 100644 --- a/iceoryx_examples/callbacks_in_c/ice_c_callbacks_with_context_data.c +++ b/iceoryx_examples/callbacks_in_c/ice_c_callbacks_with_context_data.c @@ -27,7 +27,7 @@ #include #include -bool keepRunning = true; +volatile bool keepRunning = true; static void sigHandler(int signalValue) { diff --git a/iceoryx_examples/icedelivery_in_c/README.md b/iceoryx_examples/icedelivery_in_c/README.md index 7087f53978..fb10b4f906 100644 --- a/iceoryx_examples/icedelivery_in_c/README.md +++ b/iceoryx_examples/icedelivery_in_c/README.md @@ -60,11 +60,11 @@ iox_sub_t subscriber = iox_sub_init(&subscriberStorage, "Radar", "FrontLeft", "O ``` 3. We receive samples in a loop and print the received data on the console as - long as the `killswitch` is not set to `true` by an external signal. + long as the `keepRunning` is not set to `false` by an external signal. ```c -while (!killswitch) +while (keepRunning) { if (SubscribeState_SUBSCRIBED == iox_sub_get_subscription_state(subscriber)) { @@ -132,7 +132,7 @@ iox_pub_storage_t publisherStorage; iox_pub_t publisher = iox_pub_init(&publisherStorage, "Radar", "FrontLeft", "Object", &options); ``` - 3. Until an external signal sets `killswitch` to `true`, we will send an + 3. Until an external signal sets `keepRunning` to `false`, we will send an incrementing number to all subscribers in every iteration and print the value of that number to the console. @@ -140,7 +140,7 @@ iox_pub_t publisher = iox_pub_init(&publisherStorage, "Radar", "FrontLeft", "Obj ```c double ct = 0.0; -while (!killswitch) +while (keepRunning) { void* userPayload = NULL; if (AllocationResult_SUCCESS == iox_pub_loan_chunk(publisher, &userPayload, sizeof(struct RadarObject))) diff --git a/iceoryx_examples/icedelivery_in_c/ice_c_publisher.c b/iceoryx_examples/icedelivery_in_c/ice_c_publisher.c index b292482085..848a024f03 100644 --- a/iceoryx_examples/icedelivery_in_c/ice_c_publisher.c +++ b/iceoryx_examples/icedelivery_in_c/ice_c_publisher.c @@ -24,14 +24,14 @@ #include #include -bool killswitch = false; +volatile bool keepRunning = true; static void sigHandler(int signalValue) { // Ignore unused variable warning (void)signalValue; // caught SIGINT or SIGTERM, now exit gracefully - killswitch = true; + keepRunning = false; } void sending(void) @@ -53,7 +53,7 @@ void sending(void) //! [send and print number] double ct = 0.0; - while (!killswitch) + while (keepRunning) { void* userPayload = NULL; if (AllocationResult_SUCCESS == iox_pub_loan_chunk(publisher, &userPayload, sizeof(struct RadarObject))) diff --git a/iceoryx_examples/icedelivery_in_c/ice_c_subscriber.c b/iceoryx_examples/icedelivery_in_c/ice_c_subscriber.c index 190a7f07a7..56f5df3cf5 100644 --- a/iceoryx_examples/icedelivery_in_c/ice_c_subscriber.c +++ b/iceoryx_examples/icedelivery_in_c/ice_c_subscriber.c @@ -25,14 +25,14 @@ #include #include -bool killswitch = false; +volatile bool keepRunning = true; static void sigHandler(int signalValue) { // Ignore unused variable warning (void)signalValue; // caught SIGINT or SIGTERM, now exit gracefully - killswitch = true; + keepRunning = false; } void receiving(void) @@ -57,7 +57,7 @@ void receiving(void) //! [create subscriber port] //! [receive and print data] - while (!killswitch) + while (keepRunning) { if (SubscribeState_SUBSCRIBED == iox_sub_get_subscription_state(subscriber)) { diff --git a/iceoryx_examples/icediscovery/README.md b/iceoryx_examples/icediscovery/README.md index 07eda847d3..c904037e99 100644 --- a/iceoryx_examples/icediscovery/README.md +++ b/iceoryx_examples/icediscovery/README.md @@ -177,9 +177,10 @@ Should the service we wait for never become available we can unblock any of the ```cpp -if (discoveryPtr) +keepRunning = false; +if (discoverySigHandlerAccess) { - discoveryPtr->unblockWait(); + discoverySigHandlerAccess->unblockWait(); } ``` @@ -343,7 +344,7 @@ It is also possible to unblock any of the waits even if nothing changes or the c ```cpp -void Discovery::unblockWait() +void Discovery::unblockWait() volatile noexcept { m_blocking = false; // could also unblock with a dedicated condition to unblock the wait but that requires more code diff --git a/iceoryx_examples/icediscovery/include/discovery_blocking.hpp b/iceoryx_examples/icediscovery/include/discovery_blocking.hpp index dde4897706..8e8b866b7a 100644 --- a/iceoryx_examples/icediscovery/include/discovery_blocking.hpp +++ b/iceoryx_examples/icediscovery/include/discovery_blocking.hpp @@ -48,7 +48,7 @@ class Discovery /// @brief unblock any wait /// @note not reversible, i.e. after this call no further waiting with e.g. waitUntil is possible - void unblockWait(); + void unblockWait() volatile noexcept; /// @brief get all services matching a findService query /// @return ServiceContainer, containing the found services diff --git a/iceoryx_examples/icediscovery/iox_discovery_monitor.cpp b/iceoryx_examples/icediscovery/iox_discovery_monitor.cpp index abc9437610..88fbbdb939 100644 --- a/iceoryx_examples/icediscovery/iox_discovery_monitor.cpp +++ b/iceoryx_examples/icediscovery/iox_discovery_monitor.cpp @@ -80,6 +80,8 @@ int main() { // here the app would run its functional code while the // service availability is monitored in the background + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } //! [deregister callback] diff --git a/iceoryx_examples/icediscovery/iox_wait_for_service.cpp b/iceoryx_examples/icediscovery/iox_wait_for_service.cpp index b3a77681e2..adde0acfa7 100644 --- a/iceoryx_examples/icediscovery/iox_wait_for_service.cpp +++ b/iceoryx_examples/icediscovery/iox_wait_for_service.cpp @@ -27,6 +27,8 @@ using namespace discovery; constexpr char APP_NAME[] = "iox-wait-for-service"; +volatile bool keepRunning{true}; + //! [service to wait for] iox::capro::IdString_t service{"Camera"}; iox::capro::IdString_t instance{"FrontLeft"}; @@ -44,14 +46,15 @@ void printSearchResult(const discovery::ServiceContainer& result) } } -Discovery* discoveryPtr{nullptr}; +volatile Discovery* discoverySigHandlerAccess{nullptr}; void sigHandler(int) { //! [unblock wait] - if (discoveryPtr) + keepRunning = false; + if (discoverySigHandlerAccess) { - discoveryPtr->unblockWait(); + discoverySigHandlerAccess->unblockWait(); } //! [unblock wait] } @@ -65,7 +68,7 @@ int main() Discovery discovery; //! [create custom discovery] - discoveryPtr = &discovery; + discoverySigHandlerAccess = &discovery; auto sigTermGuard = iox::posix::registerSignalHandler(iox::posix::Signal::TERM, sigHandler).expect("failed to register SIGTERM"); @@ -116,11 +119,13 @@ int main() << std::endl; // loop while the service is available (e.g. perform some computation etc.) - } while (true); + } while (keepRunning); std::cout << APP_NAME << " <" << service << ", " << instance << ", " << event << "> was unavailable" << std::endl; } + discoverySigHandlerAccess = nullptr; // invalidate for signal handler + return (EXIT_SUCCESS); } diff --git a/iceoryx_examples/icediscovery/src/discovery_blocking.cpp b/iceoryx_examples/icediscovery/src/discovery_blocking.cpp index b9611bef71..55cb92f562 100644 --- a/iceoryx_examples/icediscovery/src/discovery_blocking.cpp +++ b/iceoryx_examples/icediscovery/src/discovery_blocking.cpp @@ -58,7 +58,7 @@ void Discovery::waitUntilChange() //! [wait until change] //! [unblock wait] -void Discovery::unblockWait() +void Discovery::unblockWait() volatile noexcept { m_blocking = false; // could also unblock with a dedicated condition to unblock the wait but that requires more code diff --git a/iceoryx_examples/icediscovery_in_c/iox_c_find_service.c b/iceoryx_examples/icediscovery_in_c/iox_c_find_service.c index 0aae2e0bb2..bf22ac35ad 100644 --- a/iceoryx_examples/icediscovery_in_c/iox_c_find_service.c +++ b/iceoryx_examples/icediscovery_in_c/iox_c_find_service.c @@ -27,7 +27,7 @@ #define SEARCH_RESULT_CAPACITY 10 -bool keepRunning = true; +volatile bool keepRunning = true; const char APP_NAME[] = "iox-c-find-service"; diff --git a/iceoryx_examples/icediscovery_in_c/iox_c_offer_service.c b/iceoryx_examples/icediscovery_in_c/iox_c_offer_service.c index f1d7d3b5c5..3ae207676b 100644 --- a/iceoryx_examples/icediscovery_in_c/iox_c_offer_service.c +++ b/iceoryx_examples/icediscovery_in_c/iox_c_offer_service.c @@ -23,7 +23,7 @@ #define NUMBER_OF_CAMERA_PUBLISHERS 5 -bool keepRunning = true; +volatile bool keepRunning = true; const char APP_NAME[] = "iox-c-offer-service"; diff --git a/iceoryx_examples/iceperf/README.md b/iceoryx_examples/iceperf/README.md index 18bc4da682..7164abd711 100644 --- a/iceoryx_examples/iceperf/README.md +++ b/iceoryx_examples/iceperf/README.md @@ -241,21 +241,60 @@ void IcePerfLeader::doMeasurement(IcePerfBase& ipcTechnology) noexcept { ipcTechnology.initLeader(); + auto humanReadableMemorySize = [](const uint64_t memorySize) { + constexpr const uint64_t UNIT_DIVIDER{1024}; + auto humanReadalbeMemorySize = memorySize; + for (const auto& unit : {iox::string<2>("B"), + iox::string<2>("kB"), + iox::string<2>("MB"), + iox::string<2>("GB"), + iox::string<2>("TB")}) + { + if (humanReadalbeMemorySize >= UNIT_DIVIDER) + { + humanReadalbeMemorySize /= UNIT_DIVIDER; + continue; + } + return std::make_tuple(humanReadalbeMemorySize, unit); + } + return (std::make_tuple(memorySize, iox::string<2>("B"))); + }; + std::vector> latencyMeasurements; - const std::vector payloadSizesInKB{1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096}; + const std::vector payloadSizes{16, + 32, + 64, + 128, + 256, + 512, + 1 * IcePerfBase::ONE_KILOBYTE, + 2 * IcePerfBase::ONE_KILOBYTE, + 4 * IcePerfBase::ONE_KILOBYTE, + 8 * IcePerfBase::ONE_KILOBYTE, + 16 * IcePerfBase::ONE_KILOBYTE, + 32 * IcePerfBase::ONE_KILOBYTE, + 64 * IcePerfBase::ONE_KILOBYTE, + 128 * IcePerfBase::ONE_KILOBYTE, + 256 * IcePerfBase::ONE_KILOBYTE, + 512 * IcePerfBase::ONE_KILOBYTE, + 1024 * IcePerfBase::ONE_KILOBYTE, + 2048 * IcePerfBase::ONE_KILOBYTE, + 4096 * IcePerfBase::ONE_KILOBYTE}; std::cout << "Measurement for:"; const char* separator = " "; - for (const auto payloadSizeInKB : payloadSizesInKB) + for (const auto payloadSize : payloadSizes) { - std::cout << separator << payloadSizeInKB << " kB" << std::flush; + uint64_t humanReadablePayloadSize{0}; + iox::string<2> memorySizeUnit{}; + std::tie(humanReadablePayloadSize, memorySizeUnit) = humanReadableMemorySize(payloadSize); + std::cout << separator << humanReadablePayloadSize << " [" << memorySizeUnit << "]" << std::flush; separator = ", "; - auto payloadSizeInBytes = payloadSizeInKB * IcePerfBase::ONE_KILOBYTE; - ipcTechnology.preLatencyPerfTestLeader(payloadSizeInBytes); + ipcTechnology.preLatencyPerfTestLeader(payloadSize); auto latency = ipcTechnology.latencyPerfTestLeader(m_settings.numberOfSamples); - latencyMeasurements.push_back(std::make_tuple(payloadSizeInKB, latency)); + latencyMeasurements.push_back(std::make_tuple(payloadSize, latency)); ipcTechnology.postLatencyPerfTestLeader(); } @@ -269,14 +308,20 @@ void IcePerfLeader::doMeasurement(IcePerfBase& ipcTechnology) noexcept std::cout << "#### Measurement Result ####" << std::endl; std::cout << m_settings.numberOfSamples << " round trips for each payload." << std::endl; std::cout << std::endl; - std::cout << "| Payload Size [kB] | Average Latency [µs] |" << std::endl; - std::cout << "|------------------:|---------------------:|" << std::endl; + std::cout << "| Payload Size | Average Latency [µs] |" << std::endl; + std::cout << "|-------------:|---------------------:|" << std::endl; for (const auto& latencyMeasuement : latencyMeasurements) { - auto payloadSizeInKB = std::get<0>(latencyMeasuement); + uint64_t humanReadablePayloadSize{0}; + iox::string<2> memorySizeUnit{}; + std::tie(humanReadablePayloadSize, memorySizeUnit) = humanReadableMemorySize(std::get<0>(latencyMeasuement)); auto latencyInMicroseconds = static_cast(std::get<1>(latencyMeasuement).toNanoseconds()) / 1000.0; - std::cout << "| " << std::setw(17) << payloadSizeInKB << " | " << std::setw(20) << std::setprecision(2) - << latencyInMicroseconds << " |" << std::endl; + iox::string<10> unitString{"["}; + unitString.append(iox::TruncateToCapacity, memorySizeUnit); + unitString.append(iox::TruncateToCapacity, "]"); + std::cout << "| " << std::setw(7) << humanReadablePayloadSize << " " << std::setw(4) << std::left << unitString + << std::right << " | " << std::setw(20) << std::setprecision(2) << latencyInMicroseconds << " |" + << std::endl; } std::cout << std::endl; @@ -360,6 +405,13 @@ int IcePerfLeader::run() noexcept doMeasurement(iceoryxc); } + if (m_settings.technology == Technology::ALL || m_settings.technology == Technology::ICEORYX_CPP_WAIT_API) + { + std::cout << std::endl << "****** ICEORYX WAITSET ********" << std::endl; + IceoryxWait iceoryxwait(PUBLISHER, SUBSCRIBER); + doMeasurement(iceoryxwait); + } + return EXIT_SUCCESS; } ``` diff --git a/iceoryx_examples/iceperf/roudi_main_static_config.cpp b/iceoryx_examples/iceperf/roudi_main_static_config.cpp index 05615b0b81..9eb1f8e9d9 100644 --- a/iceoryx_examples/iceperf/roudi_main_static_config.cpp +++ b/iceoryx_examples/iceperf/roudi_main_static_config.cpp @@ -68,6 +68,12 @@ int main(int argc, char* argv[]) /// mempoolConfig}) /// @endcode + /// configure the chunk count for the introspection; each introspection topic gets this number of chunks + roudiConfig.introspectionChunkCount = 10; + + /// configure the chunk count for the service discovery + roudiConfig.discoveryChunkCount = 10; + IceOryxRouDiApp roudi(cmdLineArgs.value(), roudiConfig); return roudi.run(); diff --git a/iceoryx_examples/request_response/README.md b/iceoryx_examples/request_response/README.md index 4269305596..4924738a24 100644 --- a/iceoryx_examples/request_response/README.md +++ b/iceoryx_examples/request_response/README.md @@ -35,8 +35,8 @@ At first, the includes for the client port, request-response types, WaitSet, and ```cpp #include "request_and_response_types.hpp" -#include "iceoryx_hoofs/posix_wrapper/signal_handler.hpp" #include "iceoryx_dust/posix_wrapper/signal_watcher.hpp" +#include "iceoryx_hoofs/posix_wrapper/signal_handler.hpp" #include "iceoryx_posh/popo/client.hpp" #include "iceoryx_posh/popo/wait_set.hpp" #include "iceoryx_posh/runtime/posh_runtime.hpp" @@ -71,14 +71,15 @@ full or the server is too slow. The `ClientOptions` are similar to `PublisherOpt ```cpp -waitset.emplace(); +WaitSet waitset; +waitsetSigHandlerAccess = &waitset; iox::popo::ClientOptions options; options.responseQueueCapacity = 2U; iox::popo::Client client({"Example", "Request-Response", "Add"}, options); // attach client to waitset -waitset->attachState(client, iox::popo::ClientState::HAS_RESPONSE).or_else([](auto) { +waitset.attachState(client, iox::popo::ClientState::HAS_RESPONSE).or_else([](auto) { std::cerr << "failed to attach client" << std::endl; std::exit(EXIT_FAILURE); }); @@ -132,7 +133,7 @@ Once the request has been sent, we block and wait for samples to arrive. Then we iterate over the notification vector to check if we were triggered from our client: ```cpp -auto notificationVector = waitset->timedWait(iox::units::Duration::fromSeconds(5)); +auto notificationVector = waitset.timedWait(iox::units::Duration::fromSeconds(5)); for (auto& notification : notificationVector) { diff --git a/iceoryx_examples/request_response/client_cxx_waitset.cpp b/iceoryx_examples/request_response/client_cxx_waitset.cpp index edfe49f629..1490699007 100644 --- a/iceoryx_examples/request_response/client_cxx_waitset.cpp +++ b/iceoryx_examples/request_response/client_cxx_waitset.cpp @@ -28,8 +28,10 @@ #include constexpr char APP_NAME[] = "iox-cpp-request-response-client-waitset"; -std::atomic_bool keepRunning = {true}; -iox::optional> waitset; +volatile bool keepRunning = {true}; + +using WaitSet = iox::popo::WaitSet<>; +volatile WaitSet* waitsetSigHandlerAccess{nullptr}; //! [context data to store Fibonacci numbers and sequence ids] struct ContextData @@ -41,10 +43,13 @@ struct ContextData }; //! [context data to store Fibonacci numbers and sequence ids] -void signalHandler(int) +static void signalHandler(int sig IOX_MAYBE_UNUSED) { keepRunning = false; - waitset.and_then([&](auto& w) { w.markForDestruction(); }); + if (waitsetSigHandlerAccess) + { + waitsetSigHandlerAccess->markForDestruction(); + } } int main() @@ -61,7 +66,8 @@ int main() ContextData ctx; //! [create waitset] - waitset.emplace(); + WaitSet waitset; + waitsetSigHandlerAccess = &waitset; //! [create client] iox::popo::ClientOptions options; @@ -70,7 +76,7 @@ int main() //! [create client] // attach client to waitset - waitset->attachState(client, iox::popo::ClientState::HAS_RESPONSE).or_else([](auto) { + waitset.attachState(client, iox::popo::ClientState::HAS_RESPONSE).or_else([](auto) { std::cerr << "failed to attach client" << std::endl; std::exit(EXIT_FAILURE); }); @@ -98,7 +104,7 @@ int main() // We block and wait for samples to arrive, when the time is up we send the request again //! [wait and check if the client triggered] - auto notificationVector = waitset->timedWait(iox::units::Duration::fromSeconds(5)); + auto notificationVector = waitset.timedWait(iox::units::Duration::fromSeconds(5)); for (auto& notification : notificationVector) { @@ -131,8 +137,9 @@ int main() } //! [mainloop] - waitset.reset(); std::cout << "shutting down" << std::endl; + waitsetSigHandlerAccess = nullptr; // invalidate for signal handler + return (EXIT_SUCCESS); } diff --git a/iceoryx_examples/request_response_in_c/README.md b/iceoryx_examples/request_response_in_c/README.md index 0958f888e1..4259a29502 100644 --- a/iceoryx_examples/request_response_in_c/README.md +++ b/iceoryx_examples/request_response_in_c/README.md @@ -33,6 +33,19 @@ signal(SIGTERM, sigHandler); iox_runtime_init(APP_NAME); ``` +The signal handler clears a flag to initiate a graceful shutdown. + + +```c +volatile bool keepRunning = true; + +void sigHandler(int signalValue) +{ + (void)signalValue; + keepRunning = false; +} +``` + We continue with initializing our `client` to send requests to the server. First of all, we need some memory in which the client can be stored called `clientStorage`. `iox_client_init` will create an object in this memory location, sets the service @@ -155,11 +168,32 @@ or when you would like to know more about the listener, see the The startup phase is identical to the client basic version, we register the signal handlers, initialize the runtime, create a client and initialize our variables. + +This time the signal handler needs to wake up the waitset additionally. + +```c +volatile bool keepRunning = true; + +volatile iox_ws_t waitsetSigHandlerAccess = NULL; + +void sigHandler(int signalValue) +{ + (void)signalValue; + keepRunning = false; + if (waitsetSigHandlerAccess) + { + iox_ws_mark_for_destruction(waitsetSigHandlerAccess); + } +} +``` + Afterwards we create our waitset and attach the client state `ClientState_HAS_RESPONSE` to it. ```c -waitset = iox_ws_init(&waitsetStorage); +iox_ws_storage_t waitsetStorage; +iox_ws_t waitset = iox_ws_init(&waitsetStorage); +waitsetSigHandlerAccess = waitset; if (iox_ws_attach_client_state(waitset, client, ClientState_HAS_RESPONSE, 0U, NULL) != WaitSetResult_SUCCESS) { @@ -226,6 +260,7 @@ the waitset first and then deinitialize the waitset and the client. ```c iox_ws_detach_client_state(waitset, client, ClientState_HAS_RESPONSE); +waitsetSigHandlerAccess = NULL; // invalidate for signal handler iox_ws_deinit(waitset); iox_client_deinit(client); ``` diff --git a/iceoryx_examples/request_response_in_c/client_c_basic.c b/iceoryx_examples/request_response_in_c/client_c_basic.c index 25fb6e0f06..68277caa68 100644 --- a/iceoryx_examples/request_response_in_c/client_c_basic.c +++ b/iceoryx_examples/request_response_in_c/client_c_basic.c @@ -26,14 +26,17 @@ #include #include -bool keepRunning = true; const char APP_NAME[] = "iox-c-request-response-client-basic"; +//! [signal handler] +volatile bool keepRunning = true; + void sigHandler(int signalValue) { (void)signalValue; keepRunning = false; } +//! [signal handler] int main(void) { diff --git a/iceoryx_examples/request_response_in_c/client_c_waitset.c b/iceoryx_examples/request_response_in_c/client_c_waitset.c index 309297a644..f37f0a8195 100644 --- a/iceoryx_examples/request_response_in_c/client_c_waitset.c +++ b/iceoryx_examples/request_response_in_c/client_c_waitset.c @@ -34,18 +34,23 @@ #define NUMBER_OF_NOTIFICATIONS 1 -bool keepRunning = true; const char APP_NAME[] = "iox-c-request-response-client-waitset"; -iox_ws_t waitset; -iox_ws_storage_t waitsetStorage; +//! [signal handler] +volatile bool keepRunning = true; + +volatile iox_ws_t waitsetSigHandlerAccess = NULL; void sigHandler(int signalValue) { (void)signalValue; keepRunning = false; - iox_ws_mark_for_destruction(waitset); + if (waitsetSigHandlerAccess) + { + iox_ws_mark_for_destruction(waitsetSigHandlerAccess); + } } +//! [signal handler] int main(void) { @@ -63,7 +68,9 @@ int main(void) int64_t expectedResponseSequenceId = requestSequenceId; //! [create waitset and attach client] - waitset = iox_ws_init(&waitsetStorage); + iox_ws_storage_t waitsetStorage; + iox_ws_t waitset = iox_ws_init(&waitsetStorage); + waitsetSigHandlerAccess = waitset; if (iox_ws_attach_client_state(waitset, client, ClientState_HAS_RESPONSE, 0U, NULL) != WaitSetResult_SUCCESS) { @@ -146,6 +153,7 @@ int main(void) //! [cleanup] iox_ws_detach_client_state(waitset, client, ClientState_HAS_RESPONSE); + waitsetSigHandlerAccess = NULL; // invalidate for signal handler iox_ws_deinit(waitset); iox_client_deinit(client); //! [cleanup] diff --git a/iceoryx_examples/request_response_in_c/server_c_basic.c b/iceoryx_examples/request_response_in_c/server_c_basic.c index 7ff2fe30f2..6b10d39e0f 100644 --- a/iceoryx_examples/request_response_in_c/server_c_basic.c +++ b/iceoryx_examples/request_response_in_c/server_c_basic.c @@ -26,7 +26,7 @@ #include #include -bool keepRunning = true; +volatile bool keepRunning = true; const char APP_NAME[] = "iox-c-request-response-server-basic"; void sigHandler(int signalValue) diff --git a/iceoryx_examples/request_response_in_c/server_c_listener.c b/iceoryx_examples/request_response_in_c/server_c_listener.c index 7bf482435b..b0f943739b 100644 --- a/iceoryx_examples/request_response_in_c/server_c_listener.c +++ b/iceoryx_examples/request_response_in_c/server_c_listener.c @@ -27,7 +27,7 @@ #include #include -bool keepRunning = true; +volatile bool keepRunning = true; const char APP_NAME[] = "iox-c-request-response-server-listener"; void sigHandler(int signalValue) diff --git a/iceoryx_examples/user_header/publisher_c_api.c b/iceoryx_examples/user_header/publisher_c_api.c index ea2209ac67..5177d02354 100644 --- a/iceoryx_examples/user_header/publisher_c_api.c +++ b/iceoryx_examples/user_header/publisher_c_api.c @@ -29,20 +29,14 @@ #include //! [signal handling] -#ifdef _WIN32 -/// @todo iox-#33 needs proper fix but it seems MSVC doesn't have stdatomic.h -volatile bool killswitch = false; -#else -#include -atomic_bool killswitch = false; -#endif +volatile bool keepRunning = true; static void sigHandler(int signalValue) { // Ignore unused variable warning (void)signalValue; // caught SIGINT or SIGTERM, now exit gracefully - killswitch = true; + keepRunning = false; } //! [signal handling] @@ -67,7 +61,7 @@ int main(void) uint64_t timestamp = 37; uint64_t fibonacciLast = 0; uint64_t fibonacciCurrent = 1; - while (!killswitch) + while (keepRunning) { uint64_t fibonacciNext = fibonacciCurrent + fibonacciLast; fibonacciLast = fibonacciCurrent; diff --git a/iceoryx_examples/user_header/subscriber_c_api.c b/iceoryx_examples/user_header/subscriber_c_api.c index 29c19fd14c..77318ed51e 100644 --- a/iceoryx_examples/user_header/subscriber_c_api.c +++ b/iceoryx_examples/user_header/subscriber_c_api.c @@ -27,20 +27,14 @@ #include //! [signal handling] -#ifdef _WIN32 -/// @todo iox-#33 needs proper fix but it seems MSVC doesn't have stdatomic.h -volatile bool killswitch = false; -#else -#include -atomic_bool killswitch = false; -#endif +volatile bool keepRunning = true; static void sigHandler(int signalValue) { // Ignore unused variable warning (void)signalValue; // caught SIGINT or SIGTERM, now exit gracefully - killswitch = true; + keepRunning = false; } //! [signal handling] @@ -62,7 +56,7 @@ int main(void) //! [create subscriber] //! [poll subscriber for samples in a loop] - while (!killswitch) + while (keepRunning) { //! [take chunk] const void* userPayload; diff --git a/iceoryx_examples/waitset/README.md b/iceoryx_examples/waitset/README.md index 6c4f780855..9b2b1f13df 100644 --- a/iceoryx_examples/waitset/README.md +++ b/iceoryx_examples/waitset/README.md @@ -158,15 +158,17 @@ which wakes up the blocking `waitset->wait()` whenever Ctrl+C is pressed. ```cpp -std::atomic_bool keepRunning{true}; -iox::optional> waitset; +volatile bool keepRunning{true}; + +using WaitSet = iox::popo::WaitSet<>; +volatile WaitSet* waitsetSigHandlerAccess{nullptr}; static void sigHandler(int sig IOX_MAYBE_UNUSED) { keepRunning = false; - if (waitset) + if (waitsetSigHandlerAccess) { - waitset->markForDestruction(); + waitsetSigHandlerAccess->markForDestruction(); } } ``` @@ -176,23 +178,18 @@ In the beginning we create the WaitSet. It is important to construct it only aft Afterwards we register our signal handler which will unblock the WaitSet. Finally we attach the subscriber to the WaitSet stating that we want to be notified when it has data (indicated by `iox::popo::SubscriberState::HAS_DATA`). It is good practice to handle potential failure while attaching, otherwise warnings will emerge since the return value `expected` is marked to require handling. -In our case no errors should occur since the WaitSet can accomodate the two triggers we want to attach. +In our case no errors should occur since the WaitSet can accommodate the two triggers we want to attach. ```cpp -waitset.emplace(); - -// register signal handler to handle termination of the loop -auto signalGuard = - iox::posix::registerSignalHandler(iox::posix::Signal::INT, sigHandler).expect("failed to register SIGINT"); -auto signalTermGuard = - iox::posix::registerSignalHandler(iox::posix::Signal::TERM, sigHandler).expect("failed to register SIGTERM"); +WaitSet waitset; +waitsetSigHandlerAccess = &waitset; // create subscriber iox::popo::Subscriber subscriber({"Radar", "FrontLeft", "Counter"}); // attach subscriber to waitset -waitset->attachState(subscriber, iox::popo::SubscriberState::HAS_DATA).or_else([](auto) { +waitset.attachState(subscriber, iox::popo::SubscriberState::HAS_DATA).or_else([](auto) { std::cerr << "failed to attach subscriber" << std::endl; std::exit(EXIT_FAILURE); }); @@ -208,7 +205,7 @@ not wait indefinitely. while (keepRunning) { // We block and wait for samples to arrive. - auto notificationVector = waitset->wait(); + auto notificationVector = waitset.wait(); for (auto& notification : notificationVector) { @@ -290,29 +287,20 @@ In our example the _NotificationOrigin_ is a samples. When `take()` was successful we print our message to the console inside of the `and_then` lambda. -The `shutdownTrigger` uses a simpler callback which just informs us that we are -exiting the program. Therefore we do not need an additional `ContextDataType` pointer. - +To save some mileage on the keyboard, a type alias for _WaitSet_ with the storage capacity +for 2 events is defined. + + ```cpp -void shutdownCallback(iox::popo::UserTrigger*) -{ - std::cout << "CTRL+C pressed - exiting now" << std::endl; -} +using WaitSet = iox::popo::WaitSet; ``` -In our `main` function we create a _WaitSet_ which has storage capacity for 3 events, -2 subscribers and one shutdown trigger, after we registered us at our central -broker RouDi. Then we attach our `shutdownTrigger` to handle `CTRL+C` events. +In our `main` function we create the _WaitSet_. ```cpp -iox::popo::WaitSet waitset; - -// attach shutdownTrigger to handle CTRL+C -waitset.attachEvent(shutdownTrigger, iox::popo::createNotificationCallback(shutdownCallback)).or_else([](auto) { - std::cerr << "failed to attach shutdown trigger" << std::endl; - std::exit(EXIT_FAILURE); -}); +WaitSet waitset; +waitsetSigHandlerAccess = &waitset; ``` After that we define our `sumOfAllSamples` variable and create a vector to hold our subscribers. We create and then @@ -359,8 +347,7 @@ one or more events triggered the _WaitSet_. After the call returned we get a vector filled with _NotificationInfos_ which are corresponding to all the events which triggered the _WaitSet_. -We iterate through this vector. If an _Event_ originated from the `shutdownTrigger` -we exit the program, otherwise we just call the assigned callback by calling +We iterate through this vector and call the assigned callback by calling the trigger. This will then call the `subscriberCallback` with the _NotificationOrigin_ (the pointer to the untyped subscriber) and the contextData (`sumOfAllSamples`) as parameters. @@ -372,16 +359,8 @@ while (keepRunning) for (auto& notification : notificationVector) { - if (notification->doesOriginateFrom(&shutdownTrigger)) - { - (*notification)(); - keepRunning = false; - } - else - { - // call the callback which was assigned to the notification - (*notification)(); - } + // call the callback which was assigned to the notification + (*notification)(); } auto flags = std::cout.flags(); @@ -397,18 +376,12 @@ and we do not want to attach a callback to them. Instead we perform the calls on subscribers directly. Additionally, we would like to be notified as long as there are samples in the subscriber queue therefore we have to attach the `SubscriberState::HAS_DATA`. -We again start by creating a _WaitSet_ with a capacity of 5 (4 subscribers and 1 shutdownTrigger), -and attach the `shutdownTrigger` to handle `CTRL+C`. +We again start by creating a _WaitSet_. ```cpp -iox::popo::WaitSet waitset; - -// attach shutdownTrigger to handle CTRL+C -waitset.attachEvent(shutdownTrigger).or_else([](auto) { - std::cerr << "failed to attach shutdown trigger" << std::endl; - std::exit(EXIT_FAILURE); -}); +WaitSet waitset; +waitsetSigHandlerAccess = &waitset; ``` Now we create a vector of 4 subscribers. @@ -450,26 +423,6 @@ for (auto i = NUMBER_OF_SUBSCRIBERS / 2; i < NUMBER_OF_SUBSCRIBERS; ++i) The event loop calls `auto notificationVector = waitset.wait()` in a blocking call to receive a vector of all the _NotificationInfos_ which are corresponding to the occurred events. -If the _Event_ originated from the `shutdownTrigger` we terminate the program. - - -```cpp -while (keepRunning) -{ - auto notificationVector = waitset.wait(); - - for (auto& notification : notificationVector) - { - if (notification->doesOriginateFrom(&shutdownTrigger)) - { - keepRunning = false; - } - // ... - } - - std::cout << std::endl; -} -``` The remaining part of the loop is handling the subscribers. In the first group we would like to print the received data to the console and in the second group @@ -478,7 +431,7 @@ we just dismiss the received data. ```cpp // we print the received data for the first group -else if (notification->getNotificationId() == FIRST_GROUP_ID) +if (notification->getNotificationId() == FIRST_GROUP_ID) { auto subscriber = notification->getOrigin(); subscriber->take().and_then([&](auto& userPayload) { @@ -495,7 +448,7 @@ else if (notification->getNotificationId() == SECOND_GROUP_ID) std::cout << "dismiss data\n"; auto subscriber = notification->getOrigin(); // We need to release the data to reset the trigger hasData - // otherwise the WaitSet would notify us in `waitset.wait()` again + // otherwise the WaitSet would notify us in 'waitset.wait()' again // instantly. subscriber->releaseQueuedData(); } @@ -513,18 +466,12 @@ origin of an _Event_. We can call `event.doesOriginateFrom(NotificationOrigin)` which will return true if the event originated from _NotificationOrigin_ and otherwise false. -We start this example by creating a _WaitSet_ with the default capacity and -attaching the `shutdownTrigger` to handle `CTRL+C`. +We start this example by creating a _WaitSet_. ```cpp -iox::popo::WaitSet<> waitset; - -// attach shutdownTrigger to handle CTRL+C -waitset.attachEvent(shutdownTrigger).or_else([](auto) { - std::cerr << "failed to attach shutdown trigger" << std::endl; - std::exit(EXIT_FAILURE); -}); +WaitSet waitset; +waitsetSigHandlerAccess = &waitset; ``` Additionally, we create two subscribers and attach them with the state `SubscriberState::HAS_DATA` @@ -545,27 +492,7 @@ waitset.attachState(subscriber2, iox::popo::SubscriberState::HAS_DATA).or_else([ }); ``` -With that set up we enter the event loop and handle the program termination first. - - -```cpp -while (keepRunning) -{ - auto notificationVector = waitset.wait(); - - for (auto& notification : notificationVector) - { - if (notification->doesOriginateFrom(&shutdownTrigger)) - { - keepRunning = false; - } - // ... - } - - std::cout << std::endl; -} -``` - +With that set up we enter the event loop and handle subscriber events. When the origin is `subscriber1` we would like to print the received data to the console. But for `subscriber2` we just dismiss the received samples. We accomplish this by asking the `event` if it originated from the @@ -579,9 +506,8 @@ while (keepRunning) for (auto& notification : notificationVector) { - // ... // process sample received by subscriber1 - else if (notification->doesOriginateFrom(&subscriber1)) + if (notification->doesOriginateFrom(&subscriber1)) { subscriber1.take().and_then( [&](auto& sample) { std::cout << "subscriber 1 received: " << sample->counter << std::endl; }); @@ -590,7 +516,7 @@ while (keepRunning) if (notification->doesOriginateFrom(&subscriber2)) { // We need to release the samples to reset the trigger hasSamples - // otherwise the WaitSet would notify us in `waitset.wait()` again + // otherwise the WaitSet would notify us in 'waitset.wait()' again // instantly. subscriber2.releaseQueuedData(); std::cout << "subscriber 2 received something - dont care\n"; @@ -623,20 +549,12 @@ class SomeClass The user trigger is event based and always reset after the _WaitSet_ has acquired all triggered objects. -As always, we begin by creating a _WaitSet_ with the default capacity and by -attaching the `shutdownTrigger` to -it. In this case we do not set an event id when calling `attachEvent` which means -the default event id `NotificationInfo::INVALID_ID` is set. +As always, we begin by creating a _WaitSet_. ```cpp iox::popo::WaitSet<> waitset; - -// attach shutdownTrigger to handle CTRL+C -waitset.attachEvent(shutdownTrigger).or_else([](auto) { - std::cerr << "failed to attach shutdown trigger" << std::endl; - std::exit(EXIT_FAILURE); -}); +waitsetSigHandlerAccess = &waitset; ``` After that we require a `cyclicTrigger` to trigger our @@ -666,30 +584,7 @@ std::thread cyclicTriggerThread([&] { }); ``` -Everything is set up and we can implement the event loop. As usual we handle -`CTRL+C` which is indicated by the `shutdownTrigger`. - - -```cpp -while (keepRunning.load()) -{ - auto notificationVector = waitset.wait(); - - for (auto& notification : notificationVector) - { - if (notification->doesOriginateFrom(&shutdownTrigger)) - { - // CTRL+C was pressed -> exit - keepRunning.store(false); - } - // ... - } - - std::cout << std::endl; -} -``` - -The `cyclicTrigger` callback is called in the else part. +The `cyclicTrigger` callback is called in the loop. ```cpp @@ -699,12 +594,8 @@ while (keepRunning.load()) for (auto& notification : notificationVector) { - // ... - else - { - // call SomeClass::cyclicRun - (*notification)(); - } + // call SomeClass::cyclicRun + (*notification)(); } std::cout << std::endl; @@ -1005,11 +896,12 @@ getCallbackForIsStateConditionSatisfied(const MyTriggerClassStates event) const switch (event) { case MyTriggerClassStates::HAS_PERFORMED_ACTION: - return {*this, &MyTriggerClass::hasPerformedAction}; + return iox::popo::WaitSetIsConditionSatisfiedCallback( + iox::in_place, *this, &MyTriggerClass::hasPerformedAction); case MyTriggerClassStates::IS_ACTIVATED: - return {*this, &MyTriggerClass::isActivated}; + return iox::popo::WaitSetIsConditionSatisfiedCallback(iox::in_place, *this, &MyTriggerClass::isActivated); } - return {}; + return iox::nullopt; } ``` @@ -1024,11 +916,11 @@ has occurred which makes the `reset` call obsolete. ```cpp -void eventLoop() +void eventLoop(WaitSet& waitset) { while (keepRunning) { - auto notificationVector = waitset->wait(); + auto notificationVector = waitset.wait(); for (auto& notification : notificationVector) { if (notification->getNotificationId() == ACTIVATE_ID) @@ -1048,14 +940,12 @@ void eventLoop() } ``` -We start like in every other example by creating the `waitset` first. In this -case the `waitset` and the `triggerClass` are stored inside of two global -`optional`'s and have to be created with an `emplace` call. +We start like in every other example by creating the `waitset` first. ```cpp -waitset.emplace(); -triggerClass.emplace(); +WaitSet waitset; +MyTriggerClass triggerClass; ``` After that we can attach the `IS_ACTIVATED` state and `PERFORM_ACTION_CALLED` event @@ -1065,20 +955,20 @@ to the waitset and provide a callback for them. ```cpp // attach the IS_ACTIVATED state to the waitset and assign a callback waitset - ->attachState(*triggerClass, - MyTriggerClassStates::IS_ACTIVATED, - ACTIVATE_ID, - iox::popo::createNotificationCallback(callOnActivate)) + .attachState(triggerClass, + MyTriggerClassStates::IS_ACTIVATED, + ACTIVATE_ID, + iox::popo::createNotificationCallback(callOnActivate)) .or_else([](auto) { std::cerr << "failed to attach MyTriggerClassStates::IS_ACTIVATED state " << std::endl; std::exit(EXIT_FAILURE); }); // attach the PERFORM_ACTION_CALLED event to the waitset and assign a callback waitset - ->attachEvent(*triggerClass, - MyTriggerClassEvents::PERFORM_ACTION_CALLED, - ACTION_ID, - iox::popo::createNotificationCallback(MyTriggerClass::callOnAction)) + .attachEvent(triggerClass, + MyTriggerClassEvents::PERFORM_ACTION_CALLED, + ACTION_ID, + iox::popo::createNotificationCallback(MyTriggerClass::callOnAction)) .or_else([](auto) { std::cerr << "failed to attach MyTriggerClassEvents::PERFORM_ACTION_CALLED event " << std::endl; std::exit(EXIT_FAILURE); @@ -1089,7 +979,7 @@ Now that everything is set up we can start our `eventLoop` in a new thread. ```cpp -std::thread eventLoopThread(eventLoop); +std::thread eventLoopThread(eventLoop, std::ref(waitset)); ``` A thread which will trigger an event every second is started with the following @@ -1102,15 +992,15 @@ std::thread triggerThread([&] { for (auto i = 0U; i < 10; ++i) { std::this_thread::sleep_for(std::chrono::seconds(1)); - triggerClass->activate(activationCode++); + triggerClass.activate(activationCode++); std::this_thread::sleep_for(std::chrono::seconds(1)); - triggerClass->performAction(); + triggerClass.performAction(); } std::cout << "Sending final trigger" << std::endl; keepRunning = false; - triggerClass->activate(activationCode++); - triggerClass->performAction(); + triggerClass.activate(activationCode++); + triggerClass.performAction(); }); ``` diff --git a/iceoryx_examples/waitset/ice_waitset_basic.cpp b/iceoryx_examples/waitset/ice_waitset_basic.cpp index cc9e599da6..d415d78ee5 100644 --- a/iceoryx_examples/waitset/ice_waitset_basic.cpp +++ b/iceoryx_examples/waitset/ice_waitset_basic.cpp @@ -26,39 +26,42 @@ #include //! [sig handler] -std::atomic_bool keepRunning{true}; -iox::optional> waitset; +volatile bool keepRunning{true}; + +using WaitSet = iox::popo::WaitSet<>; +volatile WaitSet* waitsetSigHandlerAccess{nullptr}; static void sigHandler(int sig IOX_MAYBE_UNUSED) { keepRunning = false; - if (waitset) + if (waitsetSigHandlerAccess) { - waitset->markForDestruction(); + waitsetSigHandlerAccess->markForDestruction(); } } //! [sig handler] int main() { - // initialize runtime - iox::runtime::PoshRuntime::initRuntime("iox-cpp-waitset-basic"); - - // create waitset inside of the optional - //! [create waitset] - waitset.emplace(); - // register signal handler to handle termination of the loop auto signalGuard = iox::posix::registerSignalHandler(iox::posix::Signal::INT, sigHandler).expect("failed to register SIGINT"); auto signalTermGuard = iox::posix::registerSignalHandler(iox::posix::Signal::TERM, sigHandler).expect("failed to register SIGTERM"); + // initialize runtime + iox::runtime::PoshRuntime::initRuntime("iox-cpp-waitset-basic"); + + // create waitset inside of the optional + //! [create waitset] + WaitSet waitset; + waitsetSigHandlerAccess = &waitset; + // create subscriber iox::popo::Subscriber subscriber({"Radar", "FrontLeft", "Counter"}); // attach subscriber to waitset - waitset->attachState(subscriber, iox::popo::SubscriberState::HAS_DATA).or_else([](auto) { + waitset.attachState(subscriber, iox::popo::SubscriberState::HAS_DATA).or_else([](auto) { std::cerr << "failed to attach subscriber" << std::endl; std::exit(EXIT_FAILURE); }); @@ -68,7 +71,7 @@ int main() while (keepRunning) { // We block and wait for samples to arrive. - auto notificationVector = waitset->wait(); + auto notificationVector = waitset.wait(); for (auto& notification : notificationVector) { @@ -96,6 +99,7 @@ int main() std::cout << "shutting down" << std::endl; - waitset.reset(); + waitsetSigHandlerAccess = nullptr; // invalidate for signal handler + return (EXIT_SUCCESS); } diff --git a/iceoryx_examples/waitset/ice_waitset_gateway.cpp b/iceoryx_examples/waitset/ice_waitset_gateway.cpp index 4f2e52806d..ecd233d406 100644 --- a/iceoryx_examples/waitset/ice_waitset_gateway.cpp +++ b/iceoryx_examples/waitset/ice_waitset_gateway.cpp @@ -24,20 +24,24 @@ #include #include +constexpr uint64_t NUMBER_OF_SUBSCRIBERS = 2U; + std::atomic_bool keepRunning{true}; -iox::popo::UserTrigger shutdownTrigger; -static void sigHandler(int f_sig IOX_MAYBE_UNUSED) -{ - shutdownTrigger.trigger(); -} +//! [waitset type alias] +using WaitSet = iox::popo::WaitSet; +//! [waitset type alias] + +volatile WaitSet* waitsetSigHandlerAccess{nullptr}; -//! [shutdown callback] -void shutdownCallback(iox::popo::UserTrigger*) +static void sigHandler(int f_sig IOX_MAYBE_UNUSED) { - std::cout << "CTRL+C pressed - exiting now" << std::endl; + keepRunning = false; + if (waitsetSigHandlerAccess) + { + waitsetSigHandlerAccess->markForDestruction(); + } } -//! [shutdown callback] // The callback of the event. Every callback must have an argument which is // a pointer to the origin of the Trigger. In our case the event origin is @@ -63,9 +67,6 @@ void subscriberCallback(iox::popo::UntypedSubscriber* const subscriber, uint64_t int main() { - constexpr uint64_t NUMBER_OF_SUBSCRIBERS = 2U; - constexpr uint64_t ONE_SHUTDOWN_TRIGGER = 1U; - // register sigHandler auto signalIntGuard = iox::posix::registerSignalHandler(iox::posix::Signal::INT, sigHandler).expect("failed to register SIGINT"); @@ -75,13 +76,8 @@ int main() iox::runtime::PoshRuntime::initRuntime("iox-cpp-waitset-gateway"); //! [create waitset] - iox::popo::WaitSet waitset; - - // attach shutdownTrigger to handle CTRL+C - waitset.attachEvent(shutdownTrigger, iox::popo::createNotificationCallback(shutdownCallback)).or_else([](auto) { - std::cerr << "failed to attach shutdown trigger" << std::endl; - std::exit(EXIT_FAILURE); - }); + WaitSet waitset; + waitsetSigHandlerAccess = &waitset; //! [create waitset] //! [configure] @@ -115,16 +111,8 @@ int main() for (auto& notification : notificationVector) { - if (notification->doesOriginateFrom(&shutdownTrigger)) - { - (*notification)(); - keepRunning = false; - } - else - { - // call the callback which was assigned to the notification - (*notification)(); - } + // call the callback which was assigned to the notification + (*notification)(); } auto flags = std::cout.flags(); @@ -133,5 +121,7 @@ int main() } //! [event loop] + waitsetSigHandlerAccess = nullptr; // invalidate for signal handler + return (EXIT_SUCCESS); } diff --git a/iceoryx_examples/waitset/ice_waitset_grouping.cpp b/iceoryx_examples/waitset/ice_waitset_grouping.cpp index fb8d9207b4..9ec9b728c4 100644 --- a/iceoryx_examples/waitset/ice_waitset_grouping.cpp +++ b/iceoryx_examples/waitset/ice_waitset_grouping.cpp @@ -25,18 +25,23 @@ #include std::atomic_bool keepRunning{true}; -iox::popo::UserTrigger shutdownTrigger; + +constexpr uint64_t NUMBER_OF_SUBSCRIBERS = 4U; +using WaitSet = iox::popo::WaitSet; + +volatile WaitSet* waitsetSigHandlerAccess{nullptr}; static void sigHandler(int f_sig IOX_MAYBE_UNUSED) { - shutdownTrigger.trigger(); + keepRunning = false; + if (waitsetSigHandlerAccess) + { + waitsetSigHandlerAccess->markForDestruction(); + } } int main() { - constexpr uint64_t NUMBER_OF_SUBSCRIBERS = 4U; - constexpr uint64_t ONE_SHUTDOWN_TRIGGER = 1U; - // register sigHandler auto signalIntGuard = iox::posix::registerSignalHandler(iox::posix::Signal::INT, sigHandler).expect("failed to register SIGINT"); @@ -45,13 +50,8 @@ int main() iox::runtime::PoshRuntime::initRuntime("iox-cpp-waitset-grouping"); //! [create waitset] - iox::popo::WaitSet waitset; - - // attach shutdownTrigger to handle CTRL+C - waitset.attachEvent(shutdownTrigger).or_else([](auto) { - std::cerr << "failed to attach shutdown trigger" << std::endl; - std::exit(EXIT_FAILURE); - }); + WaitSet waitset; + waitsetSigHandlerAccess = &waitset; //! [create waitset] // create subscriber and subscribe them to our service @@ -95,16 +95,9 @@ int main() for (auto& notification : notificationVector) { - //! [shutdown path] - if (notification->doesOriginateFrom(&shutdownTrigger)) - { - keepRunning = false; - } - //! [shutdown path] - //! [data path] // we print the received data for the first group - else if (notification->getNotificationId() == FIRST_GROUP_ID) + if (notification->getNotificationId() == FIRST_GROUP_ID) { auto subscriber = notification->getOrigin(); subscriber->take().and_then([&](auto& userPayload) { @@ -132,5 +125,7 @@ int main() } //! [event loop] + waitsetSigHandlerAccess = nullptr; // invalidate for signal handler + return (EXIT_SUCCESS); } diff --git a/iceoryx_examples/waitset/ice_waitset_individual.cpp b/iceoryx_examples/waitset/ice_waitset_individual.cpp index fab098f672..c5af2fb084 100644 --- a/iceoryx_examples/waitset/ice_waitset_individual.cpp +++ b/iceoryx_examples/waitset/ice_waitset_individual.cpp @@ -25,11 +25,17 @@ #include std::atomic_bool keepRunning{true}; -iox::popo::UserTrigger shutdownTrigger; + +using WaitSet = iox::popo::WaitSet<>; +volatile WaitSet* waitsetSigHandlerAccess{nullptr}; static void sigHandler(int f_sig IOX_MAYBE_UNUSED) { - shutdownTrigger.trigger(); + keepRunning = false; + if (waitsetSigHandlerAccess) + { + waitsetSigHandlerAccess->markForDestruction(); + } } int main() @@ -43,13 +49,8 @@ int main() iox::runtime::PoshRuntime::initRuntime("iox-cpp-waitset-individual"); //! [create waitset] - iox::popo::WaitSet<> waitset; - - // attach shutdownTrigger to handle CTRL+C - waitset.attachEvent(shutdownTrigger).or_else([](auto) { - std::cerr << "failed to attach shutdown trigger" << std::endl; - std::exit(EXIT_FAILURE); - }); + WaitSet waitset; + waitsetSigHandlerAccess = &waitset; //! [create waitset] // create two subscribers, subscribe to the service and attach them to the waitset @@ -74,15 +75,9 @@ int main() for (auto& notification : notificationVector) { - //! [shutdown path] - if (notification->doesOriginateFrom(&shutdownTrigger)) - { - keepRunning = false; - } - //! [shutdown path] //! [data path] // process sample received by subscriber1 - else if (notification->doesOriginateFrom(&subscriber1)) + if (notification->doesOriginateFrom(&subscriber1)) { subscriber1.take().and_then( [&](auto& sample) { std::cout << "subscriber 1 received: " << sample->counter << std::endl; }); @@ -103,5 +98,7 @@ int main() } //! [event loop] + waitsetSigHandlerAccess = nullptr; // invalidate for signal handler + return (EXIT_SUCCESS); } diff --git a/iceoryx_examples/waitset/ice_waitset_timer_driven_execution.cpp b/iceoryx_examples/waitset/ice_waitset_timer_driven_execution.cpp index af95665cc9..b38924e2fc 100644 --- a/iceoryx_examples/waitset/ice_waitset_timer_driven_execution.cpp +++ b/iceoryx_examples/waitset/ice_waitset_timer_driven_execution.cpp @@ -24,11 +24,18 @@ #include #include -iox::popo::UserTrigger shutdownTrigger; +std::atomic_bool keepRunning{true}; + +using WaitSet = iox::popo::WaitSet<>; +volatile iox::popo::WaitSet<>* waitsetSigHandlerAccess{nullptr}; static void sigHandler(int f_sig IOX_MAYBE_UNUSED) { - shutdownTrigger.trigger(); + keepRunning = false; + if (waitsetSigHandlerAccess) + { + waitsetSigHandlerAccess->markForDestruction(); + } } //! [cyclic run] @@ -51,16 +58,10 @@ int main() iox::posix::registerSignalHandler(iox::posix::Signal::TERM, sigHandler).expect("failed to register SIGTERM"); iox::runtime::PoshRuntime::initRuntime("iox-cpp-waitset-sync"); - std::atomic_bool keepRunning{true}; //! [create waitset] iox::popo::WaitSet<> waitset; - - // attach shutdownTrigger to handle CTRL+C - waitset.attachEvent(shutdownTrigger).or_else([](auto) { - std::cerr << "failed to attach shutdown trigger" << std::endl; - std::exit(EXIT_FAILURE); - }); + waitsetSigHandlerAccess = &waitset; //! [create waitset] // create and attach the cyclicTrigger with a callback to @@ -91,19 +92,9 @@ int main() for (auto& notification : notificationVector) { - //! [shutdown path] - if (notification->doesOriginateFrom(&shutdownTrigger)) - { - // CTRL+C was pressed -> exit - keepRunning.store(false); - } - //! [shutdown path] //! [data path] - else - { - // call SomeClass::cyclicRun - (*notification)(); - } + // call SomeClass::cyclicRun + (*notification)(); //! [data path] } @@ -112,5 +103,8 @@ int main() //! [event loop] cyclicTriggerThread.join(); + + waitsetSigHandlerAccess = nullptr; // invalidate for signal handler + return (EXIT_SUCCESS); } diff --git a/iceoryx_examples/waitset/ice_waitset_trigger.cpp b/iceoryx_examples/waitset/ice_waitset_trigger.cpp index e07f996131..a72dfa32f6 100644 --- a/iceoryx_examples/waitset/ice_waitset_trigger.cpp +++ b/iceoryx_examples/waitset/ice_waitset_trigger.cpp @@ -24,6 +24,8 @@ std::atomic_bool keepRunning{true}; +using WaitSet = iox::popo::WaitSet<>; + // The two states and events the MyTriggerClass offers //! [state enum] enum class MyTriggerClassStates : iox::popo::StateEnumIdentifier @@ -236,9 +238,6 @@ class MyTriggerClass iox::popo::TriggerHandle m_activateTrigger; }; -iox::optional> waitset; -iox::optional triggerClass; - constexpr uint64_t ACTIVATE_ID = 0U; constexpr uint64_t ACTION_ID = 1U; @@ -250,11 +249,11 @@ void callOnActivate(MyTriggerClass* const triggerClassPtr) // The global event loop. It will create an infinite loop and // will work on the incoming notifications. //! [event loop] -void eventLoop() +void eventLoop(WaitSet& waitset) { while (keepRunning) { - auto notificationVector = waitset->wait(); + auto notificationVector = waitset.wait(); for (auto& notification : notificationVector) { if (notification->getNotificationId() == ACTIVATE_ID) @@ -281,27 +280,27 @@ int main() // we create a waitset and a triggerClass instance inside of the two // global optionals //! [create] - waitset.emplace(); - triggerClass.emplace(); + WaitSet waitset; + MyTriggerClass triggerClass; //! [create] //! [attach] // attach the IS_ACTIVATED state to the waitset and assign a callback waitset - ->attachState(*triggerClass, - MyTriggerClassStates::IS_ACTIVATED, - ACTIVATE_ID, - iox::popo::createNotificationCallback(callOnActivate)) + .attachState(triggerClass, + MyTriggerClassStates::IS_ACTIVATED, + ACTIVATE_ID, + iox::popo::createNotificationCallback(callOnActivate)) .or_else([](auto) { std::cerr << "failed to attach MyTriggerClassStates::IS_ACTIVATED state " << std::endl; std::exit(EXIT_FAILURE); }); // attach the PERFORM_ACTION_CALLED event to the waitset and assign a callback waitset - ->attachEvent(*triggerClass, - MyTriggerClassEvents::PERFORM_ACTION_CALLED, - ACTION_ID, - iox::popo::createNotificationCallback(MyTriggerClass::callOnAction)) + .attachEvent(triggerClass, + MyTriggerClassEvents::PERFORM_ACTION_CALLED, + ACTION_ID, + iox::popo::createNotificationCallback(MyTriggerClass::callOnAction)) .or_else([](auto) { std::cerr << "failed to attach MyTriggerClassEvents::PERFORM_ACTION_CALLED event " << std::endl; std::exit(EXIT_FAILURE); @@ -310,7 +309,7 @@ int main() // start the event loop which is handling the events //! [start event loop] - std::thread eventLoopThread(eventLoop); + std::thread eventLoopThread(eventLoop, std::ref(waitset)); //! [start event loop] // start a thread which will trigger an event every second @@ -320,22 +319,20 @@ int main() for (auto i = 0U; i < 10; ++i) { std::this_thread::sleep_for(std::chrono::seconds(1)); - triggerClass->activate(activationCode++); + triggerClass.activate(activationCode++); std::this_thread::sleep_for(std::chrono::seconds(1)); - triggerClass->performAction(); + triggerClass.performAction(); } std::cout << "Sending final trigger" << std::endl; keepRunning = false; - triggerClass->activate(activationCode++); - triggerClass->performAction(); + triggerClass.activate(activationCode++); + triggerClass.performAction(); }); //! [start trigger] triggerThread.join(); eventLoopThread.join(); - triggerClass.reset(); - waitset.reset(); return (EXIT_SUCCESS); } diff --git a/iceoryx_examples/waitset_in_c/README.md b/iceoryx_examples/waitset_in_c/README.md index 460684489f..d50af86ccc 100644 --- a/iceoryx_examples/waitset_in_c/README.md +++ b/iceoryx_examples/waitset_in_c/README.md @@ -71,40 +71,25 @@ void subscriberCallback(iox_sub_t const subscriber, void* const contextData) } ``` -The `shutdownTrigger` gets a simplified callback where it just states that the -program will be terminated. For this we do not need any context data. - -```c -void shutdownCallback(iox_user_trigger_t userTrigger) -{ - (void)userTrigger; - printf("CTRL+C pressed - exiting now\n"); - fflush(stdout); -} -``` - Since we attach the `SubscriberEvent_DATA_RECEIVED` event to the _WaitSet_ that notifies us just once when data was received we have to gather and process all chunks. One will never miss chunks since the event notification is reset after a call to `iox_ws_wait` or `iox_ws_timed_wait` which we introduce below. After we registered our runtime we set up some `waitSetStorage`, initialize the _WaitSet_ -and attach a `shutdownTrigger` to handle `CTRL+C`. +and let `waitSetSigHandlerAccess` point to `waitSet`. `waitSetSigHandlerAccess` is used by +the signal handler to initiate a graceful shutdown. ```c +signal(SIGINT, sigHandler); +signal(SIGTERM, sigHandler); + iox_runtime_init("iox-c-waitset-gateway"); iox_ws_storage_t waitSetStorage; iox_ws_t waitSet = iox_ws_init(&waitSetStorage); -shutdownTrigger = iox_user_trigger_init(&shutdownTriggerStorage); - -// attach shutdownTrigger with no callback to handle CTRL+C -iox_ws_attach_user_trigger_event(waitSet, shutdownTrigger, 0U, shutdownCallback); - -// register signal after shutdownTrigger since we are using it in the handler -signal(SIGINT, sigHandler); -signal(SIGTERM, sigHandler); +waitSetSigHandlerAccess = waitSet; ``` In the next steps, we define `sumOfAllSamples`, create two subscribers with `iox_sub_init`, @@ -154,7 +139,6 @@ uint64_t numberOfNotifications = 0U; // array where all notification infos from iox_ws_wait will be stored iox_notification_info_t notificationArray[NUMBER_OF_NOTIFICATIONS]; -bool keepRunning = true; while (keepRunning) { numberOfNotifications = iox_ws_wait(waitSet, notificationArray, NUMBER_OF_NOTIFICATIONS, &missedElements); @@ -163,8 +147,7 @@ while (keepRunning) ``` The events which have occurred are stored in the `notificationArray`. We iterate through -it, if the `shutdownTrigger` was triggered we terminate the program otherwise -we call the callback with `iox_notification_info_call(notification)`. +it and call the callback with `iox_notification_info_call(notification)`. ```c @@ -172,23 +155,16 @@ for (uint64_t i = 0U; i < numberOfNotifications; ++i) { iox_notification_info_t notification = notificationArray[i]; - if (iox_notification_info_does_originate_from_user_trigger(notification, shutdownTrigger)) - { - // CTRL+C was pressed -> exit - keepRunning = false; - } - else - { - // call the callback which was assigned to the event - iox_notification_info_call(notification); + // call the callback which was assigned to the event + iox_notification_info_call(notification); - printf("sum of all samples: %lu\n", (unsigned long)sumOfAllSamples); - fflush(stdout); - } + printf("sum of all samples: %lu\n", (unsigned long)sumOfAllSamples); + fflush(stdout); } ``` -Before we can close the program, we cleanup all resources. +Before we can close the program, we cleanup all resources and set `waitSetSigHandlerAccess` to +`NULL` to prevent the signal handler to access an invalid waitset. ```c @@ -200,8 +176,8 @@ for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) iox_sub_deinit(subscriber[i]); } +waitSetSigHandlerAccess = NULL; // invalidate for signal handler iox_ws_deinit(waitSet); -iox_user_trigger_deinit(shutdownTrigger); ``` ### Grouping @@ -210,23 +186,19 @@ In this scenario, we have two groups of subscribers. We are interested in the data of the first group and would like to print them onto the console and the data of the second group should be discarded. -We start like in every example with creating the _WaitSet_ and attaching the -`shutdownTrigger`. +We start like in every example with registering the signal handler, initializing +the runtime and creating the _WaitSet_. ```c +signal(SIGINT, sigHandler); +signal(SIGTERM, sigHandler); + iox_runtime_init("iox-c-waitset-grouping"); iox_ws_storage_t waitSetStorage; iox_ws_t waitSet = iox_ws_init(&waitSetStorage); -shutdownTrigger = iox_user_trigger_init(&shutdownTriggerStorage); - -// attach shutdownTrigger with no callback to handle CTRL+C -iox_ws_attach_user_trigger_event(waitSet, shutdownTrigger, 0U, NULL); - -// register signal after shutdownTrigger since we are using it in the handler -signal(SIGINT, sigHandler); -signal(SIGTERM, sigHandler); +waitSetSigHandlerAccess = waitSet; ``` After that we can create a list of subscribers and subscribe them to our topic. @@ -278,7 +250,6 @@ of notifications by calling `iox_ws_wait`. ```c -bool keepRunning = true; while (keepRunning) { numberOfNotifications = iox_ws_wait(waitSet, notificationArray, NUMBER_OF_NOTIFICATIONS, &missedElements); @@ -286,8 +257,7 @@ while (keepRunning) } ``` -When we iterate through the array we handle the `shutdownTrigger` first. -We check if an event is from the first group by calling +We iterate through the array and check if an event is from the first group by calling `iox_notification_info_get_event_id` and compare the result with `FIRST_GROUP_ID`. If that is the case we acquire the subscriber handle with `iox_notification_info_get_subscriber_origin`. This allows us to receive the new @@ -301,13 +271,8 @@ for (uint64_t i = 0U; i < numberOfNotifications; ++i) { iox_notification_info_t notification = notificationArray[i]; - if (iox_notification_info_does_originate_from_user_trigger(notification, shutdownTrigger)) - { - // CTRL+C was pressed -> exit - keepRunning = false; - } // we print the received data for the first group - else if (iox_notification_info_get_notification_id(notification) == FIRST_GROUP_ID) + if (iox_notification_info_get_notification_id(notification) == FIRST_GROUP_ID) { iox_sub_t subscriber = iox_notification_info_get_subscriber_origin(notification); const void* userPayload; @@ -325,7 +290,7 @@ for (uint64_t i = 0U; i < numberOfNotifications; ++i) printf("dismiss data\n"); iox_sub_t subscriber = iox_notification_info_get_subscriber_origin(notification); // We need to release the samples to reset the event hasSamples - // otherwise the WaitSet would notify us in `iox_ws_wait()` again + // otherwise the WaitSet would notify us in 'iox_ws_wait()' again // instantly. iox_sub_release_queued_chunks(subscriber); } @@ -345,8 +310,8 @@ for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) iox_sub_deinit(subscriber[i]); } +waitSetSigHandlerAccess = NULL; // invalidate for signal handler iox_ws_deinit(waitSet); -iox_user_trigger_deinit(shutdownTrigger); ``` ### Individual @@ -357,22 +322,18 @@ One way would be to assign every subscriber a different callback, here we look at a different approach. We check if the event originated from a specific subscriber and then perform the calls on that subscriber directly. -We start as usual by creating a _WaitSet_ and attach the `shutdownTrigger` to it. +We start as usual with the setup of the signal handler and _WaitSet_. ```c +signal(SIGINT, sigHandler); +signal(SIGTERM, sigHandler); + iox_runtime_init("iox-c-waitset-individual"); iox_ws_storage_t waitSetStorage; iox_ws_t waitSet = iox_ws_init(&waitSetStorage); -shutdownTrigger = iox_user_trigger_init(&shutdownTriggerStorage); - -// attach shutdownTrigger with no callback to handle CTRL+C -iox_ws_attach_user_trigger_event(waitSet, shutdownTrigger, 0U, NULL); - -// register signal after shutdownTrigger since we are using it in the handler -signal(SIGINT, sigHandler); -signal(SIGTERM, sigHandler); +waitSetSigHandlerAccess = waitSet; ``` Now we create two subscribers, subscribe them to our topic and attach them to @@ -411,7 +372,6 @@ uint64_t numberOfNotifications = 0U; // array where all notification infos from iox_ws_wait will be stored iox_notification_info_t notificationArray[NUMBER_OF_NOTIFICATIONS]; -bool keepRunning = true; while (keepRunning) { numberOfNotifications = iox_ws_wait(waitSet, notificationArray, NUMBER_OF_NOTIFICATIONS, &missedElements); @@ -419,8 +379,7 @@ while (keepRunning) } ``` -The `shutdownTrigger` is handled as usual and -we use `iox_notification_info_does_originate_from_subscriber` +We use `iox_notification_info_does_originate_from_subscriber` to identify the event that originated from a specific subscriber. If it originated from the first subscriber we print the received data to the console, if it originated from the second subscriber we discard the data. @@ -431,13 +390,7 @@ for (uint64_t i = 0U; i < numberOfNotifications; ++i) { iox_notification_info_t notification = notificationArray[i]; - if (iox_notification_info_does_originate_from_user_trigger(notification, shutdownTrigger)) - { - // CTRL+C was pressed -> exit - keepRunning = false; - } - // process sample received by subscriber1 - else if (iox_notification_info_does_originate_from_subscriber(notification, subscriber[0U])) + if (iox_notification_info_does_originate_from_subscriber(notification, subscriber[0U])) { const void* userPayload; if (iox_sub_take_chunk(subscriber[0U], &userPayload)) @@ -452,7 +405,7 @@ for (uint64_t i = 0U; i < numberOfNotifications; ++i) else if (iox_notification_info_does_originate_from_subscriber(notification, subscriber[1])) { // We need to release the samples to reset the event hasSamples - // otherwise the WaitSet would notify us in `iox_ws_wait()` again + // otherwise the WaitSet would notify us in 'iox_ws_wait()' again // instantly. iox_sub_release_queued_chunks(subscriber[1U]); printf("subscriber 2 received something - dont care\n"); @@ -470,8 +423,8 @@ for (uint64_t i = 0U; i < NUMBER_OF_SUBSCRIBERS; ++i) iox_sub_deinit(subscriber[i]); } +waitSetSigHandlerAccess = NULL; // invalidate for signal handler iox_ws_deinit(waitSet); -iox_user_trigger_deinit(shutdownTrigger); ``` ### Timer Driven Execution @@ -482,22 +435,18 @@ thread every second to signal the _WaitSet_ that it's time for the next run. Additionally, we attach a callback (`cyclicRun`) to this user trigger so that the event can directly call the cyclic call. -We begin by creating the _WaitSet_ and attach the `shutdownTrigger`. +We start as usual with the setup of the signal handler and _WaitSet_. ```c +signal(SIGINT, sigHandler); +signal(SIGTERM, sigHandler); + iox_runtime_init("iox-c-waitset-timer-driven-execution"); iox_ws_storage_t waitSetStorage; iox_ws_t waitSet = iox_ws_init(&waitSetStorage); -shutdownTrigger = iox_user_trigger_init(&shutdownTriggerStorage); - -// attach shutdownTrigger with no callback to handle CTRL+C -iox_ws_attach_user_trigger_event(waitSet, shutdownTrigger, 0, NULL); - -// register signal after shutdownTrigger since we are using it in the handler -signal(SIGINT, sigHandler); -signal(SIGTERM, sigHandler); +waitSetSigHandlerAccess = waitSet; ``` Now we create our cyclic trigger and attach it to our waitset with an eventId @@ -540,8 +489,8 @@ while (keepRunning) } ``` -The `shutdownTrigger` is handled as usual and the `cyclicTrigger` is handled by -just calling the attached callback with `iox_notification_info_call(notification)`. +The code checks for a notification from the `cyclicTrigger` and calls the attached +callback with `iox_notification_info_call(notification)`. ```c @@ -549,12 +498,7 @@ for (uint64_t i = 0U; i < numberOfNotifications; ++i) { iox_notification_info_t notification = notificationArray[i]; - if (iox_notification_info_does_originate_from_user_trigger(notification, shutdownTrigger)) - { - // CTRL+C was pressed -> exit - keepRunning = false; - } - else + if (iox_notification_info_does_originate_from_user_trigger(notification, cyclicTrigger)) { // call myCyclicRun iox_notification_info_call(notification); @@ -567,8 +511,11 @@ The last thing we have to do is to cleanup all the used resources. ```c joinThread(cyclicTriggerThread); + +waitSetSigHandlerAccess = NULL; // invalidate for signal handler iox_ws_deinit(waitSet); -iox_user_trigger_deinit(shutdownTrigger); + +iox_user_trigger_deinit(cyclicTrigger); ```
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 96281cb2f1..57bd5130fe 100644 --- a/iceoryx_examples/waitset_in_c/ice_c_waitset_gateway.c +++ b/iceoryx_examples/waitset_in_c/ice_c_waitset_gateway.c @@ -32,24 +32,20 @@ #define NUMBER_OF_NOTIFICATIONS 3 #define NUMBER_OF_SUBSCRIBERS 2 -iox_user_trigger_storage_t shutdownTriggerStorage; -iox_user_trigger_t shutdownTrigger; +volatile bool keepRunning = true; -static void sigHandler(int signalValue) -{ - (void)signalValue; - - iox_user_trigger_trigger(shutdownTrigger); -} +volatile iox_ws_t waitSetSigHandlerAccess = NULL; -//! [shutdown callback] -void shutdownCallback(iox_user_trigger_t userTrigger) +void sigHandler(int signalValue) { - (void)userTrigger; - printf("CTRL+C pressed - exiting now\n"); - fflush(stdout); + // Ignore unused variable warning + (void)signalValue; + keepRunning = false; + if (waitSetSigHandlerAccess) + { + iox_ws_mark_for_destruction(waitSetSigHandlerAccess); + } } -//! [shutdown callback] // The callback of the trigger. Every callback must have an argument which is // a pointer to the origin of the Trigger. In our case the trigger origin is @@ -79,18 +75,14 @@ void subscriberCallback(iox_sub_t const subscriber, void* const contextData) int main(void) { //! [initialization and shutdown handling] + signal(SIGINT, sigHandler); + signal(SIGTERM, sigHandler); + iox_runtime_init("iox-c-waitset-gateway"); iox_ws_storage_t waitSetStorage; iox_ws_t waitSet = iox_ws_init(&waitSetStorage); - shutdownTrigger = iox_user_trigger_init(&shutdownTriggerStorage); - - // attach shutdownTrigger with no callback to handle CTRL+C - iox_ws_attach_user_trigger_event(waitSet, shutdownTrigger, 0U, shutdownCallback); - - // register signal after shutdownTrigger since we are using it in the handler - signal(SIGINT, sigHandler); - signal(SIGTERM, sigHandler); + waitSetSigHandlerAccess = waitSet; //! [initialization and shutdown handling] //! [create and attach subscriber] @@ -122,7 +114,6 @@ int main(void) // array where all notification infos from iox_ws_wait will be stored iox_notification_info_t notificationArray[NUMBER_OF_NOTIFICATIONS]; - bool keepRunning = true; while (keepRunning) { numberOfNotifications = iox_ws_wait(waitSet, notificationArray, NUMBER_OF_NOTIFICATIONS, &missedElements); @@ -132,19 +123,11 @@ int main(void) { iox_notification_info_t notification = notificationArray[i]; - if (iox_notification_info_does_originate_from_user_trigger(notification, shutdownTrigger)) - { - // CTRL+C was pressed -> exit - keepRunning = false; - } - else - { - // call the callback which was assigned to the event - iox_notification_info_call(notification); - - printf("sum of all samples: %lu\n", (unsigned long)sumOfAllSamples); - fflush(stdout); - } + // call the callback which was assigned to the event + iox_notification_info_call(notification); + + printf("sum of all samples: %lu\n", (unsigned long)sumOfAllSamples); + fflush(stdout); } //! [handle events] } @@ -159,8 +142,8 @@ int main(void) iox_sub_deinit(subscriber[i]); } + waitSetSigHandlerAccess = NULL; // invalidate for signal handler iox_ws_deinit(waitSet); - iox_user_trigger_deinit(shutdownTrigger); //! [cleanup all resources] return 0; diff --git a/iceoryx_examples/waitset_in_c/ice_c_waitset_grouping.c b/iceoryx_examples/waitset_in_c/ice_c_waitset_grouping.c index f0db6e840d..c09ad96513 100644 --- a/iceoryx_examples/waitset_in_c/ice_c_waitset_grouping.c +++ b/iceoryx_examples/waitset_in_c/ice_c_waitset_grouping.c @@ -31,32 +31,32 @@ #define NUMBER_OF_NOTIFICATIONS 5 #define NUMBER_OF_SUBSCRIBERS 4 -iox_user_trigger_storage_t shutdownTriggerStorage; -iox_user_trigger_t shutdownTrigger; +volatile bool keepRunning = true; -static void sigHandler(int signalValue) +volatile iox_ws_t waitSetSigHandlerAccess = NULL; + +void sigHandler(int signalValue) { // Ignore unused variable warning (void)signalValue; - - iox_user_trigger_trigger(shutdownTrigger); + keepRunning = false; + if (waitSetSigHandlerAccess) + { + iox_ws_mark_for_destruction(waitSetSigHandlerAccess); + } } int main(void) { //! [initialization and shutdown handling] + signal(SIGINT, sigHandler); + signal(SIGTERM, sigHandler); + iox_runtime_init("iox-c-waitset-grouping"); iox_ws_storage_t waitSetStorage; iox_ws_t waitSet = iox_ws_init(&waitSetStorage); - shutdownTrigger = iox_user_trigger_init(&shutdownTriggerStorage); - - // attach shutdownTrigger with no callback to handle CTRL+C - iox_ws_attach_user_trigger_event(waitSet, shutdownTrigger, 0U, NULL); - - // register signal after shutdownTrigger since we are using it in the handler - signal(SIGINT, sigHandler); - signal(SIGTERM, sigHandler); + waitSetSigHandlerAccess = waitSet; //! [initialization and shutdown handling] //! [create subscriber] @@ -100,7 +100,6 @@ int main(void) iox_notification_info_t notificationArray[NUMBER_OF_NOTIFICATIONS]; //! [event loop] - bool keepRunning = true; while (keepRunning) { numberOfNotifications = iox_ws_wait(waitSet, notificationArray, NUMBER_OF_NOTIFICATIONS, &missedElements); @@ -110,13 +109,8 @@ int main(void) { iox_notification_info_t notification = notificationArray[i]; - if (iox_notification_info_does_originate_from_user_trigger(notification, shutdownTrigger)) - { - // CTRL+C was pressed -> exit - keepRunning = false; - } // we print the received data for the first group - else if (iox_notification_info_get_notification_id(notification) == FIRST_GROUP_ID) + if (iox_notification_info_get_notification_id(notification) == FIRST_GROUP_ID) { iox_sub_t subscriber = iox_notification_info_get_subscriber_origin(notification); const void* userPayload; @@ -149,8 +143,8 @@ int main(void) iox_sub_deinit(subscriber[i]); } + waitSetSigHandlerAccess = NULL; // invalidate for signal handler iox_ws_deinit(waitSet); - iox_user_trigger_deinit(shutdownTrigger); //! [cleanup all resources] return 0; diff --git a/iceoryx_examples/waitset_in_c/ice_c_waitset_individual.c b/iceoryx_examples/waitset_in_c/ice_c_waitset_individual.c index a24f47b342..97c1de054d 100644 --- a/iceoryx_examples/waitset_in_c/ice_c_waitset_individual.c +++ b/iceoryx_examples/waitset_in_c/ice_c_waitset_individual.c @@ -31,32 +31,32 @@ #define NUMBER_OF_NOTIFICATIONS 3 #define NUMBER_OF_SUBSCRIBERS 2 -iox_user_trigger_storage_t shutdownTriggerStorage; -iox_user_trigger_t shutdownTrigger; +volatile bool keepRunning = true; -static void sigHandler(int signalValue) +volatile iox_ws_t waitSetSigHandlerAccess = NULL; + +void sigHandler(int signalValue) { // Ignore unused variable warning (void)signalValue; - - iox_user_trigger_trigger(shutdownTrigger); + keepRunning = false; + if (waitSetSigHandlerAccess) + { + iox_ws_mark_for_destruction(waitSetSigHandlerAccess); + } } int main(void) { //! [initialization and shutdown handling] + signal(SIGINT, sigHandler); + signal(SIGTERM, sigHandler); + iox_runtime_init("iox-c-waitset-individual"); iox_ws_storage_t waitSetStorage; iox_ws_t waitSet = iox_ws_init(&waitSetStorage); - shutdownTrigger = iox_user_trigger_init(&shutdownTriggerStorage); - - // attach shutdownTrigger with no callback to handle CTRL+C - iox_ws_attach_user_trigger_event(waitSet, shutdownTrigger, 0U, NULL); - - // register signal after shutdownTrigger since we are using it in the handler - signal(SIGINT, sigHandler); - signal(SIGTERM, sigHandler); + waitSetSigHandlerAccess = waitSet; //! [initialization and shutdown handling] //! [create and attach subscriber] @@ -87,7 +87,6 @@ int main(void) // array where all notification infos from iox_ws_wait will be stored iox_notification_info_t notificationArray[NUMBER_OF_NOTIFICATIONS]; - bool keepRunning = true; while (keepRunning) { numberOfNotifications = iox_ws_wait(waitSet, notificationArray, NUMBER_OF_NOTIFICATIONS, &missedElements); @@ -97,13 +96,7 @@ int main(void) { iox_notification_info_t notification = notificationArray[i]; - if (iox_notification_info_does_originate_from_user_trigger(notification, shutdownTrigger)) - { - // CTRL+C was pressed -> exit - keepRunning = false; - } - // process sample received by subscriber1 - else if (iox_notification_info_does_originate_from_subscriber(notification, subscriber[0U])) + if (iox_notification_info_does_originate_from_subscriber(notification, subscriber[0U])) { const void* userPayload; if (iox_sub_take_chunk(subscriber[0U], &userPayload)) @@ -135,8 +128,8 @@ int main(void) iox_sub_deinit(subscriber[i]); } + waitSetSigHandlerAccess = NULL; // invalidate for signal handler iox_ws_deinit(waitSet); - iox_user_trigger_deinit(shutdownTrigger); //! [cleanup all resources] return 0; diff --git a/iceoryx_examples/waitset_in_c/ice_c_waitset_publisher.c b/iceoryx_examples/waitset_in_c/ice_c_waitset_publisher.c index 4d92b175e4..1f032bfe16 100644 --- a/iceoryx_examples/waitset_in_c/ice_c_waitset_publisher.c +++ b/iceoryx_examples/waitset_in_c/ice_c_waitset_publisher.c @@ -24,14 +24,14 @@ #include #include -bool killswitch = false; +volatile bool keepRunning = true; static void sigHandler(int signalValue) { // Ignore unused variable warning (void)signalValue; // caught SIGINT or SIGTERM, now exit gracefully - killswitch = true; + keepRunning = false; } void sending(void) @@ -45,7 +45,7 @@ void sending(void) iox_pub_storage_t publisherStorage; iox_pub_t publisher = iox_pub_init(&publisherStorage, "Radar", "FrontLeft", "Counter", &options); - for (uint32_t counter = 0U; !killswitch; ++counter) + for (uint32_t counter = 0U; keepRunning; ++counter) { void* userPayload = NULL; if (AllocationResult_SUCCESS == iox_pub_loan_chunk(publisher, &userPayload, sizeof(struct CounterTopic))) diff --git a/iceoryx_examples/waitset_in_c/ice_c_waitset_timer_driven_execution.c b/iceoryx_examples/waitset_in_c/ice_c_waitset_timer_driven_execution.c index 9cd910c399..e127990242 100644 --- a/iceoryx_examples/waitset_in_c/ice_c_waitset_timer_driven_execution.c +++ b/iceoryx_examples/waitset_in_c/ice_c_waitset_timer_driven_execution.c @@ -35,20 +35,22 @@ typedef HANDLE pthread_t; #define NUMBER_OF_NOTIFICATIONS 2 -iox_user_trigger_storage_t shutdownTriggerStorage; -iox_user_trigger_t shutdownTrigger; +volatile bool keepRunning = true; iox_user_trigger_storage_t cyclicTriggerStorage; iox_user_trigger_t cyclicTrigger; -bool keepRunning = true; +volatile iox_ws_t waitSetSigHandlerAccess = NULL; -static void sigHandler(int signalValue) +void sigHandler(int signalValue) { // Ignore unused variable warning (void)signalValue; - - iox_user_trigger_trigger(shutdownTrigger); + keepRunning = false; + if (waitSetSigHandlerAccess) + { + iox_ws_mark_for_destruction(waitSetSigHandlerAccess); + } } void cyclicRun(iox_user_trigger_t trigger) @@ -62,10 +64,16 @@ void* cyclicTriggerCallback(void* dontCare) { // Ignore unused variable warning (void)dontCare; + int countdownToTrigger = 100; while (keepRunning) { - iox_user_trigger_trigger(cyclicTrigger); - sleep_for(1000); + if (countdownToTrigger == 0) + { + iox_user_trigger_trigger(cyclicTrigger); + countdownToTrigger = 100; + } + sleep_for(10); + --countdownToTrigger; } return NULL; } @@ -93,18 +101,14 @@ void joinThread(pthread_t threadHandle) int main(void) { //! [initialization and shutdown handling] + signal(SIGINT, sigHandler); + signal(SIGTERM, sigHandler); + iox_runtime_init("iox-c-waitset-timer-driven-execution"); iox_ws_storage_t waitSetStorage; iox_ws_t waitSet = iox_ws_init(&waitSetStorage); - shutdownTrigger = iox_user_trigger_init(&shutdownTriggerStorage); - - // attach shutdownTrigger with no callback to handle CTRL+C - iox_ws_attach_user_trigger_event(waitSet, shutdownTrigger, 0, NULL); - - // register signal after shutdownTrigger since we are using it in the handler - signal(SIGINT, sigHandler); - signal(SIGTERM, sigHandler); + waitSetSigHandlerAccess = waitSet; //! [initialization and shutdown handling] // create and attach the cyclicTrigger with a callback to @@ -140,12 +144,7 @@ int main(void) { iox_notification_info_t notification = notificationArray[i]; - if (iox_notification_info_does_originate_from_user_trigger(notification, shutdownTrigger)) - { - // CTRL+C was pressed -> exit - keepRunning = false; - } - else + if (iox_notification_info_does_originate_from_user_trigger(notification, cyclicTrigger)) { // call myCyclicRun iox_notification_info_call(notification); @@ -157,8 +156,11 @@ int main(void) //! [cleanup all resources] joinThread(cyclicTriggerThread); + + waitSetSigHandlerAccess = NULL; // invalidate for signal handler iox_ws_deinit(waitSet); - iox_user_trigger_deinit(shutdownTrigger); + + iox_user_trigger_deinit(cyclicTrigger); //! [cleanup all resources] return 0; diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_callback_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_callback_example.py index 52995b358d..6d9679b395 100755 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_callback_example.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_callback_example.py @@ -42,7 +42,7 @@ def generate_test_description(): 'iox-roudi' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_callback_in_c_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_callback_in_c_example.py index 2d5d59c3e6..0eb40aa022 100755 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_callback_in_c_example.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_callback_in_c_example.py @@ -42,7 +42,7 @@ def generate_test_description(): 'iox-roudi' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_complexdata_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_complexdata_example.py index ffec24b399..3ef33ce469 100755 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_complexdata_example.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_complexdata_example.py @@ -56,7 +56,7 @@ def generate_test_description(): 'iox-roudi' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_ice_access_control_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_ice_access_control_example.py index 3bcf84dbb7..a2ed26b335 100755 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_ice_access_control_example.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_ice_access_control_example.py @@ -57,7 +57,7 @@ def generate_test_description(): 'iox-cpp-roudi-static-segments' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_icedelivery_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_icedelivery_example.py index d6a54d4cd6..074434cb93 100755 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_icedelivery_example.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_icedelivery_example.py @@ -56,7 +56,7 @@ def generate_test_description(): 'iox-roudi' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_icedelivery_in_c_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_icedelivery_in_c_example.py index 5435483c37..a4f3627773 100755 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_icedelivery_in_c_example.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_icedelivery_in_c_example.py @@ -41,7 +41,7 @@ def generate_test_description(): 'iox-roudi' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_icediscovery_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_icediscovery_example.py index 15d7769ca3..568fdef364 100644 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_icediscovery_example.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_icediscovery_example.py @@ -55,7 +55,7 @@ def generate_test_description(): 'iox-roudi' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_icediscovery_in_c_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_icediscovery_in_c_example.py index 9368e0cda9..3879e1ed3e 100644 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_icediscovery_in_c_example.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_icediscovery_in_c_example.py @@ -55,7 +55,7 @@ def generate_test_description(): 'iox-roudi' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_icediscovery_monitoring.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_icediscovery_monitoring.py index 7b2cbf9b6c..ea415dbf8b 100644 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_icediscovery_monitoring.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_icediscovery_monitoring.py @@ -55,7 +55,7 @@ def generate_test_description(): 'iox-roudi' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_icehello_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_icehello_example.py index 9132f78ca3..a240c7276a 100755 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_icehello_example.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_icehello_example.py @@ -41,7 +41,7 @@ def generate_test_description(): 'iox-roudi' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_iceoptions_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_iceoptions_example.py index 724736a0c3..65ffb3f11e 100755 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_iceoptions_example.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_iceoptions_example.py @@ -41,7 +41,7 @@ def generate_test_description(): 'iox-roudi' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_basic.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_basic.py index b055c7007c..da3350fe23 100755 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_basic.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_basic.py @@ -55,7 +55,7 @@ def generate_test_description(): 'iox-roudi' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_in_c_basic.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_in_c_basic.py index 31ac8e464d..8ca5e9a39b 100755 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_in_c_basic.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_in_c_basic.py @@ -55,7 +55,7 @@ def generate_test_description(): 'iox-roudi' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_in_c_listener_and_waitset.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_in_c_listener_and_waitset.py index 3c7e65c2dd..02e5ee1fa8 100755 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_in_c_listener_and_waitset.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_in_c_listener_and_waitset.py @@ -55,7 +55,7 @@ def generate_test_description(): 'iox-roudi' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_listener.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_listener.py index 23b5dd03cb..9760ee4cb5 100755 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_listener.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_listener.py @@ -55,7 +55,7 @@ def generate_test_description(): 'iox-roudi' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_untyped.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_untyped.py index 99f2e3868f..de96e31b95 100755 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_untyped.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_request_response_untyped.py @@ -55,7 +55,7 @@ def generate_test_description(): 'iox-roudi' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_roudi_startup_shutdown.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_roudi_startup_shutdown.py index 98ec680445..bd70ef97df 100755 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_roudi_startup_shutdown.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_roudi_startup_shutdown.py @@ -43,7 +43,7 @@ def generate_test_description(): 'iox-roudi' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_user_header_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_user_header_example.py index bffb499ebb..9d696a827b 100755 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_user_header_example.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_user_header_example.py @@ -48,7 +48,7 @@ def generate_test_description(): 'iox-roudi' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_waitset_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_waitset_example.py index 06b137f0d8..2af49ff33b 100755 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_waitset_example.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_waitset_example.py @@ -57,7 +57,7 @@ def generate_test_description(): 'iox-roudi' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_integrationtest/iceoryx_integrationtest/test_waitset_in_c_example.py b/iceoryx_integrationtest/iceoryx_integrationtest/test_waitset_in_c_example.py index 1003a684d8..531456f622 100755 --- a/iceoryx_integrationtest/iceoryx_integrationtest/test_waitset_in_c_example.py +++ b/iceoryx_integrationtest/iceoryx_integrationtest/test_waitset_in_c_example.py @@ -57,7 +57,7 @@ def generate_test_description(): 'iox-roudi' ) roudi_process = launch.actions.ExecuteProcess( - cmd=[roudi_executable, '-l', 'debug'], + cmd=[roudi_executable, '-l', 'trace', '--termination-delay', '5', '--kill-delay', '5'], env=proc_env, output='screen', sigterm_timeout='20') diff --git a/iceoryx_posh/include/iceoryx_posh/error_handling/error_handling.hpp b/iceoryx_posh/include/iceoryx_posh/error_handling/error_handling.hpp index b14361dc35..c8b9d78cbc 100644 --- a/iceoryx_posh/include/iceoryx_posh/error_handling/error_handling.hpp +++ b/iceoryx_posh/include/iceoryx_posh/error_handling/error_handling.hpp @@ -129,6 +129,7 @@ namespace iox error(PORT_POOL__CONDITION_VARIABLE_LIST_OVERFLOW) \ error(PORT_MANAGER__PORT_POOL_UNAVAILABLE) \ error(PORT_MANAGER__INTROSPECTION_MEMORY_MANAGER_UNAVAILABLE) \ + error(PORT_MANAGER__DISCOVERY_MEMORY_MANAGER_UNAVAILABLE) \ error(PORT_MANAGER__HANDLE_PUBLISHER_PORTS_INVALID_CAPRO_MESSAGE) \ error(PORT_MANAGER__HANDLE_SUBSCRIBER_PORTS_INVALID_CAPRO_MESSAGE) \ error(PORT_MANAGER__HANDLE_CLIENT_PORTS_INVALID_CAPRO_MESSAGE) \ @@ -139,6 +140,7 @@ namespace iox error(ROUDI_APP__FAILED_TO_UNLOCK_SEMAPHORE_IN_SIG_HANDLER) \ error(ROUDI__DEFAULT_ROUDI_MEMORY_FAILED_TO_ADD_SEGMENT_MANAGER_MEMORY_BLOCK) \ error(ROUDI__DEFAULT_ROUDI_MEMORY_FAILED_TO_ADD_INTROSPECTION_MEMORY_BLOCK) \ + error(ROUDI__DEFAULT_ROUDI_MEMORY_FAILED_TO_ADD_DISCOVERY_MEMORY_BLOCK) \ error(ROUDI__PRECONDITIONS_FOR_PROCESS_MANAGER_NOT_FULFILLED) \ error(MEMORY_PROVIDER__INSUFFICIENT_SEGMENT_IDS) \ error(ICEORYX_ROUDI_MEMORY_MANAGER__COULD_NOT_ACQUIRE_FILE_LOCK) \ diff --git a/iceoryx_posh/include/iceoryx_posh/iceoryx_posh_types.hpp b/iceoryx_posh/include/iceoryx_posh/iceoryx_posh_types.hpp index 823781f140..f312ddfa1c 100644 --- a/iceoryx_posh/include/iceoryx_posh/iceoryx_posh_types.hpp +++ b/iceoryx_posh/include/iceoryx_posh/iceoryx_posh_types.hpp @@ -226,6 +226,7 @@ constexpr uint16_t DEFAULT_UNIQUE_ROUDI_ID{0U}; // Timeout using namespace units::duration_literals; +constexpr units::Duration PROCESS_DEFAULT_TERMINATION_DELAY = 0_s; constexpr units::Duration PROCESS_DEFAULT_KILL_DELAY = 45_s; constexpr units::Duration PROCESS_TERMINATED_CHECK_INTERVAL = 250_ms; constexpr units::Duration DISCOVERY_INTERVAL = 100_ms; diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_listener.hpp b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_listener.hpp index ce82c3ee4c..1b78cfd3d5 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_listener.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/building_blocks/condition_listener.hpp @@ -46,7 +46,7 @@ class ConditionListener /// 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; + void destroy() volatile noexcept; /// @brief returns a sorted vector of indices of active notifications; blocking if ConditionVariableData was /// not notified unless destroy() was called before. The indices of active notifications are @@ -65,8 +65,8 @@ class ConditionListener NotificationVector_t timedWait(const units::Duration& timeToWait) noexcept; protected: - const ConditionVariableData* getMembers() const noexcept; - ConditionVariableData* getMembers() noexcept; + const ConditionVariableData* getMembers() volatile const noexcept; + ConditionVariableData* getMembers() volatile noexcept; private: void resetUnchecked(const uint64_t index) noexcept; 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 474619ba25..5501db3c1f 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/popo/wait_set.inl +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/wait_set.inl @@ -96,7 +96,7 @@ inline WaitSet::~WaitSet() noexcept } template -inline void WaitSet::markForDestruction() noexcept +inline void WaitSet::markForDestruction() volatile noexcept { m_conditionListener.destroy(); } 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 06ec06578a..cf5f9e9780 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/roudi/port_manager.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/roudi/port_manager.hpp @@ -198,7 +198,7 @@ class PortManager bool isInternal(const capro::ServiceDescription& service) const noexcept; - void publishServiceRegistry() const noexcept; + void publishServiceRegistry() noexcept; const ServiceRegistry& serviceRegistry() const noexcept; diff --git a/iceoryx_posh/include/iceoryx_posh/internal/roudi/process_manager.hpp b/iceoryx_posh/include/iceoryx_posh/internal/roudi/process_manager.hpp index a5b94bfb12..93dbe1141b 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/roudi/process_manager.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/roudi/process_manager.hpp @@ -92,9 +92,12 @@ class ProcessManager : public ProcessManagerInterface /// @brief Informs the user about the processes which are registered and then clears the process list void printWarningForRegisteredProcessesAndClearProcessList() noexcept; + /// @brief Returns the number of registered processes + uint64_t registeredProcessCount() const noexcept; + /// @brief Is one or more of the registered processes running? /// @return true if one or more of the registered processes is running, false otherwise - bool isAnyRegisteredProcessStillRunning() noexcept; + bool probeRegisteredProcessesAliveWithSigTerm() noexcept; /// @brief A process is about to shut down and needs to be unblock by a potentially block publisher /// @param [in] name of the process runtime which is about to shut down @@ -208,7 +211,7 @@ class ProcessManager : public ProcessManagerInterface /// @brief Evaluates with a kill SIGTERM signal to a process if he is still alive. /// @param [in] process The process to check. /// @return Returns true if the process is still alive, otherwise false. - bool isProcessAlive(const Process& process) noexcept; + bool probeProcessAliveWithSigTerm(const Process& process) noexcept; /// @brief Evaluates eventual upcoming errors from kill() command in requestShutdownOfProcess /// Calls the errorhandler. diff --git a/iceoryx_posh/include/iceoryx_posh/internal/roudi/roudi.hpp b/iceoryx_posh/include/iceoryx_posh/internal/roudi/roudi.hpp index 820bcbd2d5..4b821b9f48 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/roudi/roudi.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/roudi/roudi.hpp @@ -24,6 +24,7 @@ #include "iceoryx_posh/internal/roudi/introspection/mempool_introspection.hpp" #include "iceoryx_posh/internal/roudi/process_manager.hpp" #include "iceoryx_posh/internal/runtime/ipc_interface_creator.hpp" +#include "iceoryx_posh/popo/user_trigger.hpp" #include "iceoryx_posh/roudi/memory/roudi_memory_interface.hpp" #include "iceoryx_posh/roudi/memory/roudi_memory_manager.hpp" #include "iceoryx_posh/roudi/roudi_app.hpp" @@ -57,12 +58,14 @@ class RouDi const bool killProcessesInDestructor = true, const RuntimeMessagesThreadStart RuntimeMessagesThreadStart = RuntimeMessagesThreadStart::IMMEDIATE, const version::CompatibilityCheckLevel compatibilityCheckLevel = version::CompatibilityCheckLevel::PATCH, - const units::Duration processKillDelay = roudi::PROCESS_DEFAULT_KILL_DELAY) noexcept + const units::Duration processKillDelay = roudi::PROCESS_DEFAULT_KILL_DELAY, + const units::Duration processTerminationDelay = roudi::PROCESS_DEFAULT_TERMINATION_DELAY) noexcept : m_monitoringMode(monitoringMode) , m_killProcessesInDestructor(killProcessesInDestructor) , m_runtimesMessagesThreadStart(RuntimeMessagesThreadStart) , m_compatibilityCheckLevel(compatibilityCheckLevel) , m_processKillDelay(processKillDelay) + , m_processTerminationDelay(processTerminationDelay) { } @@ -71,6 +74,7 @@ class RouDi const RuntimeMessagesThreadStart m_runtimesMessagesThreadStart; const version::CompatibilityCheckLevel m_compatibilityCheckLevel; const units::Duration m_processKillDelay; + const units::Duration m_processTerminationDelay; }; RouDi& operator=(const RouDi& other) = delete; @@ -82,6 +86,11 @@ class RouDi virtual ~RouDi() noexcept; + /// @brief Triggers the discovery loop to run immediately instead of waiting for the next tick interval + /// @param[in] timeout is the time to wait to unblock the function call in case the discovery loop never signals to + /// have finished the run + void triggerDiscoveryLoopAndWaitToFinish(units::Duration timeout) noexcept; + protected: /// @brief Starts the thread processing messages from the runtimes /// Once this is done, applications can register and Roudi is fully operational. @@ -131,6 +140,9 @@ class RouDi std::atomic_bool m_runMonitoringAndDiscoveryThread; std::atomic_bool m_runHandleRuntimeMessageThread; + popo::UserTrigger m_discoveryLoopTrigger; + optional m_discoveryFinishedSemaphore; + const units::Duration m_runtimeMessagesThreadTimeout{100_ms}; protected: @@ -157,6 +169,7 @@ class RouDi private: roudi::MonitoringMode m_monitoringMode{roudi::MonitoringMode::ON}; + units::Duration m_processTerminationDelay; units::Duration m_processKillDelay; }; diff --git a/iceoryx_posh/include/iceoryx_posh/internal/roudi/service_registry.hpp b/iceoryx_posh/include/iceoryx_posh/internal/roudi/service_registry.hpp index 8b88e5a95d..fd4776abcb 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/roudi/service_registry.hpp +++ b/iceoryx_posh/include/iceoryx_posh/internal/roudi/service_registry.hpp @@ -91,11 +91,15 @@ class ServiceRegistry const optional& event, function_ref callable) const noexcept; - /// @brief Applys a callable to all entries + /// @brief Applies a callable to all entries /// @param[in] callable, callable to apply to each entry /// @note Can be used to obtain all entries or count them void forEach(function_ref callable) const noexcept; + /// @brief Checks whether the registry data changed since the last time this method was called + /// @return true when the registry changed since the last call, false otherwise + bool hasDataChangedSinceLastCall() noexcept; + private: using Entry_t = optional; using ServiceDescriptionContainer_t = vector; @@ -109,6 +113,8 @@ class ServiceRegistry // for the filling pattern of a vector (prefer entries close to the front) uint32_t m_freeIndex{NO_INDEX}; + bool m_dataChanged{true}; // initially true in order to also get notified of the empty registry + private: uint32_t findIndex(const capro::ServiceDescription& serviceDescription) const noexcept; diff --git a/iceoryx_posh/include/iceoryx_posh/popo/wait_set.hpp b/iceoryx_posh/include/iceoryx_posh/popo/wait_set.hpp index f2f3a7e843..8fa7ab3b93 100644 --- a/iceoryx_posh/include/iceoryx_posh/popo/wait_set.hpp +++ b/iceoryx_posh/include/iceoryx_posh/popo/wait_set.hpp @@ -73,7 +73,7 @@ class WaitSet /// not block any longer and never return triggered events/states. This /// method can be used to manually initialize destruction and to wakeup /// any thread which is waiting in wait() or timedWait(). - void markForDestruction() noexcept; + void markForDestruction() volatile noexcept; /// @brief attaches an event of a given class to the WaitSet. /// @note attachEvent does not take ownership of callback in the underlying eventCallback or the optional diff --git a/iceoryx_posh/include/iceoryx_posh/roudi/cmd_line_args.hpp b/iceoryx_posh/include/iceoryx_posh/roudi/cmd_line_args.hpp index 459db8a420..915c71d762 100644 --- a/iceoryx_posh/include/iceoryx_posh/roudi/cmd_line_args.hpp +++ b/iceoryx_posh/include/iceoryx_posh/roudi/cmd_line_args.hpp @@ -28,12 +28,13 @@ namespace config { struct CmdLineArgs_t { - roudi::MonitoringMode monitoringMode{roudi::MonitoringMode::OFF}; + bool run{true}; iox::log::LogLevel logLevel{iox::log::LogLevel::INFO}; + roudi::MonitoringMode monitoringMode{roudi::MonitoringMode::OFF}; version::CompatibilityCheckLevel compatibilityCheckLevel{version::CompatibilityCheckLevel::PATCH}; - units::Duration processKillDelay{roudi::PROCESS_DEFAULT_KILL_DELAY}; optional uniqueRouDiId{nullopt}; - bool run{true}; + units::Duration processTerminationDelay{roudi::PROCESS_DEFAULT_TERMINATION_DELAY}; + units::Duration processKillDelay{roudi::PROCESS_DEFAULT_KILL_DELAY}; roudi::ConfigFilePathString_t configFilePath; }; @@ -44,6 +45,7 @@ inline iox::log::LogStream& operator<<(iox::log::LogStream& logstream, const Cmd logstream << "Compatibility check level: " << cmdLineArgs.compatibilityCheckLevel << "\n"; cmdLineArgs.uniqueRouDiId.and_then([&logstream](auto& id) { logstream << "Unique RouDi ID: " << id << "\n"; }) .or_else([&logstream] { logstream << "Unique RouDi ID: < unset >\n"; }); + logstream << "Process termination delay: " << cmdLineArgs.processTerminationDelay.toSeconds() << " s\n"; logstream << "Process kill delay: " << cmdLineArgs.processKillDelay.toSeconds() << " s\n"; if (!cmdLineArgs.configFilePath.empty()) { diff --git a/iceoryx_posh/include/iceoryx_posh/roudi/memory/default_roudi_memory.hpp b/iceoryx_posh/include/iceoryx_posh/roudi/memory/default_roudi_memory.hpp index 7e0b875216..1d9b78d5c6 100644 --- a/iceoryx_posh/include/iceoryx_posh/roudi/memory/default_roudi_memory.hpp +++ b/iceoryx_posh/include/iceoryx_posh/roudi/memory/default_roudi_memory.hpp @@ -36,11 +36,14 @@ struct DefaultRouDiMemory DefaultRouDiMemory(const DefaultRouDiMemory&) = delete; DefaultRouDiMemory& operator=(const DefaultRouDiMemory&) = delete; - mepoo::MePooConfig introspectionMemPoolConfig() const noexcept; - MemPoolCollectionMemoryBlock m_introspectionMemPoolBlock; + MemPoolCollectionMemoryBlock m_discoveryMemPoolBlock; MemPoolSegmentManagerMemoryBlock m_segmentManagerBlock; PosixShmMemoryProvider m_managementShm; + + private: + mepoo::MePooConfig introspectionMemPoolConfig(const uint32_t chunkCount) const noexcept; + mepoo::MePooConfig discoveryMemPoolConfig(const uint32_t chunkCount) const noexcept; }; } // namespace roudi } // namespace iox diff --git a/iceoryx_posh/include/iceoryx_posh/roudi/memory/iceoryx_roudi_memory_manager.hpp b/iceoryx_posh/include/iceoryx_posh/roudi/memory/iceoryx_roudi_memory_manager.hpp index 3fe922534f..d5fac9d07c 100644 --- a/iceoryx_posh/include/iceoryx_posh/roudi/memory/iceoryx_roudi_memory_manager.hpp +++ b/iceoryx_posh/include/iceoryx_posh/roudi/memory/iceoryx_roudi_memory_manager.hpp @@ -55,6 +55,7 @@ class IceOryxRouDiMemoryManager : public RouDiMemoryInterface const PosixShmMemoryProvider* mgmtMemoryProvider() const noexcept override; optional portPool() noexcept override; optional introspectionMemoryManager() const noexcept override; + optional discoveryMemoryManager() const noexcept override; optional*> segmentManager() const noexcept override; private: diff --git a/iceoryx_posh/include/iceoryx_posh/roudi/memory/roudi_memory_interface.hpp b/iceoryx_posh/include/iceoryx_posh/roudi/memory/roudi_memory_interface.hpp index 612a56ab70..fe6e5084ff 100644 --- a/iceoryx_posh/include/iceoryx_posh/roudi/memory/roudi_memory_interface.hpp +++ b/iceoryx_posh/include/iceoryx_posh/roudi/memory/roudi_memory_interface.hpp @@ -57,6 +57,7 @@ class RouDiMemoryInterface virtual const PosixShmMemoryProvider* mgmtMemoryProvider() const noexcept = 0; virtual optional portPool() noexcept = 0; virtual optional introspectionMemoryManager() const noexcept = 0; + virtual optional discoveryMemoryManager() const noexcept = 0; virtual optional*> segmentManager() const noexcept = 0; }; } // namespace roudi diff --git a/iceoryx_posh/include/iceoryx_posh/roudi/memory/roudi_memory_manager.hpp b/iceoryx_posh/include/iceoryx_posh/roudi/memory/roudi_memory_manager.hpp index eb90379aab..b82ec49d04 100644 --- a/iceoryx_posh/include/iceoryx_posh/roudi/memory/roudi_memory_manager.hpp +++ b/iceoryx_posh/include/iceoryx_posh/roudi/memory/roudi_memory_manager.hpp @@ -78,7 +78,6 @@ class RouDiMemoryManager expected destroyMemory() noexcept; private: - mepoo::MePooConfig introspectionMemPoolConfig() const noexcept; vector m_memoryProvider; }; } // namespace roudi diff --git a/iceoryx_posh/include/iceoryx_posh/roudi/roudi_app.hpp b/iceoryx_posh/include/iceoryx_posh/roudi/roudi_app.hpp index c22d77befb..f7b27751a8 100644 --- a/iceoryx_posh/include/iceoryx_posh/roudi/roudi_app.hpp +++ b/iceoryx_posh/include/iceoryx_posh/roudi/roudi_app.hpp @@ -57,6 +57,7 @@ class RouDiApp RouDiConfig_t m_config; version::CompatibilityCheckLevel m_compatibilityCheckLevel{version::CompatibilityCheckLevel::PATCH}; + units::Duration m_processTeminationDelay{roudi::PROCESS_DEFAULT_TERMINATION_DELAY}; units::Duration m_processKillDelay{roudi::PROCESS_DEFAULT_KILL_DELAY}; private: diff --git a/iceoryx_posh/include/iceoryx_posh/roudi/roudi_cmd_line_parser.hpp b/iceoryx_posh/include/iceoryx_posh/roudi/roudi_cmd_line_parser.hpp index aa17bcd7ef..b63df1b14c 100644 --- a/iceoryx_posh/include/iceoryx_posh/roudi/roudi_cmd_line_parser.hpp +++ b/iceoryx_posh/include/iceoryx_posh/roudi/roudi_cmd_line_parser.hpp @@ -63,12 +63,7 @@ class CmdLineParser const CmdLineArgumentParsingMode cmdLineParsingMode = CmdLineArgumentParsingMode::ALL) noexcept; protected: - bool m_run{true}; - iox::log::LogLevel m_logLevel{iox::log::LogLevel::INFO}; - roudi::MonitoringMode m_monitoringMode{roudi::MonitoringMode::OFF}; - version::CompatibilityCheckLevel m_compatibilityCheckLevel{version::CompatibilityCheckLevel::PATCH}; - optional m_uniqueRouDiId; - units::Duration m_processKillDelay{roudi::PROCESS_DEFAULT_KILL_DELAY}; + CmdLineArgs_t m_cmdLineArgs; }; } // namespace config diff --git a/iceoryx_posh/include/iceoryx_posh/roudi/roudi_cmd_line_parser_config_file_option.hpp b/iceoryx_posh/include/iceoryx_posh/roudi/roudi_cmd_line_parser_config_file_option.hpp index 1114be7107..8340ab1cb1 100644 --- a/iceoryx_posh/include/iceoryx_posh/roudi/roudi_cmd_line_parser_config_file_option.hpp +++ b/iceoryx_posh/include/iceoryx_posh/roudi/roudi_cmd_line_parser_config_file_option.hpp @@ -42,9 +42,6 @@ class CmdLineParserConfigFileOption : public CmdLineParser parse(int argc, char* argv[], const CmdLineArgumentParsingMode cmdLineParsingMode = CmdLineArgumentParsingMode::ALL) noexcept override; - - protected: - roudi::ConfigFilePathString_t m_customConfigFilePath; }; } // namespace config diff --git a/iceoryx_posh/include/iceoryx_posh/roudi/roudi_config.hpp b/iceoryx_posh/include/iceoryx_posh/roudi/roudi_config.hpp index df3e5296af..ea7b5aa343 100644 --- a/iceoryx_posh/include/iceoryx_posh/roudi/roudi_config.hpp +++ b/iceoryx_posh/include/iceoryx_posh/roudi/roudi_config.hpp @@ -26,6 +26,13 @@ namespace config { struct RouDiConfig { + // have some spare chunks to still deliver introspection data in case there are multiple subscribers to the data + // which are caching different samples; could probably be reduced to 2 with the instruction to not cache the + // introspection samples + uint32_t introspectionChunkCount{10}; + + uint32_t discoveryChunkCount{10}; + RouDiConfig& setDefaults() noexcept; RouDiConfig& optimize() noexcept; }; diff --git a/iceoryx_posh/source/mepoo/mem_pool.cpp b/iceoryx_posh/source/mepoo/mem_pool.cpp index 7532d336fa..2fdc70c049 100644 --- a/iceoryx_posh/source/mepoo/mem_pool.cpp +++ b/iceoryx_posh/source/mepoo/mem_pool.cpp @@ -86,7 +86,7 @@ void* MemPool::getChunk() noexcept if (!m_freeIndices.pop(l_index)) { IOX_LOG(WARN) << "Mempool [m_chunkSize = " << m_chunkSize << ", numberOfChunks = " << m_numberOfChunks - << ", used_chunks = " << m_usedChunks << " ] has no more space left"; + << ", used_chunks = " << m_usedChunks.load() << " ] has no more space left"; return nullptr; } diff --git a/iceoryx_posh/source/popo/building_blocks/condition_listener.cpp b/iceoryx_posh/source/popo/building_blocks/condition_listener.cpp index 7709010ca4..5a37ff224e 100644 --- a/iceoryx_posh/source/popo/building_blocks/condition_listener.cpp +++ b/iceoryx_posh/source/popo/building_blocks/condition_listener.cpp @@ -43,7 +43,7 @@ void ConditionListener::resetSemaphore() noexcept } } -void ConditionListener::destroy() noexcept +void ConditionListener::destroy() volatile noexcept { m_toBeDestroyed.store(true, std::memory_order_relaxed); getMembers()->m_semaphore->post().or_else([](auto) { @@ -113,12 +113,12 @@ void ConditionListener::resetUnchecked(const uint64_t index) noexcept getMembers()->m_wasNotified.store(false, std::memory_order_relaxed); } -const ConditionVariableData* ConditionListener::getMembers() const noexcept +const ConditionVariableData* ConditionListener::getMembers() volatile const noexcept { return m_condVarDataPtr; } -ConditionVariableData* ConditionListener::getMembers() noexcept +ConditionVariableData* ConditionListener::getMembers() volatile noexcept { return m_condVarDataPtr; } diff --git a/iceoryx_posh/source/roudi/application/iceoryx_roudi_app.cpp b/iceoryx_posh/source/roudi/application/iceoryx_roudi_app.cpp index 722ad7de82..19cd9d183f 100644 --- a/iceoryx_posh/source/roudi/application/iceoryx_roudi_app.cpp +++ b/iceoryx_posh/source/roudi/application/iceoryx_roudi_app.cpp @@ -48,7 +48,8 @@ uint8_t IceOryxRouDiApp::run() noexcept true, RouDi::RuntimeMessagesThreadStart::IMMEDIATE, m_compatibilityCheckLevel, - m_processKillDelay}); + m_processKillDelay, + m_processTeminationDelay}); iox::posix::waitForTerminationRequest(); } return EXIT_SUCCESS; diff --git a/iceoryx_posh/source/roudi/application/roudi_app.cpp b/iceoryx_posh/source/roudi/application/roudi_app.cpp index 686ba1abe2..9f3d50c189 100644 --- a/iceoryx_posh/source/roudi/application/roudi_app.cpp +++ b/iceoryx_posh/source/roudi/application/roudi_app.cpp @@ -37,6 +37,7 @@ RouDiApp::RouDiApp(const config::CmdLineArgs_t& cmdLineArgs, const RouDiConfig_t , m_run(checkAndOptimizeConfig(config)) , m_config(config) , m_compatibilityCheckLevel(cmdLineArgs.compatibilityCheckLevel) + , m_processTeminationDelay(cmdLineArgs.processTerminationDelay) , m_processKillDelay(cmdLineArgs.processKillDelay) { // the "and" is intentional, just in case the the provided RouDiConfig_t is empty diff --git a/iceoryx_posh/source/roudi/memory/default_roudi_memory.cpp b/iceoryx_posh/source/roudi/memory/default_roudi_memory.cpp index d6e67cc922..9b8bd2eb0b 100644 --- a/iceoryx_posh/source/roudi/memory/default_roudi_memory.cpp +++ b/iceoryx_posh/source/roudi/memory/default_roudi_memory.cpp @@ -17,6 +17,7 @@ #include "iceoryx_posh/roudi/memory/default_roudi_memory.hpp" #include "iceoryx_posh/internal/mepoo/mem_pool.hpp" +#include "iceoryx_posh/internal/roudi/service_registry.hpp" #include "iceoryx_posh/roudi/introspection_types.hpp" #include "iox/memory.hpp" @@ -25,7 +26,8 @@ namespace iox namespace roudi { DefaultRouDiMemory::DefaultRouDiMemory(const RouDiConfig_t& roudiConfig) noexcept - : m_introspectionMemPoolBlock(introspectionMemPoolConfig()) + : m_introspectionMemPoolBlock(introspectionMemPoolConfig(roudiConfig.introspectionChunkCount)) + , m_discoveryMemPoolBlock(discoveryMemPoolConfig(roudiConfig.discoveryChunkCount)) , m_segmentManagerBlock(roudiConfig) , m_managementShm(SHM_NAME, posix::AccessMode::READ_WRITE, posix::OpenMode::PURGE_AND_CREATE) { @@ -33,30 +35,41 @@ DefaultRouDiMemory::DefaultRouDiMemory(const RouDiConfig_t& roudiConfig) noexcep errorHandler(PoshError::ROUDI__DEFAULT_ROUDI_MEMORY_FAILED_TO_ADD_INTROSPECTION_MEMORY_BLOCK, ErrorLevel::FATAL); }); + m_managementShm.addMemoryBlock(&m_discoveryMemPoolBlock).or_else([](auto) { + errorHandler(PoshError::ROUDI__DEFAULT_ROUDI_MEMORY_FAILED_TO_ADD_DISCOVERY_MEMORY_BLOCK, ErrorLevel::FATAL); + }); m_managementShm.addMemoryBlock(&m_segmentManagerBlock).or_else([](auto) { errorHandler(PoshError::ROUDI__DEFAULT_ROUDI_MEMORY_FAILED_TO_ADD_SEGMENT_MANAGER_MEMORY_BLOCK, ErrorLevel::FATAL); }); } -mepoo::MePooConfig DefaultRouDiMemory::introspectionMemPoolConfig() const noexcept + +mepoo::MePooConfig DefaultRouDiMemory::introspectionMemPoolConfig(const uint32_t chunkCount) const noexcept { constexpr uint32_t ALIGNMENT{mepoo::MemPool::CHUNK_MEMORY_ALIGNMENT}; - // have some spare chunks to still deliver introspection data in case there are multiple subscriber to the data - // which are caching different samples; could probably be reduced to 2 with the instruction to not cache the - // introspection samples - constexpr uint32_t CHUNK_COUNT{10U}; mepoo::MePooConfig mempoolConfig; mempoolConfig.m_mempoolConfig.push_back( - {align(static_cast(sizeof(roudi::MemPoolIntrospectionInfoContainer)), ALIGNMENT), CHUNK_COUNT}); + {align(static_cast(sizeof(roudi::MemPoolIntrospectionInfoContainer)), ALIGNMENT), chunkCount}); mempoolConfig.m_mempoolConfig.push_back( - {align(static_cast(sizeof(roudi::ProcessIntrospectionFieldTopic)), ALIGNMENT), CHUNK_COUNT}); + {align(static_cast(sizeof(roudi::ProcessIntrospectionFieldTopic)), ALIGNMENT), chunkCount}); mempoolConfig.m_mempoolConfig.push_back( - {align(static_cast(sizeof(roudi::PortIntrospectionFieldTopic)), ALIGNMENT), CHUNK_COUNT}); + {align(static_cast(sizeof(roudi::PortIntrospectionFieldTopic)), ALIGNMENT), chunkCount}); mempoolConfig.m_mempoolConfig.push_back( - {align(static_cast(sizeof(roudi::PortThroughputIntrospectionFieldTopic)), ALIGNMENT), CHUNK_COUNT}); + {align(static_cast(sizeof(roudi::PortThroughputIntrospectionFieldTopic)), ALIGNMENT), chunkCount}); mempoolConfig.m_mempoolConfig.push_back( {align(static_cast(sizeof(roudi::SubscriberPortChangingIntrospectionFieldTopic)), ALIGNMENT), - CHUNK_COUNT}); + chunkCount}); + + mempoolConfig.optimize(); + return mempoolConfig; +} + +mepoo::MePooConfig DefaultRouDiMemory::discoveryMemPoolConfig(const uint32_t chunkCount) const noexcept +{ + constexpr uint32_t ALIGNMENT{mepoo::MemPool::CHUNK_MEMORY_ALIGNMENT}; + mepoo::MePooConfig mempoolConfig; + mempoolConfig.m_mempoolConfig.push_back( + {align(static_cast(sizeof(roudi::ServiceRegistry)), ALIGNMENT), chunkCount}); mempoolConfig.optimize(); return mempoolConfig; diff --git a/iceoryx_posh/source/roudi/memory/iceoryx_roudi_memory_manager.cpp b/iceoryx_posh/source/roudi/memory/iceoryx_roudi_memory_manager.cpp index 3c404d7e59..6c3e638659 100644 --- a/iceoryx_posh/source/roudi/memory/iceoryx_roudi_memory_manager.cpp +++ b/iceoryx_posh/source/roudi/memory/iceoryx_roudi_memory_manager.cpp @@ -63,6 +63,11 @@ optional IceOryxRouDiMemoryManager::introspectionMemoryMa return m_defaultMemory.m_introspectionMemPoolBlock.memoryManager(); } +optional IceOryxRouDiMemoryManager::discoveryMemoryManager() const noexcept +{ + return m_defaultMemory.m_discoveryMemPoolBlock.memoryManager(); +} + optional*> IceOryxRouDiMemoryManager::segmentManager() const noexcept { return m_defaultMemory.m_segmentManagerBlock.segmentManager(); diff --git a/iceoryx_posh/source/roudi/port_manager.cpp b/iceoryx_posh/source/roudi/port_manager.cpp old mode 100755 new mode 100644 index 61a24ac3f3..10d8a802b9 --- a/iceoryx_posh/source/roudi/port_manager.cpp +++ b/iceoryx_posh/source/roudi/port_manager.cpp @@ -53,13 +53,13 @@ PortManager::PortManager(RouDiMemoryInterface* roudiMemoryInterface) noexcept } m_portPool = maybePortPool.value(); - auto maybeIntrospectionMemoryManager = m_roudiMemoryInterface->introspectionMemoryManager(); - if (!maybeIntrospectionMemoryManager.has_value()) + auto maybediscoveryMemoryManager = m_roudiMemoryInterface->discoveryMemoryManager(); + if (!maybediscoveryMemoryManager.has_value()) { - IOX_LOG(FATAL) << "Could not get MemoryManager for introspection!"; - errorHandler(PoshError::PORT_MANAGER__INTROSPECTION_MEMORY_MANAGER_UNAVAILABLE, iox::ErrorLevel::FATAL); + IOX_LOG(FATAL) << "Could not get MemoryManager for discovery!"; + errorHandler(PoshError::PORT_MANAGER__DISCOVERY_MEMORY_MANAGER_UNAVAILABLE, iox::ErrorLevel::FATAL); } - auto introspectionMemoryManager = maybeIntrospectionMemoryManager.value(); + auto& discoveryMemoryManager = maybediscoveryMemoryManager.value(); popo::PublisherOptions registryPortOptions; registryPortOptions.historyCapacity = 1U; @@ -70,12 +70,20 @@ PortManager::PortManager(RouDiMemoryInterface* roudiMemoryInterface) noexcept m_serviceRegistryPublisherPortData = acquireInternalPublisherPortDataWithoutDiscovery( {SERVICE_DISCOVERY_SERVICE_NAME, SERVICE_DISCOVERY_INSTANCE_NAME, SERVICE_DISCOVERY_EVENT_NAME}, registryPortOptions, - introspectionMemoryManager); + discoveryMemoryManager); // if we arrive here, the port for service discovery exists and we perform the discovery PublisherPortRouDiType serviceRegistryPort(*m_serviceRegistryPublisherPortData); doDiscoveryForPublisherPort(serviceRegistryPort); + auto maybeIntrospectionMemoryManager = m_roudiMemoryInterface->introspectionMemoryManager(); + if (!maybeIntrospectionMemoryManager.has_value()) + { + IOX_LOG(FATAL) << "Could not get MemoryManager for introspection!"; + errorHandler(PoshError::PORT_MANAGER__INTROSPECTION_MEMORY_MANAGER_UNAVAILABLE, iox::ErrorLevel::FATAL); + } + auto& introspectionMemoryManager = maybeIntrospectionMemoryManager.value(); + popo::PublisherOptions options; options.historyCapacity = 1U; options.nodeName = INTROSPECTION_NODE_NAME; @@ -115,6 +123,8 @@ void PortManager::doDiscovery() noexcept handleNodes(); handleConditionVariables(); + + publishServiceRegistry(); } void PortManager::handlePublisherPorts() noexcept @@ -1036,8 +1046,13 @@ popo::InterfacePortData* PortManager::acquireInterfacePortData(capro::Interfaces } } -void PortManager::publishServiceRegistry() const noexcept +void PortManager::publishServiceRegistry() noexcept { + if (!m_serviceRegistry.hasDataChangedSinceLastCall()) + { + return; + } + if (!m_serviceRegistryPublisherPortData.has_value()) { // should not happen (except during RouDi shutdown) @@ -1071,13 +1086,11 @@ void PortManager::addPublisherToServiceRegistry(const capro::ServiceDescription& IOX_LOG(WARN) << "Could not add publisher with service description '" << service << "' to service registry!"; errorHandler(PoshError::POSH__PORT_MANAGER_COULD_NOT_ADD_SERVICE_TO_REGISTRY, ErrorLevel::MODERATE); }); - publishServiceRegistry(); } void PortManager::removePublisherFromServiceRegistry(const capro::ServiceDescription& service) noexcept { m_serviceRegistry.removePublisher(service); - publishServiceRegistry(); } void PortManager::addServerToServiceRegistry(const capro::ServiceDescription& service) noexcept @@ -1086,13 +1099,11 @@ void PortManager::addServerToServiceRegistry(const capro::ServiceDescription& se IOX_LOG(WARN) << "Could not add server with service description '" << service << "' to service registry!"; errorHandler(PoshError::POSH__PORT_MANAGER_COULD_NOT_ADD_SERVICE_TO_REGISTRY, ErrorLevel::MODERATE); }); - publishServiceRegistry(); } void PortManager::removeServerFromServiceRegistry(const capro::ServiceDescription& service) noexcept { m_serviceRegistry.removeServer(service); - publishServiceRegistry(); } expected PortManager::acquireNodeData(const RuntimeName_t& runtimeName, diff --git a/iceoryx_posh/source/roudi/process_manager.cpp b/iceoryx_posh/source/roudi/process_manager.cpp index 15f8929928..b18c5da00f 100644 --- a/iceoryx_posh/source/roudi/process_manager.cpp +++ b/iceoryx_posh/source/roudi/process_manager.cpp @@ -94,6 +94,7 @@ void ProcessManager::requestShutdownOfAllProcesses() noexcept // send SIG_TERM to all running applications and wait for processes to answer with TERMINATION for (auto& process : m_processList) { + IOX_LOG(DEBUG) << "Sending SIGTERM to Process ID " << process.getPid() << " named '" << process.getName(); requestShutdownOfProcess(process, ShutdownPolicy::SIG_TERM); } @@ -101,11 +102,16 @@ void ProcessManager::requestShutdownOfAllProcesses() noexcept m_portManager.unblockRouDiShutdown(); } -bool ProcessManager::isAnyRegisteredProcessStillRunning() noexcept +uint64_t ProcessManager::registeredProcessCount() const noexcept +{ + return m_processList.size(); +} + +bool ProcessManager::probeRegisteredProcessesAliveWithSigTerm() noexcept { for (auto& process : m_processList) { - if (isProcessAlive(process)) + if (probeProcessAliveWithSigTerm(process)) { return true; } @@ -147,7 +153,7 @@ bool ProcessManager::requestShutdownOfProcess(Process& process, ShutdownPolicy s .has_error(); } -bool ProcessManager::isProcessAlive(const Process& process) noexcept +bool ProcessManager::probeProcessAliveWithSigTerm(const Process& process) noexcept { static constexpr int32_t ERROR_CODE = -1; auto checkCommand = posix::posixCall(kill)(static_cast(process.getPid()), SIGTERM) diff --git a/iceoryx_posh/source/roudi/roudi.cpp b/iceoryx_posh/source/roudi/roudi.cpp index 8abc9ff9ff..12e2fb37ef 100644 --- a/iceoryx_posh/source/roudi/roudi.cpp +++ b/iceoryx_posh/source/roudi/roudi.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2019, 2021 by Robert Bosch GmbH. All rights reserved. // Copyright (c) 2021 - 2022 by Apex.AI Inc. All rights reserved. +// Copyright (c) 2023 by Mathias Kraus . 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,6 +24,7 @@ #include "iceoryx_hoofs/posix_wrapper/thread.hpp" #include "iceoryx_posh/internal/runtime/node_property.hpp" #include "iceoryx_posh/popo/subscriber_options.hpp" +#include "iceoryx_posh/popo/wait_set.hpp" #include "iceoryx_posh/roudi/introspection_types.hpp" #include "iceoryx_posh/runtime/port_config_info.hpp" #include "iox/logging.hpp" @@ -48,6 +50,7 @@ RouDi::RouDi(RouDiMemoryInterface& roudiMemoryInterface, *m_roudiMemoryInterface->segmentManager().value(), PublisherPortUserType(m_prcMgr->addIntrospectionPublisherPort(IntrospectionMempoolService))) , m_monitoringMode(roudiStartupParameters.m_monitoringMode) + , m_processTerminationDelay(roudiStartupParameters.m_processTerminationDelay) , m_processKillDelay(roudiStartupParameters.m_processKillDelay) { if (internal::isCompiledOn32BitSystem()) @@ -63,6 +66,13 @@ RouDi::RouDi(RouDiMemoryInterface& roudiMemoryInterface, // since RouDi offers the introspection services, also add it to the list of processes m_processIntrospection.addProcess(getpid(), IPC_CHANNEL_ROUDI_NAME); + // initialize semaphore for discovery loop finish indicator + iox::posix::UnnamedSemaphoreBuilder() + .initialValue(0U) + .isInterProcessCapable(false) + .create(m_discoveryFinishedSemaphore) + .expect("Valid Semaphore"); + // run the threads m_monitoringAndDiscoveryThread = std::thread(&RouDi::monitorAndDiscoveryUpdate, this); posix::setThreadName(m_monitoringAndDiscoveryThread.native_handle(), "Mon+Discover"); @@ -86,11 +96,17 @@ void RouDi::startProcessRuntimeMessagesThread() noexcept void RouDi::shutdown() noexcept { + // trigger the shutdown of the monitoring and discovery thread in order to prevent application to register while + // shutting down + m_runMonitoringAndDiscoveryThread = false; + m_discoveryLoopTrigger.trigger(); + + // stop the introspection m_processIntrospection.stop(); + m_mempoolIntrospection.stop(); m_portManager->stopPortIntrospection(); - // stop the process management thread in order to prevent application to register while shutting down - m_runMonitoringAndDiscoveryThread = false; + // wait for the monitoring and discovery thread to stop if (m_monitoringAndDiscoveryThread.joinable()) { IOX_LOG(DEBUG) << "Joining 'Mon+Discover' thread..."; @@ -100,13 +116,25 @@ void RouDi::shutdown() noexcept if (m_killProcessesInDestructor) { - deadline_timer finalKillTimer(m_processKillDelay); + deadline_timer terminationDelayTimer(m_processTerminationDelay); + using namespace units::duration_literals; + auto remainingDurationForInfoPrint = m_processTerminationDelay - 1_s; + while (!terminationDelayTimer.hasExpired() && m_prcMgr->registeredProcessCount() > 0) + { + if (remainingDurationForInfoPrint > terminationDelayTimer.remainingTime()) + { + IOX_LOG(WARN) << "Some applications seem to be still running! Time until graceful shutdown: " + << terminationDelayTimer.remainingTime().toSeconds() << "s!"; + remainingDurationForInfoPrint = remainingDurationForInfoPrint - 5_s; + } + std::this_thread::sleep_for(std::chrono::milliseconds(PROCESS_TERMINATED_CHECK_INTERVAL.toMilliseconds())); + } m_prcMgr->requestShutdownOfAllProcesses(); - using namespace units::duration_literals; + deadline_timer finalKillTimer(m_processKillDelay); auto remainingDurationForWarnPrint = m_processKillDelay - 2_s; - while (m_prcMgr->isAnyRegisteredProcessStillRunning() && !finalKillTimer.hasExpired()) + while (m_prcMgr->probeRegisteredProcessesAliveWithSigTerm() && !finalKillTimer.hasExpired()) { if (remainingDurationForWarnPrint > finalKillTimer.remainingTime()) { @@ -119,13 +147,13 @@ void RouDi::shutdown() noexcept } // Is any processes still alive? - if (m_prcMgr->isAnyRegisteredProcessStillRunning() && finalKillTimer.hasExpired()) + if (m_prcMgr->probeRegisteredProcessesAliveWithSigTerm() && finalKillTimer.hasExpired()) { // Time to kill them m_prcMgr->killAllProcesses(); } - if (m_prcMgr->isAnyRegisteredProcessStillRunning()) + if (m_prcMgr->probeRegisteredProcessesAliveWithSigTerm()) { m_prcMgr->printWarningForRegisteredProcessesAndClearProcessList(); } @@ -147,15 +175,67 @@ void RouDi::cyclicUpdateHook() noexcept // default implementation; do nothing } +void RouDi::triggerDiscoveryLoopAndWaitToFinish(units::Duration timeout) noexcept +{ + bool decrementSemaphoreCount{true}; + while (decrementSemaphoreCount) + { + m_discoveryFinishedSemaphore->tryWait() + .and_then([&decrementSemaphoreCount](const auto& countNonZero) { decrementSemaphoreCount = countNonZero; }) + .or_else([&decrementSemaphoreCount](const auto& error) { + decrementSemaphoreCount = false; + IOX_LOG(ERROR) << "Could not decrement count of the semaphore which signals a finished run of the " + "discovery loop! Error: " + << static_cast(error); + }); + } + m_discoveryLoopTrigger.trigger(); + m_discoveryFinishedSemaphore->timedWait(timeout).or_else([](const auto& error) { + IOX_LOG(ERROR) << "A timed wait on the semaphore which signals a finished run of the " + "discovery loop failed! Error: " + << static_cast(error); + }); +} + void RouDi::monitorAndDiscoveryUpdate() noexcept { + class DiscoveryWaitSet : public popo::WaitSet<1> + { + public: + DiscoveryWaitSet(popo::ConditionVariableData& condVarData) noexcept + : WaitSet(condVarData) + { + } + }; + + popo::ConditionVariableData conditionVariableData; + DiscoveryWaitSet discoveryLoopWaitset{conditionVariableData}; + discoveryLoopWaitset.attachEvent(m_discoveryLoopTrigger).expect("Successfully attaching a single event"); + bool manuallyTriggered{false}; + while (m_runMonitoringAndDiscoveryThread) { m_prcMgr->run(); cyclicUpdateHook(); - std::this_thread::sleep_for(std::chrono::milliseconds(DISCOVERY_INTERVAL.toMilliseconds())); + if (manuallyTriggered) + { + m_discoveryFinishedSemaphore->post().or_else([](const auto& error) { + IOX_LOG(ERROR) << "Could not trigger semaphore to signal a finished run of the discovery loop! Error: " + << static_cast(error); + }); + } + + manuallyTriggered = false; + for (const auto& notification : discoveryLoopWaitset.timedWait(DISCOVERY_INTERVAL)) + { + if (notification->doesOriginateFrom(&m_discoveryLoopTrigger)) + { + manuallyTriggered = true; + break; + } + } } } diff --git a/iceoryx_posh/source/roudi/roudi_cmd_line_parser.cpp b/iceoryx_posh/source/roudi/roudi_cmd_line_parser.cpp index f497543db3..2028206e1b 100644 --- a/iceoryx_posh/source/roudi/roudi_cmd_line_parser.cpp +++ b/iceoryx_posh/source/roudi/roudi_cmd_line_parser.cpp @@ -36,11 +36,12 @@ CmdLineParser::parse(int argc, char* argv[], const CmdLineArgumentParsingMode cm {"log-level", required_argument, nullptr, 'l'}, {"unique-roudi-id", required_argument, nullptr, 'u'}, {"compatibility", required_argument, nullptr, 'x'}, + {"termination-delay", required_argument, nullptr, 't'}, {"kill-delay", required_argument, nullptr, 'k'}, {nullptr, 0, nullptr, 0}}; // colon after shortOption means it requires an argument, two colons mean optional argument - constexpr const char* SHORT_OPTIONS = "hvm:l:u:x:k:"; + constexpr const char* SHORT_OPTIONS = "hvm:l:u:x:t:k:"; int index; int32_t opt{-1}; while ((opt = getopt_long(argc, argv, SHORT_OPTIONS, LONG_OPTIONS, &index), opt != -1)) @@ -59,28 +60,41 @@ CmdLineParser::parse(int argc, char* argv[], const CmdLineArgumentParsingMode cm std::cout << " on: enables monitoring for all processes" << std::endl; std::cout << " off: disables monitoring for all processes" << std::endl; std::cout << "-l, --log-level Set log level." << std::endl; - std::cout << " {off, fatal, error, warning, info, debug, trace}" - << std::endl; - std::cout << "-x, --compatibility Set compatibility check level between runtime and RouDi." - << std::endl; + std::cout << " {off, fatal, error, warning, info," << std::endl; + std::cout << " debug, trace}" << std::endl; + std::cout << " default = 'info'" << std::endl; + std::cout << "-x, --compatibility Set compatibility check level between runtime" << std::endl; + std::cout << " and RouDi. Value are" << std::endl; std::cout << " off: no check" << std::endl; std::cout << " major: same major version " << std::endl; std::cout << " minor: same minor version + major check" << std::endl; std::cout << " patch: same patch version + minor check" << std::endl; std::cout << " commitId: same commit ID + patch check" << std::endl; std::cout << " buildDate: same build date + commId check" << std::endl; - std::cout << "-k, --kill-delay Sets the delay when RouDi sends SIG_KILL, if apps" - << std::endl; - std::cout << " have't responded after trying SIG_TERM first, in seconds." - << std::endl; + std::cout << " default = 'patch'" << std::endl; + std::cout << "-t, --termination-delay Sets the delay in seconds before RouDi sends" << std::endl; + std::cout << " SIGTERM to running applications at shutdown." << std::endl; + std::cout << " When RouDi and the applications are running" << std::endl; + std::cout << " in an automated environment like" << std::endl; + std::cout << " launch_testing, where the framework takes" << std::endl; + std::cout << " care of the shutdown, this results in a race" << std::endl; + std::cout << " between RouDi and the framework in" << std::endl; + std::cout << " terminating the applications. To prevent this" << std::endl; + std::cout << " race, this parameter can be used to delay the" << std::endl; + std::cout << " raising of SIGTERM by a few seconds." << std::endl; + std::cout << " default = '0'" << std::endl; + std::cout << "-k, --kill-delay Sets the delay in seconds before RouDi sends" << std::endl; + std::cout << " SIGKILL to application which did not respond" << std::endl; + std::cout << " to the initial SIGTERM signal." << std::endl; + std::cout << " default = '45'" << std::endl; - m_run = false; + m_cmdLineArgs.run = false; break; case 'v': std::cout << "RouDi version: " << ICEORYX_LATEST_RELEASE_VERSION << std::endl; std::cout << "Build date: " << ICEORYX_BUILDDATE << std::endl; std::cout << "Commit ID: " << ICEORYX_SHA1 << std::endl; - m_run = false; + m_cmdLineArgs.run = false; break; case 'u': { @@ -89,25 +103,25 @@ CmdLineParser::parse(int argc, char* argv[], const CmdLineArgumentParsingMode cm if (!cxx::convert::fromString(optarg, roudiId)) { IOX_LOG(ERROR) << "The RouDi id must be in the range of [0, " << MAX_ROUDI_ID << "]"; - m_run = false; + m_cmdLineArgs.run = false; } - m_uniqueRouDiId.emplace(roudiId); + m_cmdLineArgs.uniqueRouDiId.emplace(roudiId); break; } case 'm': { if (strcmp(optarg, "on") == 0) { - m_monitoringMode = roudi::MonitoringMode::ON; + m_cmdLineArgs.monitoringMode = roudi::MonitoringMode::ON; } else if (strcmp(optarg, "off") == 0) { - m_monitoringMode = roudi::MonitoringMode::OFF; + m_cmdLineArgs.monitoringMode = roudi::MonitoringMode::OFF; } else { - m_run = false; + m_cmdLineArgs.run = false; IOX_LOG(ERROR) << "Options for monitoring-mode are 'on' and 'off'!"; } break; @@ -116,40 +130,56 @@ CmdLineParser::parse(int argc, char* argv[], const CmdLineArgumentParsingMode cm { if (strcmp(optarg, "off") == 0) { - m_logLevel = iox::log::LogLevel::OFF; + m_cmdLineArgs.logLevel = iox::log::LogLevel::OFF; } else if (strcmp(optarg, "fatal") == 0) { - m_logLevel = iox::log::LogLevel::FATAL; + m_cmdLineArgs.logLevel = iox::log::LogLevel::FATAL; } else if (strcmp(optarg, "error") == 0) { - m_logLevel = iox::log::LogLevel::ERROR; + m_cmdLineArgs.logLevel = iox::log::LogLevel::ERROR; } else if (strcmp(optarg, "warning") == 0) { - m_logLevel = iox::log::LogLevel::WARN; + m_cmdLineArgs.logLevel = iox::log::LogLevel::WARN; } else if (strcmp(optarg, "info") == 0) { - m_logLevel = iox::log::LogLevel::INFO; + m_cmdLineArgs.logLevel = iox::log::LogLevel::INFO; } else if (strcmp(optarg, "debug") == 0) { - m_logLevel = iox::log::LogLevel::DEBUG; + m_cmdLineArgs.logLevel = iox::log::LogLevel::DEBUG; } else if (strcmp(optarg, "trace") == 0) { - m_logLevel = iox::log::LogLevel::TRACE; + m_cmdLineArgs.logLevel = iox::log::LogLevel::TRACE; } else { - m_run = false; + m_cmdLineArgs.run = false; IOX_LOG(ERROR) << "Options for log-level are 'off', 'fatal', 'error', 'warning', 'info', 'debug' and " "'trace'!"; } break; } + case 't': + { + uint32_t processTerminationDelayInSeconds{0u}; + constexpr uint64_t MAX_PROCESS_TERMINATION_DELAY = std::numeric_limits::max(); + if (!cxx::convert::fromString(optarg, processTerminationDelayInSeconds)) + { + IOX_LOG(ERROR) << "The process termination delay must be in the range of [0, " + << MAX_PROCESS_TERMINATION_DELAY << "]"; + m_cmdLineArgs.run = false; + } + else + { + m_cmdLineArgs.processTerminationDelay = units::Duration::fromSeconds(processTerminationDelayInSeconds); + } + break; + } case 'k': { uint32_t processKillDelayInSeconds{0u}; @@ -158,11 +188,11 @@ CmdLineParser::parse(int argc, char* argv[], const CmdLineArgumentParsingMode cm { IOX_LOG(ERROR) << "The process kill delay must be in the range of [0, " << MAX_PROCESS_KILL_DELAY << "]"; - m_run = false; + m_cmdLineArgs.run = false; } else { - m_processKillDelay = units::Duration::fromSeconds(processKillDelayInSeconds); + m_cmdLineArgs.processKillDelay = units::Duration::fromSeconds(processKillDelayInSeconds); } break; } @@ -170,31 +200,31 @@ CmdLineParser::parse(int argc, char* argv[], const CmdLineArgumentParsingMode cm { if (strcmp(optarg, "off") == 0) { - m_compatibilityCheckLevel = iox::version::CompatibilityCheckLevel::OFF; + m_cmdLineArgs.compatibilityCheckLevel = iox::version::CompatibilityCheckLevel::OFF; } else if (strcmp(optarg, "major") == 0) { - m_compatibilityCheckLevel = iox::version::CompatibilityCheckLevel::MAJOR; + m_cmdLineArgs.compatibilityCheckLevel = iox::version::CompatibilityCheckLevel::MAJOR; } else if (strcmp(optarg, "minor") == 0) { - m_compatibilityCheckLevel = iox::version::CompatibilityCheckLevel::MINOR; + m_cmdLineArgs.compatibilityCheckLevel = iox::version::CompatibilityCheckLevel::MINOR; } else if (strcmp(optarg, "patch") == 0) { - m_compatibilityCheckLevel = iox::version::CompatibilityCheckLevel::PATCH; + m_cmdLineArgs.compatibilityCheckLevel = iox::version::CompatibilityCheckLevel::PATCH; } else if (strcmp(optarg, "commitId") == 0) { - m_compatibilityCheckLevel = iox::version::CompatibilityCheckLevel::COMMIT_ID; + m_cmdLineArgs.compatibilityCheckLevel = iox::version::CompatibilityCheckLevel::COMMIT_ID; } else if (strcmp(optarg, "buildDate") == 0) { - m_compatibilityCheckLevel = iox::version::CompatibilityCheckLevel::BUILD_DATE; + m_cmdLineArgs.compatibilityCheckLevel = iox::version::CompatibilityCheckLevel::BUILD_DATE; } else { - m_run = false; + m_cmdLineArgs.run = false; IOX_LOG(ERROR) << "Options for compatibility are 'off', 'major', 'minor', 'patch', 'commitId' and 'buildDate'!"; } @@ -203,7 +233,7 @@ CmdLineParser::parse(int argc, char* argv[], const CmdLineArgumentParsingMode cm default: { // CmdLineParser did not understand the parameters, don't run - m_run = false; + m_cmdLineArgs.run = false; return err(CmdLineParserResult::UNKNOWN_OPTION_USED); } }; @@ -213,13 +243,7 @@ CmdLineParser::parse(int argc, char* argv[], const CmdLineArgumentParsingMode cm break; } } - return ok(CmdLineArgs_t{m_monitoringMode, - m_logLevel, - m_compatibilityCheckLevel, - m_processKillDelay, - m_uniqueRouDiId, - m_run, - iox::roudi::ConfigFilePathString_t("")}); + return ok(m_cmdLineArgs); } // namespace roudi } // namespace config } // namespace iox diff --git a/iceoryx_posh/source/roudi/roudi_cmd_line_parser_config_file_option.cpp b/iceoryx_posh/source/roudi/roudi_cmd_line_parser_config_file_option.cpp index a3b1e24d3f..072b12dfc1 100644 --- a/iceoryx_posh/source/roudi/roudi_cmd_line_parser_config_file_option.cpp +++ b/iceoryx_posh/source/roudi/roudi_cmd_line_parser_config_file_option.cpp @@ -57,12 +57,12 @@ expected CmdLineParserConfigFileOption::pars << std::endl; std::cout << " 1) /etc/iceoryx/roudi_config.toml" << std::endl; std::cout << " 2) hard-coded config" << std::endl; - m_run = false; + m_cmdLineArgs.run = false; break; } case 'c': { - m_customConfigFilePath = roudi::ConfigFilePathString_t(TruncateToCapacity, optarg); + m_cmdLineArgs.configFilePath = roudi::ConfigFilePathString_t(TruncateToCapacity, optarg); break; } default: @@ -82,13 +82,7 @@ expected CmdLineParserConfigFileOption::pars break; } } - return ok(CmdLineArgs_t{m_monitoringMode, - m_logLevel, - m_compatibilityCheckLevel, - m_processKillDelay, - m_uniqueRouDiId, - m_run, - m_customConfigFilePath}); + return ok(m_cmdLineArgs); } } // namespace config diff --git a/iceoryx_posh/source/roudi/service_registry.cpp b/iceoryx_posh/source/roudi/service_registry.cpp index a1280cfc25..08d9d6ebf6 100644 --- a/iceoryx_posh/source/roudi/service_registry.cpp +++ b/iceoryx_posh/source/roudi/service_registry.cpp @@ -37,6 +37,7 @@ expected ServiceRegistry::add(const capro::Service // entry exists, increment counter auto& entry = m_serviceDescriptions[index]; ((*entry).*count)++; + m_dataChanged = true; return ok(); } @@ -49,6 +50,7 @@ expected ServiceRegistry::add(const capro::Service auto& entry = m_serviceDescriptions[m_freeIndex]; entry.emplace(serviceDescription); (*entry).*count = 1U; + m_dataChanged = true; m_freeIndex = NO_INDEX; return ok(); } @@ -60,6 +62,7 @@ expected ServiceRegistry::add(const capro::Service { entry.emplace(serviceDescription); (*entry).*count = 1U; + m_dataChanged = true; return ok(); } } @@ -70,6 +73,7 @@ expected ServiceRegistry::add(const capro::Service auto& entry = m_serviceDescriptions.back(); entry.emplace(serviceDescription); (*entry).*count = 1U; + m_dataChanged = true; return ok(); } @@ -102,6 +106,7 @@ void ServiceRegistry::removePublisher(const capro::ServiceDescription& serviceDe entry.reset(); // reuse the slot in the next insertion m_freeIndex = index; + m_dataChanged = true; } } } @@ -121,6 +126,7 @@ void ServiceRegistry::removeServer(const capro::ServiceDescription& serviceDescr entry.reset(); // reuse the slot in the next insertion m_freeIndex = index; + m_dataChanged = true; } } } @@ -135,6 +141,7 @@ void ServiceRegistry::purge(const capro::ServiceDescription& serviceDescription) entry.reset(); // reuse the slot in the next insertion m_freeIndex = index; + m_dataChanged = true; } } @@ -183,5 +190,12 @@ void ServiceRegistry::forEach(function_ref } } +bool ServiceRegistry::hasDataChangedSinceLastCall() noexcept +{ + auto dataChanged = m_dataChanged; + m_dataChanged = false; + return dataChanged; +} + } // namespace roudi } // namespace iox diff --git a/iceoryx_posh/source/runtime/ipc_runtime_interface.cpp b/iceoryx_posh/source/runtime/ipc_runtime_interface.cpp index e5a1237942..59711319ee 100644 --- a/iceoryx_posh/source/runtime/ipc_runtime_interface.cpp +++ b/iceoryx_posh/source/runtime/ipc_runtime_interface.cpp @@ -185,6 +185,7 @@ void IpcRuntimeInterface::waitForRoudi(deadline_timer& timer) noexcept { bool printWaitingWarning = true; bool printFoundMessage = false; + uint32_t numberOfRemainingFastPolls{10}; while (!timer.hasExpired() && !m_RoudiIpcInterface.isInitialized()) { m_RoudiIpcInterface.reopen(); @@ -201,7 +202,15 @@ void IpcRuntimeInterface::waitForRoudi(deadline_timer& timer) noexcept printWaitingWarning = false; printFoundMessage = true; } - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + if (numberOfRemainingFastPolls > 0) + { + --numberOfRemainingFastPolls; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + else + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } } if (printFoundMessage && m_RoudiIpcInterface.isInitialized()) diff --git a/iceoryx_posh/test/integrationtests/test_client_server.cpp b/iceoryx_posh/test/integrationtests/test_client_server.cpp index dd46b583e4..9540b09af9 100644 --- a/iceoryx_posh/test/integrationtests/test_client_server.cpp +++ b/iceoryx_posh/test/integrationtests/test_client_server.cpp @@ -21,6 +21,7 @@ #include "iceoryx_posh/popo/untyped_client.hpp" #include "iceoryx_posh/popo/untyped_server.hpp" #include "iceoryx_posh/runtime/posh_runtime.hpp" +#include "iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp" #include "iceoryx_posh/testing/roudi_gtest.hpp" #include "test.hpp" @@ -32,6 +33,7 @@ using namespace ::testing; using namespace iox::popo; using namespace iox::capro; using namespace iox::runtime; +using namespace iox::testing; class DummyRequest { @@ -60,6 +62,11 @@ class DummyResponse class ClientServer_test : public RouDi_GTest { public: + ClientServer_test() + : RouDi_GTest(MinimalRouDiConfigBuilder().create()) + { + } + void SetUp() override { PoshRuntime::initRuntime("together"); diff --git a/iceoryx_posh/test/integrationtests/test_interface_port_stack_blowup.cpp b/iceoryx_posh/test/integrationtests/test_interface_port_stack_blowup.cpp index 4fef00149a..0c2f822789 100644 --- a/iceoryx_posh/test/integrationtests/test_interface_port_stack_blowup.cpp +++ b/iceoryx_posh/test/integrationtests/test_interface_port_stack_blowup.cpp @@ -18,6 +18,7 @@ #include "iceoryx_posh/gateway/gateway_base.hpp" #include "iceoryx_posh/runtime/posh_runtime.hpp" +#include "iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp" #include "iceoryx_posh/testing/roudi_gtest.hpp" #include "test.hpp" @@ -27,10 +28,16 @@ namespace using namespace ::testing; using namespace iox::gw; +using namespace iox::testing; class InterfacePortRequestStackBlowup_test : public RouDi_GTest { public: + InterfacePortRequestStackBlowup_test() + : RouDi_GTest(MinimalRouDiConfigBuilder().create()) + { + } + void SetUp(){}; void TearDown(){}; }; 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 37d6c548f7..e91fe2f173 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 @@ -310,7 +310,7 @@ class PortUser_IntegrationTest : public Test }); /// Add some jitter to make thread breathe - std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 10)); + std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 4)); } // Signal the subscriber thread we're done diff --git a/iceoryx_posh/test/integrationtests/test_popo_pub_sub_listener.cpp b/iceoryx_posh/test/integrationtests/test_popo_pub_sub_listener.cpp index 9a0cd09c93..4658d1c4e7 100644 --- a/iceoryx_posh/test/integrationtests/test_popo_pub_sub_listener.cpp +++ b/iceoryx_posh/test/integrationtests/test_popo_pub_sub_listener.cpp @@ -18,6 +18,7 @@ #include "iceoryx_posh/popo/publisher.hpp" #include "iceoryx_posh/popo/subscriber.hpp" #include "iceoryx_posh/popo/untyped_subscriber.hpp" +#include "iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp" #include "iceoryx_posh/testing/roudi_gtest.hpp" #include "test.hpp" @@ -29,6 +30,7 @@ using namespace ::testing; using namespace iox::popo; using namespace iox::capro; using namespace iox::runtime; +using namespace iox::testing; void onSampleReceivedCallback(Subscriber* subscriber IOX_MAYBE_UNUSED) { @@ -41,6 +43,11 @@ void onSampleReceivedCallbackForUntypedSub(UntypedSubscriber* subscriber IOX_MAY class PubSubListener_IntegrationTest : public RouDi_GTest { public: + PubSubListener_IntegrationTest() + : RouDi_GTest(MinimalRouDiConfigBuilder().create()) + { + } + void SetUp() override { PoshRuntime::initRuntime("PubSubListener_IntegrationTest"); diff --git a/iceoryx_posh/test/integrationtests/test_posh_mepoo.cpp b/iceoryx_posh/test/integrationtests/test_posh_mepoo.cpp index 1998bbc2e4..813f2568f2 100644 --- a/iceoryx_posh/test/integrationtests/test_posh_mepoo.cpp +++ b/iceoryx_posh/test/integrationtests/test_posh_mepoo.cpp @@ -287,18 +287,6 @@ class Mepoo_IntegrationTest : public Test constexpr auto TOPIC_SIZE = sizeof(Topic); constexpr auto TOPIC_ALIGNMENT = alignof(Topic); - if (!(publisherPort->isOffered())) - { - publisherPort->offer(); - } - - if (subscriberPort->getSubscriptionState() != iox::SubscribeState::SUBSCRIBED) - { - subscriberPort->subscribe(); - } - - m_roudiEnv->InterOpWait(); - bool hasRunAsExpected = true; for (uint32_t idx = 0; idx < times; ++idx) { @@ -310,7 +298,6 @@ class Mepoo_IntegrationTest : public Test .and_then([&](auto sample) { new (sample->userPayload()) Topic; publisherPort->sendChunk(sample); - m_roudiEnv->InterOpWait(); }); if (allocationResult.has_value()) @@ -383,8 +370,6 @@ TEST_F(Mepoo_IntegrationTest, MempoolConfigCheck) memPoolTestContainer[mempolIndex1].m_minFreeChunks = memPoolTestContainer[mempolIndex1].m_minFreeChunks - REPETITION_1; - m_roudiEnv->InterOpWait(); - constexpr uint32_t SAMPLE_SIZE_2 = 450U; constexpr uint32_t REPETITION_2 = 3U; ASSERT_TRUE(sendReceiveSample(REPETITION_2)); @@ -393,8 +378,6 @@ TEST_F(Mepoo_IntegrationTest, MempoolConfigCheck) memPoolTestContainer[mempolIndex2].m_minFreeChunks = memPoolTestContainer[mempolIndex2].m_minFreeChunks - REPETITION_2; - m_roudiEnv->InterOpWait(); - // get mempoolconfig from introspection MemPoolInfoContainer memPoolInfoContainer; EXPECT_THAT(compareMemPoolInfo(memPoolInfoContainer, memPoolTestContainer, Log::Off), Eq(false)); diff --git a/iceoryx_posh/test/integrationtests/test_publisher_subscriber_communication.cpp b/iceoryx_posh/test/integrationtests/test_publisher_subscriber_communication.cpp index 3ebeca9ec7..3c4b4d1a79 100644 --- a/iceoryx_posh/test/integrationtests/test_publisher_subscriber_communication.cpp +++ b/iceoryx_posh/test/integrationtests/test_publisher_subscriber_communication.cpp @@ -22,6 +22,7 @@ #include "iceoryx_posh/popo/publisher.hpp" #include "iceoryx_posh/popo/subscriber.hpp" #include "iceoryx_posh/runtime/posh_runtime.hpp" +#include "iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp" #include "iceoryx_posh/testing/roudi_gtest.hpp" #include "iox/optional.hpp" #include "iox/stack.hpp" @@ -38,6 +39,7 @@ using namespace ::testing; using namespace iox; using namespace iox::popo; using namespace iox::cxx; +using namespace iox::testing; template struct ComplexDataType @@ -49,6 +51,11 @@ struct ComplexDataType class PublisherSubscriberCommunication_test : public RouDi_GTest { public: + PublisherSubscriberCommunication_test() + : RouDi_GTest(MinimalRouDiConfigBuilder().payloadChunkSize(512).create()) + { + } + void SetUp() { runtime::PoshRuntime::initRuntime("PublisherSubscriberCommunication_test"); @@ -150,7 +157,6 @@ TEST_F(PublisherSubscriberCommunication_test, AllSubscriberInterfacesCanBeSubscr { ::testing::Test::RecordProperty("TEST_ID", "aba18b27-bf64-49a7-8ad6-06a84b23a455"); auto publisher = createPublisher(); - this->InterOpWait(); std::vector>> subscribers; for (uint16_t interface = 0U; interface < static_cast(capro::Interfaces::INTERFACE_END); ++interface) @@ -159,7 +165,6 @@ TEST_F(PublisherSubscriberCommunication_test, AllSubscriberInterfacesCanBeSubscr SubscriberPortData::ChunkQueueData_t::MAX_CAPACITY, static_cast(interface))); } - this->InterOpWait(); constexpr int TRANSMISSION_DATA = 1337; ASSERT_FALSE(publisher->loan() @@ -280,7 +285,6 @@ TEST_F(PublisherSubscriberCommunication_test, SubscriberCanOnlyBeSubscribedWhenI auto publisher = createPublisher(ConsumerTooSlowPolicy::DISCARD_OLDEST_DATA, static_cast(publisherInterface)); - this->InterOpWait(); std::vector>> subscribers; for (uint16_t subscriberInterface = 0U; @@ -291,7 +295,6 @@ TEST_F(PublisherSubscriberCommunication_test, SubscriberCanOnlyBeSubscribedWhenI SubscriberPortData::ChunkQueueData_t::MAX_CAPACITY, static_cast(subscriberInterface))); } - this->InterOpWait(); constexpr int TRANSMISSION_DATA = 1337; ASSERT_FALSE(publisher->loan() @@ -323,9 +326,7 @@ TEST_F(PublisherSubscriberCommunication_test, SendingComplexDataType_forward_lis ::testing::Test::RecordProperty("TEST_ID", "97cbebbe-d430-4437-881d-90329e73dd42"); using Type_t = ComplexDataType, 5>>; auto publisher = createPublisher(); - this->InterOpWait(); auto subscriber = createSubscriber(); - this->InterOpWait(); ASSERT_FALSE(publisher->loan() .and_then([](auto& sample) { @@ -353,9 +354,7 @@ TEST_F(PublisherSubscriberCommunication_test, SendingComplexDataType_list) ::testing::Test::RecordProperty("TEST_ID", "4c5fa83a-935d-46ba-8adf-91e1de6acc89"); using Type_t = ComplexDataType>; auto publisher = createPublisher(); - this->InterOpWait(); auto subscriber = createSubscriber(); - this->InterOpWait(); ASSERT_FALSE(publisher->loan() .and_then([](auto& sample) { @@ -386,9 +385,7 @@ TEST_F(PublisherSubscriberCommunication_test, SendingComplexDataType_optional) ::testing::Test::RecordProperty("TEST_ID", "341ff552-a7a7-4dd9-be83-29d41bf142ec"); using Type_t = ComplexDataType, 5>>; auto publisher = createPublisher(); - this->InterOpWait(); auto subscriber = createSubscriber(); - this->InterOpWait(); ASSERT_FALSE(publisher->loan() .and_then([](auto& sample) { @@ -419,9 +416,7 @@ TEST_F(PublisherSubscriberCommunication_test, SendingComplexDataType_stack) ::testing::Test::RecordProperty("TEST_ID", "c378e0db-d863-4cad-9efa-4daec364b266"); using Type_t = ComplexDataType>; auto publisher = createPublisher(); - this->InterOpWait(); auto subscriber = createSubscriber(); - this->InterOpWait(); ASSERT_FALSE(publisher->loan() .and_then([](auto& sample) { @@ -454,9 +449,7 @@ TEST_F(PublisherSubscriberCommunication_test, SendingComplexDataType_string) ::testing::Test::RecordProperty("TEST_ID", "0603b4ca-f41a-4280-9984-cf1465ee05c7"); using Type_t = ComplexDataType>; auto publisher = createPublisher(); - this->InterOpWait(); auto subscriber = createSubscriber(); - this->InterOpWait(); ASSERT_FALSE(publisher->loan() .and_then([](auto& sample) { @@ -477,11 +470,9 @@ TEST_F(PublisherSubscriberCommunication_test, SendingComplexDataType_string) TEST_F(PublisherSubscriberCommunication_test, SendingComplexDataType_vector) { ::testing::Test::RecordProperty("TEST_ID", "fdfe4d05-c61a-4a99-b0b7-5e79da2700d5"); - using Type_t = ComplexDataType, 20>>; + using Type_t = ComplexDataType, 5>>; auto publisher = createPublisher(); - this->InterOpWait(); auto subscriber = createSubscriber(); - this->InterOpWait(); ASSERT_FALSE(publisher->loan() .and_then([](auto& sample) { @@ -507,11 +498,9 @@ TEST_F(PublisherSubscriberCommunication_test, SendingComplexDataType_vector) TEST_F(PublisherSubscriberCommunication_test, SendingComplexDataType_variant) { ::testing::Test::RecordProperty("TEST_ID", "0b5688ff-2367-4c76-93a2-6e447403c5ed"); - using Type_t = ComplexDataType, int>, 20>>; + using Type_t = ComplexDataType, int>, 5>>; auto publisher = createPublisher(); - this->InterOpWait(); auto subscriber = createSubscriber(); - this->InterOpWait(); ASSERT_FALSE(publisher->loan() .and_then([](auto& sample) { @@ -544,10 +533,8 @@ TEST_F(PublisherSubscriberCommunication_test, PublisherBlocksWhenBlockingActivat { ::testing::Test::RecordProperty("TEST_ID", "e97f1665-3488-4288-8fde-f485067bfeb4"); auto publisher = createPublisher>(ConsumerTooSlowPolicy::WAIT_FOR_CONSUMER); - this->InterOpWait(); auto subscriber = createSubscriber>(QueueFullPolicy::BLOCK_PRODUCER, 2U); - this->InterOpWait(); EXPECT_FALSE(publisher->publishCopyOf("start your day with a smile").has_error()); EXPECT_FALSE(publisher->publishCopyOf("and hypnotoad will smile back").has_error()); @@ -587,10 +574,8 @@ TEST_F(PublisherSubscriberCommunication_test, PublisherDoesNotBlockAndDiscardsSa { ::testing::Test::RecordProperty("TEST_ID", "1d92226d-fb3a-487c-bf52-6eb3c7946dc6"); auto publisher = createPublisher>(ConsumerTooSlowPolicy::DISCARD_OLDEST_DATA); - this->InterOpWait(); auto subscriber = createSubscriber>(QueueFullPolicy::DISCARD_OLDEST_DATA, 2U); - this->InterOpWait(); EXPECT_FALSE(publisher->publishCopyOf("first there was a blubb named mantua").has_error()); EXPECT_FALSE(publisher->publishCopyOf("second hypnotoad ate it").has_error()); @@ -621,13 +606,10 @@ TEST_F(PublisherSubscriberCommunication_test, NoSubscriptionWhenSubscriberWantsB { ::testing::Test::RecordProperty("TEST_ID", "c0144704-6dd7-4354-a41d-d4e512633484"); auto publisher = createPublisher>(ConsumerTooSlowPolicy::DISCARD_OLDEST_DATA); - this->InterOpWait(); auto subscriber = createSubscriber>(QueueFullPolicy::BLOCK_PRODUCER, 2U); - this->InterOpWait(); EXPECT_FALSE(publisher->publishCopyOf("never kiss the hypnotoad").has_error()); - this->InterOpWait(); auto sample = subscriber->take(); EXPECT_THAT(sample.has_error(), Eq(true)); @@ -637,13 +619,10 @@ TEST_F(PublisherSubscriberCommunication_test, SubscriptionWhenSubscriberDoesNotR { ::testing::Test::RecordProperty("TEST_ID", "228ea848-8926-4779-9e38-4d92eeb87feb"); auto publisher = createPublisher>(ConsumerTooSlowPolicy::WAIT_FOR_CONSUMER); - this->InterOpWait(); auto subscriber = createSubscriber>(QueueFullPolicy::DISCARD_OLDEST_DATA, 2U); - this->InterOpWait(); EXPECT_FALSE(publisher->publishCopyOf("never kiss the hypnotoad").has_error()); - this->InterOpWait(); auto sample = subscriber->take(); EXPECT_THAT(sample.has_error(), Eq(false)); @@ -655,11 +634,9 @@ TEST_F(PublisherSubscriberCommunication_test, MixedOptionsSetupWorksWithBlocking ::testing::Test::RecordProperty("TEST_ID", "c60ade45-1765-40ca-bc4b-7452c82ba127"); auto publisherBlocking = createPublisher>(ConsumerTooSlowPolicy::WAIT_FOR_CONSUMER); auto publisherNonBlocking = createPublisher>(ConsumerTooSlowPolicy::DISCARD_OLDEST_DATA); - this->InterOpWait(); auto subscriberBlocking = createSubscriber>(QueueFullPolicy::BLOCK_PRODUCER, 2U); auto subscriberNonBlocking = createSubscriber>(QueueFullPolicy::DISCARD_OLDEST_DATA, 2U); - this->InterOpWait(); EXPECT_FALSE(publisherBlocking->publishCopyOf("hypnotoads real name is Salsabarh Slimekirkdingle").has_error()); EXPECT_FALSE(publisherBlocking->publishCopyOf("hypnotoad wants a cookie").has_error()); @@ -714,9 +691,7 @@ TEST_F(PublisherSubscriberCommunication_test, PublisherUniqueIdMatchesReceivedSa ::testing::Test::RecordProperty("TEST_ID", "decbfcdd-778f-4e18-b6a8-395d400fdd80"); auto publisher = createPublisher(); - this->InterOpWait(); auto subscriber = createSubscriber(); - this->InterOpWait(); const auto uid = publisher->getUid(); diff --git a/iceoryx_posh/test/integrationtests/test_roudi_roudi_environment.cpp b/iceoryx_posh/test/integrationtests/test_roudi_roudi_environment.cpp index 61821cdb4e..1a93101897 100644 --- a/iceoryx_posh/test/integrationtests/test_roudi_roudi_environment.cpp +++ b/iceoryx_posh/test/integrationtests/test_roudi_roudi_environment.cpp @@ -15,6 +15,7 @@ // SPDX-License-Identifier: Apache-2.0 +#include "iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp" #include "iceoryx_posh/testing/roudi_environment/roudi_environment.hpp" #include "test.hpp" @@ -23,6 +24,7 @@ namespace { using namespace ::testing; using namespace iox::roudi; +using namespace iox::testing; class RouDiEnvironment_test : public Test { @@ -34,10 +36,10 @@ class RouDiEnvironment_test : public Test TEST_F(RouDiEnvironment_test, StartingRouDiTwiceLeadsToError) { ::testing::Test::RecordProperty("TEST_ID", "38075292-7897-4db5-b20e-f06ab324ad31"); - RouDiEnvironment m_sut{iox::RouDiConfig_t().setDefaults()}; + RouDiEnvironment m_sut{MinimalRouDiConfigBuilder().create()}; GTEST_FLAG(death_test_style) = "threadsafe"; - EXPECT_DEATH({ RouDiEnvironment m_sut2{iox::RouDiConfig_t().setDefaults()}; }, ".*"); + EXPECT_DEATH({ RouDiEnvironment m_sut2{MinimalRouDiConfigBuilder().create()}; }, ".*"); } } // namespace diff --git a/iceoryx_posh/test/integrationtests/test_service_discovery.cpp b/iceoryx_posh/test/integrationtests/test_service_discovery.cpp index c4595b67c7..d6a4d455e0 100644 --- a/iceoryx_posh/test/integrationtests/test_service_discovery.cpp +++ b/iceoryx_posh/test/integrationtests/test_service_discovery.cpp @@ -25,6 +25,7 @@ #include "iceoryx_posh/runtime/posh_runtime.hpp" #include "iceoryx_posh/runtime/service_discovery.hpp" #include "iceoryx_posh/testing/mocks/posh_runtime_mock.hpp" +#include "iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp" #include "iceoryx_posh/testing/roudi_gtest.hpp" #include "test.hpp" @@ -42,6 +43,7 @@ using namespace iox::runtime; using namespace iox::cxx; using namespace iox::popo; using namespace iox::capro; +using namespace iox::testing; using iox::capro::IdString_t; using iox::capro::ServiceDescription; using iox::popo::MessagingPattern; @@ -59,6 +61,11 @@ ServiceContainer serviceContainer; class ServiceDiscoveryBase_test : public RouDi_GTest { public: + ServiceDiscoveryBase_test() + : RouDi_GTest(MinimalRouDiConfigBuilder().create()) + { + } + void findService(const optional& service, const optional& instance, const optional& event, @@ -105,29 +112,6 @@ class ServiceDiscovery_test : public ServiceDiscoveryBase_test findService(s.getServiceIDString(), s.getInstanceIDString(), s.getEventIDString()); } - void waitUntilServiceChange() - { - m_waitset.wait(); - } - - void waitUntilEventuallyFound(const ServiceDescription& s) - { - do - { - waitUntilServiceChange(); - findService(s); - } while (serviceContainer.empty()); - } - - void waitUntilEventuallyNotFound(const ServiceDescription& s) - { - do - { - waitUntilServiceChange(); - findService(s); - } while (!serviceContainer.empty()); - } - // Used to avoid sleeps in tests if the system behaves correctly. // The watchdog ensures that the test will fail if it takes too long, due to // timeouts or potential deadlocks. @@ -213,18 +197,26 @@ TYPED_TEST(ServiceDiscovery_test, ReofferedServiceCanBeFound) const iox::capro::ServiceDescription SERVICE_DESCRIPTION("service", "instance", "event"); typename TestFixture::CommunicationKind::Producer producer(SERVICE_DESCRIPTION); + this->InterOpWait(); + this->findService(SERVICE_DESCRIPTION); ASSERT_THAT(serviceContainer.size(), Eq(1U)); EXPECT_THAT(*serviceContainer.begin(), Eq(SERVICE_DESCRIPTION)); producer.stopOffer(); - this->waitUntilEventuallyNotFound(SERVICE_DESCRIPTION); + + this->InterOpWait(); + + this->findService(SERVICE_DESCRIPTION); EXPECT_TRUE(serviceContainer.empty()); producer.offer(); - this->waitUntilEventuallyFound(SERVICE_DESCRIPTION); + + this->InterOpWait(); + + this->findService(SERVICE_DESCRIPTION); ASSERT_THAT(serviceContainer.size(), Eq(1U)); EXPECT_THAT(serviceContainer[0], Eq(SERVICE_DESCRIPTION)); @@ -237,13 +229,16 @@ TYPED_TEST(ServiceDiscovery_test, ServiceOfferedMultipleTimesCanBeFound) const iox::capro::ServiceDescription SERVICE_DESCRIPTION("service", "instance", "event"); typename TestFixture::CommunicationKind::Producer producer(SERVICE_DESCRIPTION); + this->InterOpWait(); + this->findService(SERVICE_DESCRIPTION); ASSERT_THAT(serviceContainer.size(), Eq(1U)); EXPECT_THAT(serviceContainer[0], Eq(SERVICE_DESCRIPTION)); producer.offer(); - this->waitUntilServiceChange(); + + this->InterOpWait(); this->findService(SERVICE_DESCRIPTION); @@ -810,6 +805,9 @@ TYPED_TEST(ServiceDiscoveryFindService_test, FindWhenNothingOffered) { ::testing::Test::RecordProperty("TEST_ID", "7f0bf2c0-5e96-4da6-b282-f84917bb5243"); + // ensure the discovery loop ran at least once + this->InterOpWait(); + // Checks whether the reference implementation result matches the sut result if we call // findService({"a"}, {"b"}, {"c"}). // In this case they both should find nothing (with any parameterization of findService) @@ -822,6 +820,8 @@ TYPED_TEST(ServiceDiscoveryFindService_test, FindWhenSingleServiceOffered) ::testing::Test::RecordProperty("TEST_ID", "aab09c10-8b1e-4f25-8f72-bd762b69f2cb"); this->add({"a", "b", "c"}); + this->InterOpWait(); + this->testFindService({"a"}, {"b"}, {"c"}); } @@ -831,6 +831,8 @@ TYPED_TEST(ServiceDiscoveryFindService_test, FindWhenSingleServiceIsOfferedMulti this->add({"a", "b", "c"}); this->add({"a", "b", "c"}); + this->InterOpWait(); + this->testFindService({"a"}, {"b"}, {"c"}); } @@ -842,6 +844,8 @@ TYPED_TEST(ServiceDiscoveryFindService_test, FindWhenMultipleServicesAreOffered) this->add({"aa", "a", "c"}); this->add({"a", "ab", "a"}); + this->InterOpWait(); + this->testFindService({"aa"}, {"a"}, {"c"}); } @@ -851,6 +855,8 @@ TYPED_TEST(ServiceDiscoveryFindService_test, FindWhenMultipleInstancesOfTheSameS this->add({"a", "b", "c"}); this->add({"a", "d", "c"}); + this->InterOpWait(); + this->testFindService({"a"}, {"d"}, {"c"}); } @@ -862,6 +868,8 @@ TYPED_TEST(ServiceDiscoveryFindService_test, RepeatedSearchYieldsSameResult) this->add({"aa", "a", "c"}); this->add({"a", "ab", "a"}); + this->InterOpWait(); + this->testFindService({"a"}, {"b"}, {"aa"}); auto previousResult = serviceContainer; @@ -874,6 +882,8 @@ TYPED_TEST(ServiceDiscoveryFindService_test, FindNonExistingService) ::testing::Test::RecordProperty("TEST_ID", "6f953d0d-bae3-45a1-82e7-c78a32b6d365"); this->add({"a", "b", "c"}); + this->InterOpWait(); + // those are all representatives of equivalence classes of mismatches // that hould not be found this->testFindService({"x"}, {"b"}, {"c"}); @@ -897,6 +907,8 @@ TYPED_TEST(ServiceDiscoveryFindService_test, FindNonExistingServiceAmongMultiple this->add({"x", "b", "x"}); this->add({"x", "x", "x"}); + this->InterOpWait(); + this->testFindService({"a"}, {"b"}, {"c"}); } @@ -945,6 +957,8 @@ TYPED_TEST(ServiceDiscoveryFindService_test, FindInMaximumServices) EXPECT_EQ(created, MAX); + this->InterOpWait(); + // search for specific services we inserted at various times (includes wildcard searches etc.): // find first offered service, last offered service and some service offered inbetween this->testFindService(s1); @@ -962,6 +976,8 @@ TYPED_TEST(ServiceDiscoveryFindService_test, SameServerAndPublisherCanBeFound) this->add({"Ferdinand", "Schnüffel", "Spitz"}); this->addOther({"Ferdinand", "Schnüffel", "Spitz"}); + this->InterOpWait(); + this->testFindService({"Ferdinand"}, {"Schnüffel"}, {"Spitz"}); } @@ -971,6 +987,8 @@ TYPED_TEST(ServiceDiscoveryFindService_test, OtherServiceKindWithMatchingNameIsN this->add({"Schnüffel", "Ferdinand", "Spitz"}); this->addOther({"Ferdinand", "Schnüffel", "Spitz"}); + this->InterOpWait(); + this->testFindService({"Ferdinand"}, {"Schnüffel"}, {"Spitz"}); } @@ -1049,6 +1067,8 @@ TYPED_TEST(ServiceDiscoveryFindService_test, FindInMaximumMixedServices) EXPECT_EQ(created, OTHER_MAX); + this->InterOpWait(); + // now we have the maximum of services of both kinds with semi-random services // search for specific services we inserted at various times (includes wildcard searches etc.): diff --git a/iceoryx_posh/test/moduletests/test_popo_trigger_handle.cpp b/iceoryx_posh/test/moduletests/test_popo_trigger_handle.cpp index a242351444..1f06533b7f 100644 --- a/iceoryx_posh/test/moduletests/test_popo_trigger_handle.cpp +++ b/iceoryx_posh/test/moduletests/test_popo_trigger_handle.cpp @@ -18,6 +18,7 @@ #include "iceoryx_posh/internal/popo/building_blocks/condition_listener.hpp" #include "iceoryx_posh/internal/popo/building_blocks/condition_variable_data.hpp" #include "iceoryx_posh/popo/trigger_handle.hpp" +#include "iox/deadline_timer.hpp" #include "test.hpp" #include @@ -122,18 +123,25 @@ TEST_F(TriggerHandle_test, triggerNotifiesConditionVariable) ::testing::Test::RecordProperty("TEST_ID", "11e752c8-d473-4bfd-b973-869c3b2d9fbc"); std::atomic_int stage{0}; + iox::deadline_timer timeout{100_ms}; + std::thread t([&] { - ConditionListener(m_condVar).wait(); stage.store(1); + ConditionListener(m_condVar).wait(); }); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - EXPECT_EQ(stage.load(), 0); + while (!timeout.hasExpired() && stage.load() < 1) + { + std::this_thread::yield(); + } + ASSERT_FALSE(timeout.hasExpired()); + EXPECT_THAT(stage.load(), Eq(1)); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + EXPECT_THAT(stage.load(), Eq(1)); m_sut.trigger(); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - EXPECT_EQ(stage.load(), 1); - t.join(); + + EXPECT_FALSE(timeout.hasExpired()); } TEST_F(TriggerHandle_test, wasTriggeredReturnsFalseAfterCreation) diff --git a/iceoryx_posh/test/moduletests/test_posh_runtime.cpp b/iceoryx_posh/test/moduletests/test_posh_runtime.cpp index fee7dc6d3b..8cf0bcaacc 100644 --- a/iceoryx_posh/test/moduletests/test_posh_runtime.cpp +++ b/iceoryx_posh/test/moduletests/test_posh_runtime.cpp @@ -27,6 +27,7 @@ #include "iceoryx_posh/popo/untyped_server.hpp" #include "iceoryx_posh/runtime/posh_runtime.hpp" #include "iceoryx_posh/testing/mocks/posh_runtime_mock.hpp" +#include "iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp" #include "iceoryx_posh/testing/roudi_environment/roudi_environment.hpp" #include "test.hpp" @@ -40,6 +41,7 @@ using namespace iox::capro; using namespace iox::cxx; using namespace iox; using namespace iox::popo; +using namespace iox::testing; using iox::roudi::RouDiEnvironment; class PoshRuntime_test : public Test @@ -61,11 +63,6 @@ class PoshRuntime_test : public Test { } - void InterOpWait() - { - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - } - void checkClientInitialization(const ClientPortData* const portData, const ServiceDescription& sd, const ClientOptions& options, @@ -109,7 +106,7 @@ class PoshRuntime_test : public Test } const iox::RuntimeName_t m_runtimeName{"publisher"}; - RouDiEnvironment m_roudiEnv{iox::RouDiConfig_t().setDefaults()}; + RouDiEnvironment m_roudiEnv{MinimalRouDiConfigBuilder().create()}; PoshRuntime* m_runtime{&iox::runtime::PoshRuntime::initRuntime(m_runtimeName)}; IpcMessage m_sendBuffer; IpcMessage m_receiveBuffer; diff --git a/iceoryx_posh/test/moduletests/test_posh_runtime_node.cpp b/iceoryx_posh/test/moduletests/test_posh_runtime_node.cpp index b533455838..bf3d7db4ac 100644 --- a/iceoryx_posh/test/moduletests/test_posh_runtime_node.cpp +++ b/iceoryx_posh/test/moduletests/test_posh_runtime_node.cpp @@ -17,6 +17,7 @@ #include "iceoryx_posh/runtime/node.hpp" #include "iceoryx_posh/runtime/posh_runtime.hpp" +#include "iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp" #include "iceoryx_posh/testing/roudi_environment/roudi_environment.hpp" #include "test.hpp" @@ -26,6 +27,7 @@ namespace using namespace ::testing; using namespace iox::runtime; using namespace iox::roudi; +using namespace iox::testing; using namespace iox; /// @brief Test goal: This test suit verifies class node @@ -46,7 +48,7 @@ class PoshRuntimeNode_test : public Test virtual void TearDown(){}; const RuntimeName_t m_runtimeName{"App"}; - RouDiEnvironment m_roudiEnv{iox::RouDiConfig_t().setDefaults()}; + RouDiEnvironment m_roudiEnv{MinimalRouDiConfigBuilder().create()}; PoshRuntime* m_runtime{&iox::runtime::PoshRuntime::initRuntime(m_runtimeName)}; }; diff --git a/iceoryx_posh/test/moduletests/test_posh_runtime_single_process.cpp b/iceoryx_posh/test/moduletests/test_posh_runtime_single_process.cpp index 37eb4bf2b7..d33d5a0fee 100644 --- a/iceoryx_posh/test/moduletests/test_posh_runtime_single_process.cpp +++ b/iceoryx_posh/test/moduletests/test_posh_runtime_single_process.cpp @@ -16,6 +16,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "iceoryx_posh/runtime/posh_runtime_single_process.hpp" +#include "iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp" #include "iceoryx_posh/testing/roudi_environment/roudi_environment.hpp" #include "test.hpp" @@ -25,6 +26,7 @@ namespace using namespace ::testing; using namespace iox::runtime; using namespace iox::roudi; +using namespace iox::testing; using namespace iox; /// @brief Test goal: This test suit verifies class posh_runtime_single_process @@ -48,8 +50,8 @@ class PoshRuntimeSingleProcess_test : public Test TEST_F(PoshRuntimeSingleProcess_test, ConstructorPoshRuntimeSingleProcessIsSuccess) { ::testing::Test::RecordProperty("TEST_ID", "9faf7053-86af-4d26-b3a7-fb3c6319ab86"); - iox::RouDiConfig_t defaultRouDiConfig = iox::RouDiConfig_t().setDefaults(); - std::unique_ptr roudiComponents{new IceOryxRouDiComponents(defaultRouDiConfig)}; + std::unique_ptr roudiComponents{ + new IceOryxRouDiComponents(MinimalRouDiConfigBuilder().create())}; std::unique_ptr roudi{new RouDi(roudiComponents->rouDiMemoryManager, roudiComponents->portManager, @@ -64,7 +66,7 @@ TEST_F(PoshRuntimeSingleProcess_test, ConstructorPoshRuntimeSingleProcessIsSucce TEST_F(PoshRuntimeSingleProcess_test, ConstructorPoshRuntimeSingleProcessMultipleProcessIsFound) { ::testing::Test::RecordProperty("TEST_ID", "1cc7ad5d-5878-454a-94ba-5cf412c22682"); - RouDiEnvironment roudiEnv{iox::RouDiConfig_t().setDefaults()}; + RouDiEnvironment roudiEnv{MinimalRouDiConfigBuilder().create()}; const RuntimeName_t runtimeName{"App"}; diff --git a/iceoryx_posh/test/moduletests/test_roudi_cmd_line_parser.cpp b/iceoryx_posh/test/moduletests/test_roudi_cmd_line_parser.cpp index f97a14e4a5..23bb7c974f 100644 --- a/iceoryx_posh/test/moduletests/test_roudi_cmd_line_parser.cpp +++ b/iceoryx_posh/test/moduletests/test_roudi_cmd_line_parser.cpp @@ -36,6 +36,7 @@ bool operator==(const CmdLineArgs_t& lhs, const CmdLineArgs_t& rhs) { return (lhs.monitoringMode == rhs.monitoringMode) && (lhs.logLevel == rhs.logLevel) && (lhs.compatibilityCheckLevel == rhs.compatibilityCheckLevel) + && (lhs.processTerminationDelay == rhs.processTerminationDelay) && (lhs.processKillDelay == rhs.processKillDelay) && (lhs.uniqueRouDiId == rhs.uniqueRouDiId) && (lhs.run == rhs.run) && (lhs.configFilePath == rhs.configFilePath); } @@ -346,6 +347,65 @@ TEST_F(CmdLineParser_test, KillDelayOptionOutOfBoundsLeadsToProgrammNotRunning) EXPECT_FALSE(result.value().run); } +TEST_F(CmdLineParser_test, TerminationDelayLongOptionLeadsToCorrectDelay) +{ + ::testing::Test::RecordProperty("TEST_ID", "9125f775-93b6-4560-a535-f8ecf77671b5"); + constexpr uint8_t NUMBER_OF_ARGS{3U}; + char* args[NUMBER_OF_ARGS]; + char appName[] = "./foo"; + char option[] = "--termination-delay"; + char value[] = "73"; + args[0] = &appName[0]; + args[1] = &option[0]; + args[2] = &value[0]; + + CmdLineParser sut; + auto result = sut.parse(NUMBER_OF_ARGS, args); + + ASSERT_FALSE(result.has_error()); + EXPECT_EQ(result.value().processTerminationDelay, 73_s); + EXPECT_TRUE(result.value().run); +} + +TEST_F(CmdLineParser_test, TerminationDelayShortOptionLeadsToCorrectDelay) +{ + ::testing::Test::RecordProperty("TEST_ID", "05b24a16-334b-4b42-8846-766b0c99943b"); + constexpr uint8_t NUMBER_OF_ARGS{3U}; + char* args[NUMBER_OF_ARGS]; + char appName[] = "./foo"; + char option[] = "-t"; + char value[] = "42"; + args[0] = &appName[0]; + args[1] = &option[0]; + args[2] = &value[0]; + + CmdLineParser sut; + auto result = sut.parse(NUMBER_OF_ARGS, args); + + ASSERT_FALSE(result.has_error()); + EXPECT_EQ(result.value().processTerminationDelay, 42_s); + EXPECT_TRUE(result.value().run); +} + +TEST_F(CmdLineParser_test, TerminationDelayOptionOutOfBoundsLeadsToProgrammNotRunning) +{ + ::testing::Test::RecordProperty("TEST_ID", "7eaffeb5-a0e5-4cdd-a898-d9d64c999d16"); + constexpr uint8_t NUMBER_OF_ARGS{3U}; + char* args[NUMBER_OF_ARGS]; + char appName[] = "./foo"; + char option[] = "--termination-delay"; + char value[] = "4294967296"; // MAX_PROCESS_TERMINATION_DELAY + 1 + args[0] = &appName[0]; + args[1] = &option[0]; + args[2] = &value[0]; + + CmdLineParser sut; + auto result = sut.parse(NUMBER_OF_ARGS, args); + + ASSERT_FALSE(result.has_error()); + EXPECT_FALSE(result.value().run); +} + TEST_F(CmdLineParser_test, CompatibilityLevelOptionsLeadToCorrectCompatibilityLevel) { ::testing::Test::RecordProperty("TEST_ID", "62b7d5c9-0638-4314-b4f7-c622ef101045"); @@ -458,18 +518,22 @@ TEST_F(CmdLineParser_test, OutOfBoundsUniqueIdOptionLeadsToProgrammNotRunning) TEST_F(CmdLineParser_test, CmdLineParsingModeEqualToOneHandlesOnlyTheFirstOption) { ::testing::Test::RecordProperty("TEST_ID", "1e674db9-d71a-4b82-83cc-eea2e04f4601"); - constexpr uint8_t NUMBER_OF_ARGS{5U}; + constexpr uint8_t NUMBER_OF_ARGS{7U}; char* args[NUMBER_OF_ARGS]; char appName[] = "./foo"; char uniqueIdOption[] = "-u"; char idValue[] = "4242"; char killOption[] = "-k"; char killValue[] = "42"; + char terminationOption[] = "-t"; + char terminationValue[] = "2"; args[0] = &appName[0]; args[1] = &uniqueIdOption[0]; args[2] = &idValue[0]; args[3] = &killOption[0]; args[4] = &killValue[0]; + args[5] = &terminationOption[0]; + args[6] = &terminationValue[0]; CmdLineParser sut; auto result = sut.parse(NUMBER_OF_ARGS, args, CmdLineParser::CmdLineArgumentParsingMode::ONE); @@ -477,6 +541,7 @@ TEST_F(CmdLineParser_test, CmdLineParsingModeEqualToOneHandlesOnlyTheFirstOption ASSERT_FALSE(result.has_error()); ASSERT_TRUE(result.value().uniqueRouDiId.has_value()); EXPECT_EQ(result.value().uniqueRouDiId.value(), 4242); + EXPECT_EQ(result.value().processTerminationDelay, iox::roudi::PROCESS_DEFAULT_TERMINATION_DELAY); EXPECT_EQ(result.value().processKillDelay, iox::roudi::PROCESS_DEFAULT_KILL_DELAY); // default value for kill delay EXPECT_TRUE(result.value().run); @@ -487,8 +552,9 @@ TEST_F(CmdLineParser_test, CmdLineParsingModeEqualToOneHandlesOnlyTheFirstOption ASSERT_FALSE(res.has_error()); ASSERT_TRUE(res.value().uniqueRouDiId.has_value()); EXPECT_EQ(res.value().uniqueRouDiId.value(), 4242); - EXPECT_EQ(res.value().processKillDelay, Duration::fromSeconds(42)); - EXPECT_TRUE(result.value().run); + EXPECT_EQ(res.value().processTerminationDelay, 2_s); + EXPECT_EQ(res.value().processKillDelay, 42_s); + EXPECT_TRUE(res.value().run); } } // namespace diff --git a/iceoryx_posh/test/moduletests/test_roudi_iceoyx_roudi_memory_manager.cpp b/iceoryx_posh/test/moduletests/test_roudi_iceoyx_roudi_memory_manager.cpp index cfc4367321..2eb3bd003d 100644 --- a/iceoryx_posh/test/moduletests/test_roudi_iceoyx_roudi_memory_manager.cpp +++ b/iceoryx_posh/test/moduletests/test_roudi_iceoyx_roudi_memory_manager.cpp @@ -18,11 +18,13 @@ #include "test.hpp" #include "iceoryx_posh/roudi/memory/iceoryx_roudi_memory_manager.hpp" +#include "iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp" namespace { using namespace ::testing; +using namespace iox::testing; using iox::roudi::IceOryxRouDiMemoryManager; /// @brief This test file verifies that the BaseClass IceoryxRouDiMemoryManager is tested @@ -33,8 +35,8 @@ class IceoryxRoudiMemoryManager_test : public Test void SetUp() override { - auto config = iox::RouDiConfig_t().setDefaults(); - m_roudiMemoryManagerTest = std::unique_ptr(new IceOryxRouDiMemoryManager(config)); + m_roudiMemoryManagerTest = std::unique_ptr( + new IceOryxRouDiMemoryManager(MinimalRouDiConfigBuilder().create())); } void TearDown() override @@ -45,7 +47,7 @@ class IceoryxRoudiMemoryManager_test : public Test TEST_F(IceoryxRoudiMemoryManager_test, ConstructorSuccess) { ::testing::Test::RecordProperty("TEST_ID", "f435abe6-07da-44e8-9f1a-074fbcb66209"); - EXPECT_THAT(m_roudiMemoryManagerTest, Not(Eq(nullptr))); + EXPECT_THAT(m_roudiMemoryManagerTest, Ne(nullptr)); } TEST_F(IceoryxRoudiMemoryManager_test, IntrospectionMemoryManagerNulloptWhenNotPresent) @@ -55,6 +57,13 @@ TEST_F(IceoryxRoudiMemoryManager_test, IntrospectionMemoryManagerNulloptWhenNotP EXPECT_THAT(result, Eq(iox::nullopt_t())); } +TEST_F(IceoryxRoudiMemoryManager_test, DiscoveryMemoryManagerNulloptWhenNotPresent) +{ + ::testing::Test::RecordProperty("TEST_ID", "5c432412-8095-4683-994b-55bbb82bb255"); + auto result = m_roudiMemoryManagerTest->discoveryMemoryManager(); + EXPECT_THAT(result, Eq(iox::nullopt_t())); +} + TEST_F(IceoryxRoudiMemoryManager_test, segmentManagerNulloptWhenNotPresent) { ::testing::Test::RecordProperty("TEST_ID", "951b828b-a99c-4eb6-8ea0-34cb34cf7d28"); @@ -80,7 +89,7 @@ TEST_F(IceoryxRoudiMemoryManager_test, MgmtMemoryProviderReturnNonNullPtr) { ::testing::Test::RecordProperty("TEST_ID", "e89c1ba8-34ca-410f-bb23-45fb41e24e77"); auto testResult = m_roudiMemoryManagerTest->mgmtMemoryProvider(); - EXPECT_THAT(testResult, Not(Eq(nullptr))); + EXPECT_THAT(testResult, Ne(nullptr)); } TEST_F(IceoryxRoudiMemoryManager_test, AcquiringIntrospectionMemoryManagerAfterCreateAndAnnounceMemoryIsSuccessful) @@ -91,7 +100,18 @@ TEST_F(IceoryxRoudiMemoryManager_test, AcquiringIntrospectionMemoryManagerAfterC EXPECT_THAT(tr.has_error(), Eq(false)); auto result = m_roudiMemoryManagerTest->introspectionMemoryManager(); - EXPECT_THAT(result, Not(Eq(iox::nullopt_t()))); + EXPECT_TRUE(result.has_value()); +} + +TEST_F(IceoryxRoudiMemoryManager_test, AcquiringDiscoveryMemoryManagerAfterCreateAndAnnounceMemoryIsSuccessful) +{ + ::testing::Test::RecordProperty("TEST_ID", "ce42929a-0721-41b9-bf08-b48e53fe901e"); + auto tr = m_roudiMemoryManagerTest->createAndAnnounceMemory(); + + EXPECT_THAT(tr.has_error(), Eq(false)); + + auto result = m_roudiMemoryManagerTest->discoveryMemoryManager(); + EXPECT_TRUE(result.has_value()); } TEST_F(IceoryxRoudiMemoryManager_test, AcquiringSegmentManagerAfterCreateAndAnnounceMemoryIsSuccessful) @@ -102,7 +122,7 @@ TEST_F(IceoryxRoudiMemoryManager_test, AcquiringSegmentManagerAfterCreateAndAnno EXPECT_THAT(tr.has_error(), Eq(false)); auto resultTest = m_roudiMemoryManagerTest->segmentManager(); - EXPECT_THAT(resultTest, Not(Eq(iox::nullopt_t()))); + EXPECT_TRUE(resultTest.has_value()); } TEST_F(IceoryxRoudiMemoryManager_test, AcquiringPortPoolAfterCreateAndAnnounceMemoryIsSuccessful) @@ -113,7 +133,7 @@ TEST_F(IceoryxRoudiMemoryManager_test, AcquiringPortPoolAfterCreateAndAnnounceMe EXPECT_THAT(tr.has_error(), Eq(false)); auto testResult = m_roudiMemoryManagerTest->portPool(); - EXPECT_THAT(testResult, Not(Eq(iox::nullopt_t()))); + EXPECT_TRUE(testResult.has_value()); } TEST_F(IceoryxRoudiMemoryManager_test, DestroyMemoryReturnNoError) @@ -140,6 +160,19 @@ TEST_F(IceoryxRoudiMemoryManager_test, DestroyMemoryIntrospectionMemoryManagerRe EXPECT_THAT(res, Eq(iox::nullopt_t())); } +TEST_F(IceoryxRoudiMemoryManager_test, DestroyMemoryDiscoveryMemoryManagerReturnNullOpt) +{ + ::testing::Test::RecordProperty("TEST_ID", "44c826e3-7ba1-4398-8c14-ad8b3e6297ca"); + auto testResult = m_roudiMemoryManagerTest->createAndAnnounceMemory(); + ASSERT_FALSE(testResult.has_error()); + + auto result = m_roudiMemoryManagerTest->destroyMemory(); + ASSERT_FALSE(result.has_error()); + + auto res = m_roudiMemoryManagerTest->discoveryMemoryManager(); + EXPECT_THAT(res, Eq(iox::nullopt_t())); +} + TEST_F(IceoryxRoudiMemoryManager_test, DestroyMemorySegmentManagerReturnNullOpt) { ::testing::Test::RecordProperty("TEST_ID", "105509a6-21fd-4503-b431-1fdf069ac767"); diff --git a/iceoryx_posh/test/moduletests/test_roudi_portmanager_fixture.hpp b/iceoryx_posh/test/moduletests/test_roudi_portmanager_fixture.hpp index fe8082ea22..46c2f29cf5 100644 --- a/iceoryx_posh/test/moduletests/test_roudi_portmanager_fixture.hpp +++ b/iceoryx_posh/test/moduletests/test_roudi_portmanager_fixture.hpp @@ -29,6 +29,7 @@ #include "iceoryx_posh/popo/client_options.hpp" #include "iceoryx_posh/popo/server_options.hpp" #include "iceoryx_posh/roudi/memory/iceoryx_roudi_memory_manager.hpp" +#include "iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp" #include "test.hpp" @@ -43,6 +44,7 @@ using namespace iox::capro; using namespace iox::cxx; using namespace iox::popo; using namespace iox::roudi; +using namespace iox::testing; using iox::runtime::PortConfigInfo; @@ -84,8 +86,7 @@ class PortManager_test : public Test m_eventIdCounter = 0; // starting at {1,1,1} - auto config = iox::RouDiConfig_t().setDefaults(); - m_roudiMemoryManager = new IceOryxRouDiMemoryManager(config); + m_roudiMemoryManager = new IceOryxRouDiMemoryManager(MinimalRouDiConfigBuilder().create()); EXPECT_FALSE(m_roudiMemoryManager->createAndAnnounceMemory().has_error()); m_portManager = new PortManagerTester(m_roudiMemoryManager); diff --git a/iceoryx_posh/test/moduletests/test_roudi_process_manager.cpp b/iceoryx_posh/test/moduletests/test_roudi_process_manager.cpp index 86d6a20231..19af8e3572 100644 --- a/iceoryx_posh/test/moduletests/test_roudi_process_manager.cpp +++ b/iceoryx_posh/test/moduletests/test_roudi_process_manager.cpp @@ -23,6 +23,7 @@ #include "iceoryx_posh/mepoo/mepoo_config.hpp" #include "iceoryx_posh/roudi/memory/iceoryx_roudi_memory_manager.hpp" #include "iceoryx_posh/roudi/memory/roudi_memory_interface.hpp" +#include "iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp" #include "iceoryx_posh/version/compatibility_check_level.hpp" #include "iox/string.hpp" #include "test.hpp" @@ -34,6 +35,7 @@ using namespace iox::roudi; using namespace iox::popo; using namespace iox::runtime; using namespace iox::posix; +using namespace iox::testing; using namespace iox::version; class ProcessManager_test : public Test @@ -41,8 +43,7 @@ class ProcessManager_test : public Test public: void SetUp() override { - auto config = iox::RouDiConfig_t().setDefaults(); - m_roudiMemoryManager = std::make_unique(config); + m_roudiMemoryManager = std::make_unique(MinimalRouDiConfigBuilder().create()); EXPECT_FALSE(m_roudiMemoryManager->createAndAnnounceMemory().has_error()); m_portManager = std::make_unique(m_roudiMemoryManager.get()); CompatibilityCheckLevel m_compLevel{CompatibilityCheckLevel::OFF}; @@ -69,12 +70,20 @@ class ProcessManager_test : public Test }; +TEST_F(ProcessManager_test, RegisteredProcessCountIsInitiallyZero) +{ + ::testing::Test::RecordProperty("TEST_ID", "54ec8fa2-e2dd-43a0-9e39-a9da967941de"); + + EXPECT_THAT(m_sut->registeredProcessCount(), Eq(0)); +} + TEST_F(ProcessManager_test, RegisterProcessWithMonitorningWorks) { ::testing::Test::RecordProperty("TEST_ID", "57311fb6-f993-4011-bbe9-e42df5e54d5e"); auto result = m_sut->registerProcess(m_processname, m_pid, m_user, m_isMonitored, 1U, 1U, m_versionInfo); EXPECT_TRUE(result); + EXPECT_THAT(m_sut->registeredProcessCount(), Eq(1)); } TEST_F(ProcessManager_test, RegisterProcessWithoutMonitoringWorks) @@ -94,6 +103,7 @@ TEST_F(ProcessManager_test, RegisterSameProcessTwiceWithMonitoringWorks) EXPECT_TRUE(result1); EXPECT_TRUE(result2); + EXPECT_THAT(m_sut->registeredProcessCount(), Eq(1)); } TEST_F(ProcessManager_test, RegisterSameProcessTwiceWithoutMonitoringWorks) @@ -105,6 +115,7 @@ TEST_F(ProcessManager_test, RegisterSameProcessTwiceWithoutMonitoringWorks) EXPECT_TRUE(result1); EXPECT_TRUE(result2); + EXPECT_THAT(m_sut->registeredProcessCount(), Eq(1)); } TEST_F(ProcessManager_test, UnregisterNonExistentProcessLeadsToError) @@ -122,6 +133,7 @@ TEST_F(ProcessManager_test, RegisterAndUnregisterWorks) auto unregisterResult = m_sut->unregisterProcess(m_processname); EXPECT_TRUE(unregisterResult); + EXPECT_THAT(m_sut->registeredProcessCount(), Eq(0)); } TEST_F(ProcessManager_test, HandleProcessShutdownPreparationRequestWorks) diff --git a/iceoryx_posh/test/moduletests/test_roudi_service_registry.cpp b/iceoryx_posh/test/moduletests/test_roudi_service_registry.cpp index d9016dc503..506b5abceb 100644 --- a/iceoryx_posh/test/moduletests/test_roudi_service_registry.cpp +++ b/iceoryx_posh/test/moduletests/test_roudi_service_registry.cpp @@ -704,4 +704,46 @@ TYPED_TEST(ServiceRegistry_test, FindWithMixOfPublishersAndServersWorks) EXPECT_EQ(filtered[1].serviceDescription, service3); } +TYPED_TEST(ServiceRegistry_test, HasDataChangedSinceLastCallReturnsTrueOnInitialCall) +{ + ::testing::Test::RecordProperty("TEST_ID", "51398abb-53b2-4dce-9267-73f02f9d7574"); + + EXPECT_TRUE(this->sut.registry.hasDataChangedSinceLastCall()); +} + +TYPED_TEST(ServiceRegistry_test, HasDataChangedSinceLastCallReturnsFalseOnSubsequentCall) +{ + ::testing::Test::RecordProperty("TEST_ID", "a8a8d286-01ba-4084-94c4-fd0866e0e5d0"); + + EXPECT_TRUE(this->sut.registry.hasDataChangedSinceLastCall()); + EXPECT_FALSE(this->sut.registry.hasDataChangedSinceLastCall()); +} + +TYPED_TEST(ServiceRegistry_test, HasDataChangedSinceLastCallReturnsTrueAfterAddingService) +{ + ::testing::Test::RecordProperty("TEST_ID", "17d5b84a-abe0-46e0-aa06-2d049c716b22"); + + iox::capro::ServiceDescription service("a", "a", "a"); + + this->sut.registry.hasDataChangedSinceLastCall(); + + ASSERT_FALSE(this->sut.add(service).has_error()); + + EXPECT_TRUE(this->sut.registry.hasDataChangedSinceLastCall()); +} + +TYPED_TEST(ServiceRegistry_test, HasDataChangedSinceLastCallReturnsTrueAfterRemovingService) +{ + ::testing::Test::RecordProperty("TEST_ID", "a4f0c9e2-2549-4fa0-88d4-75a2ef8714b8"); + + iox::capro::ServiceDescription service("a", "a", "a"); + + ASSERT_FALSE(this->sut.add(service).has_error()); + this->sut.registry.hasDataChangedSinceLastCall(); + + this->sut.remove(service); + + EXPECT_TRUE(this->sut.registry.hasDataChangedSinceLastCall()); +} + } // namespace diff --git a/iceoryx_posh/testing/CMakeLists.txt b/iceoryx_posh/testing/CMakeLists.txt index 4c9d469994..1e55c62304 100644 --- a/iceoryx_posh/testing/CMakeLists.txt +++ b/iceoryx_posh/testing/CMakeLists.txt @@ -36,6 +36,7 @@ if(ROUDI_ENVIRONMENT OR BUILD_TEST) iceoryx_posh::iceoryx_posh_roudi iceoryx_hoofs_testing::iceoryx_hoofs_testing FILES + roudi_environment/minimal_roudi_config.cpp roudi_environment/runtime_test_interface.cpp roudi_environment/roudi_environment.cpp ) diff --git a/iceoryx_posh/testing/include/iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp b/iceoryx_posh/testing/include/iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp new file mode 100644 index 0000000000..d0714e8bbc --- /dev/null +++ b/iceoryx_posh/testing/include/iceoryx_posh/testing/roudi_environment/minimal_roudi_config.hpp @@ -0,0 +1,50 @@ +// Copyright (c) 2023 by Mathias Kraus . 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_ROUDI_ENVIRONMENT_MINIMAL_ROUDI_CONFIG_HPP +#define IOX_POSH_ROUDI_ENVIRONMENT_MINIMAL_ROUDI_CONFIG_HPP + +#include "iceoryx_posh/iceoryx_posh_config.hpp" +#include "iox/builder.hpp" + +namespace iox +{ +namespace testing +{ +/// @brief Builder for a minimal RouDiConfig_t with only one MemPool. This significantly speeds up tests which create a +/// shared memory. +class MinimalRouDiConfigBuilder +{ + /// @brief Set the payload chunk size. Default = 128 + IOX_BUILDER_PARAMETER(uint32_t, payloadChunkSize, 128) + + /// @brief Set the payload chunk count. Default = 10 + IOX_BUILDER_PARAMETER(uint32_t, payloadChunkCount, 10) + + /// @brief Set the introspection chunk count. Default = 2 + IOX_BUILDER_PARAMETER(uint32_t, introspectionChunkCount, 2) + + /// @brief Set the discovery chunk count. Default = 2 + IOX_BUILDER_PARAMETER(uint32_t, discoveryChunkCount, 2) + + public: + /// @brief creates the previously configured RouDiConfig_t + RouDiConfig_t create() const noexcept; +}; +} // namespace testing +} // namespace iox + +#endif // IOX_POSH_ROUDI_ENVIRONMENT_MINIMAL_ROUDI_CONFIG_HPP diff --git a/iceoryx_posh/testing/include/iceoryx_posh/testing/roudi_environment/roudi_environment.hpp b/iceoryx_posh/testing/include/iceoryx_posh/testing/roudi_environment/roudi_environment.hpp index 739cdfe242..39f5390122 100644 --- a/iceoryx_posh/testing/include/iceoryx_posh/testing/roudi_environment/roudi_environment.hpp +++ b/iceoryx_posh/testing/include/iceoryx_posh/testing/roudi_environment/roudi_environment.hpp @@ -23,6 +23,7 @@ #include "iceoryx_posh/roudi/iceoryx_roudi_components.hpp" #include "iceoryx_posh/roudi/memory/iceoryx_roudi_memory_manager.hpp" #include "iceoryx_posh/testing/roudi_environment/runtime_test_interface.hpp" +#include "iox/duration.hpp" #include #include @@ -67,9 +68,9 @@ class RouDiEnvironment private: RuntimeTestInterface m_runtimes; #if defined(__APPLE__) - std::chrono::milliseconds m_interOpWaitingTime = std::chrono::milliseconds(1000); + iox::units::Duration m_interOpWaitingTimeout{iox::units::Duration::fromMilliseconds(1000)}; #else - std::chrono::milliseconds m_interOpWaitingTime = std::chrono::milliseconds(200); + iox::units::Duration m_interOpWaitingTimeout{iox::units::Duration::fromMilliseconds(200)}; #endif std::unique_ptr m_roudiComponents; std::unique_ptr m_roudiApp; diff --git a/iceoryx_posh/testing/include/iceoryx_posh/testing/roudi_gtest.hpp b/iceoryx_posh/testing/include/iceoryx_posh/testing/roudi_gtest.hpp index ce24142148..1845946a3e 100644 --- a/iceoryx_posh/testing/include/iceoryx_posh/testing/roudi_gtest.hpp +++ b/iceoryx_posh/testing/include/iceoryx_posh/testing/roudi_gtest.hpp @@ -28,7 +28,7 @@ class RouDi_GTest : public iox::roudi::RouDiEnvironment, public Test { public: RouDi_GTest() = default; - RouDi_GTest(iox::RouDiConfig_t& roudiConfig) + RouDi_GTest(const iox::RouDiConfig_t& roudiConfig) : iox::roudi::RouDiEnvironment(roudiConfig) { } diff --git a/iceoryx_posh/testing/roudi_environment/minimal_roudi_config.cpp b/iceoryx_posh/testing/roudi_environment/minimal_roudi_config.cpp new file mode 100644 index 0000000000..21ecf03152 --- /dev/null +++ b/iceoryx_posh/testing/roudi_environment/minimal_roudi_config.cpp @@ -0,0 +1,37 @@ +// Copyright (c) 2023 by Mathias Kraus . 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/testing/roudi_environment/minimal_roudi_config.hpp" + +namespace iox +{ +namespace testing +{ +RouDiConfig_t MinimalRouDiConfigBuilder::create() const noexcept +{ + RouDiConfig_t roudiConfig; + mepoo::MePooConfig mepooConfig; + mepooConfig.addMemPool({m_payloadChunkSize, m_payloadChunkCount}); + auto currentGroup = iox::posix::PosixGroup::getGroupOfCurrentProcess(); + roudiConfig.m_sharedMemorySegments.push_back({currentGroup.getName(), currentGroup.getName(), mepooConfig}); + + roudiConfig.introspectionChunkCount = m_introspectionChunkCount; + roudiConfig.discoveryChunkCount = m_discoveryChunkCount; + + return roudiConfig; +} +} // namespace testing +} // namespace iox diff --git a/iceoryx_posh/testing/roudi_environment/roudi_environment.cpp b/iceoryx_posh/testing/roudi_environment/roudi_environment.cpp index 8683b365a9..5ad459cbdd 100644 --- a/iceoryx_posh/testing/roudi_environment/roudi_environment.cpp +++ b/iceoryx_posh/testing/roudi_environment/roudi_environment.cpp @@ -57,12 +57,12 @@ RouDiEnvironment::~RouDiEnvironment() void RouDiEnvironment::SetInterOpWaitingTime(const std::chrono::milliseconds& v) { - m_interOpWaitingTime = v; + m_interOpWaitingTimeout = units::Duration::fromMilliseconds(v.count()); } void RouDiEnvironment::InterOpWait() { - std::this_thread::sleep_for(m_interOpWaitingTime); + m_roudiApp->triggerDiscoveryLoopAndWaitToFinish(m_interOpWaitingTimeout); } void RouDiEnvironment::CleanupAppResources(const RuntimeName_t& name)