diff --git a/iceoryx_hoofs/include/iceoryx_hoofs/design_pattern/polymorphic_handler.hpp b/iceoryx_hoofs/include/iceoryx_hoofs/design_pattern/polymorphic_handler.hpp index 19a32315215..b2dea21b359 100644 --- a/iceoryx_hoofs/include/iceoryx_hoofs/design_pattern/polymorphic_handler.hpp +++ b/iceoryx_hoofs/include/iceoryx_hoofs/design_pattern/polymorphic_handler.hpp @@ -63,7 +63,7 @@ struct DefaultHooks template > class PolymorphicHandler { - static_assert(std::is_base_of::value, "Default must inherit from Interface"); + static_assert(std::is_base_of::value, "Interface must be a base class of Default"); using Self = PolymorphicHandler; friend class StaticLifetimeGuard; diff --git a/iceoryx_hoofs/include/iceoryx_hoofs/internal/design_pattern/static_lifetime_guard.inl b/iceoryx_hoofs/include/iceoryx_hoofs/internal/design_pattern/static_lifetime_guard.inl index 65a76538c0b..1fba53844c0 100644 --- a/iceoryx_hoofs/include/iceoryx_hoofs/internal/design_pattern/static_lifetime_guard.inl +++ b/iceoryx_hoofs/include/iceoryx_hoofs/internal/design_pattern/static_lifetime_guard.inl @@ -19,6 +19,7 @@ #include "iceoryx_hoofs/design_pattern/static_lifetime_guard.hpp" +#include #include namespace iox @@ -71,6 +72,7 @@ template template T& StaticLifetimeGuard::instance(Args&&... args) noexcept { + // primary guard static StaticLifetimeGuard guard; // we determine wether this call has to initialize the instance @@ -117,11 +119,36 @@ uint64_t StaticLifetimeGuard::count() template void StaticLifetimeGuard::destroy() { + // instance either exists, i.e. instance() was called and returned + // or is being called for the first time and has already set the instance + // 1) was called is no problem, because this means the primary guard must have + // gone out of scope at program end + // 2) is called for first time and has not yet returned + // - the state is INITIALIZED or INITIALIZING + // - s_count is > 0 due to the primary guard + // - s_count went up after it went to zero due to this guard that triggered this destroy + // + // NB: instance can be called for the first time in a destructor + // of a static object that is destroyed after main; + // unusual but OK if guards are used correctly to control the destruction order of all statics if (s_instance) { - s_instance->~T(); - s_instance = nullptr; - s_instanceState = UNINITIALIZED; + // there is an instance, so there MUST be a primary guard (that may have + // triggered this destroy) + // + // check the counter again, if it is zero the primary guard and all others + // are destroyed (one of them triggered this destroy) + uint64_t exp{0}; + if (s_count.compare_exchange_strong(exp, 0, std::memory_order_acq_rel)) + { + // s_count is 0, we know the primary guard was destroyed before this OR + // has triggered this destroy + // this will only happen at program end when the primary guard goes out of scope + s_instance->~T(); + s_instance = nullptr; + // NB: reinitialization is normally only possible at program end (static destruction) + s_instanceState = UNINITIALIZED; + } } } diff --git a/iceoryx_hoofs/test/moduletests/test_static_lifetime_guard.cpp b/iceoryx_hoofs/test/moduletests/test_static_lifetime_guard.cpp index ca1d462f427..d40d4f59bec 100644 --- a/iceoryx_hoofs/test/moduletests/test_static_lifetime_guard.cpp +++ b/iceoryx_hoofs/test/moduletests/test_static_lifetime_guard.cpp @@ -292,6 +292,7 @@ TEST_F(StaticLifetimeGuard_test, destructionAtZeroCountWorks) EXPECT_EQ(T::Foo::dtorCalled, 1); } +// TODO: decide whether we want this TEST_F(StaticLifetimeGuard_test, constructionAfterDestructionWorks) { ::testing::Test::RecordProperty("TEST_ID", "0077e73d-ddf5-47e7-a7c6-93819f376175"); @@ -306,7 +307,7 @@ TEST_F(StaticLifetimeGuard_test, constructionAfterDestructionWorks) EXPECT_EQ(instance.id, FIRST_INSTANCE_ID); } - // first instance destroyed (should usually only happen at the the program + // first instance destroyed (should usually only happen at the the program end // during static destruction) T::Foo::reset();