This is an automated email from the ASF dual-hosted git repository. pnoltes pushed a commit to branch pnoltes/feature/update_component_and_pattern_documentation in repository https://gitbox.apache.org/repos/asf/celix.git
commit b83d6ecaeee0bda587d391ca2af91b582b336bbe Author: Pepijn Noltes <[email protected]> AuthorDate: Tue May 17 16:43:08 2022 +0200 Updates dm cmp state to atomic and refactors dm cmp for testing. --- .../benchmark/src/DependencyManagerBenchmark.cc | 2 +- .../gtest/src/DependencyManagerTestSuite.cc | 503 ++++++++++++--------- libs/framework/src/dm_component_impl.c | 121 +++-- 3 files changed, 340 insertions(+), 286 deletions(-) diff --git a/libs/framework/benchmark/src/DependencyManagerBenchmark.cc b/libs/framework/benchmark/src/DependencyManagerBenchmark.cc index b52a236e..1a43e275 100644 --- a/libs/framework/benchmark/src/DependencyManagerBenchmark.cc +++ b/libs/framework/benchmark/src/DependencyManagerBenchmark.cc @@ -86,7 +86,7 @@ static void createAndDestroyComponentTest(benchmark::State& state, bool cTest) { celix_dependencyManager_addAsync(cMan, cmp); celix_dependencyManager_wait(cMan); - assert(celix_dmComponent_currentState(cmp) == DM_CMP_STATE_TRACKING_OPTIONAL); + assert(celix_dmComponent_currentState(cmp) == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL); celix_dependencyManager_removeAllComponents(cMan); } } else { diff --git a/libs/framework/gtest/src/DependencyManagerTestSuite.cc b/libs/framework/gtest/src/DependencyManagerTestSuite.cc index a31b2db4..3aee5f41 100644 --- a/libs/framework/gtest/src/DependencyManagerTestSuite.cc +++ b/libs/framework/gtest/src/DependencyManagerTestSuite.cc @@ -19,6 +19,7 @@ #include <gtest/gtest.h> #include <atomic> +#include <condition_variable> #include "celix/dm/DependencyManager.h" #include "celix_framework_factory.h" @@ -53,14 +54,14 @@ public: TEST_F(DependencyManagerTestSuite, DmCreateComponent) { auto *mng = celix_bundleContext_getDependencyManager(ctx); - auto *cmp = celix_dmComponent_create(ctx, "test1"); - celix_dependencyManager_add(mng, cmp); + auto *dmCmp = celix_dmComponent_create(ctx, "test1"); + celix_dependencyManager_add(mng, dmCmp); ASSERT_EQ(1, celix_dependencyManager_nrOfComponents(mng)); ASSERT_TRUE(celix_dependencyManager_allComponentsActive(mng)); - cmp = celix_dmComponent_create(ctx, "test2"); - celix_dependencyManager_add(mng, cmp); + dmCmp = celix_dmComponent_create(ctx, "test2"); + celix_dependencyManager_add(mng, dmCmp); ASSERT_EQ(2, celix_dependencyManager_nrOfComponents(mng)); ASSERT_TRUE(celix_dependencyManager_allComponentsActive(mng)); @@ -68,17 +69,17 @@ TEST_F(DependencyManagerTestSuite, DmCreateComponent) { TEST_F(DependencyManagerTestSuite, DmComponentAddRemove) { auto *mng = celix_bundleContext_getDependencyManager(ctx); - auto *cmp = celix_dmComponent_create(ctx, "test1"); - celix_dependencyManager_add(mng, cmp); + auto *dmCmp = celix_dmComponent_create(ctx, "test1"); + celix_dependencyManager_add(mng, dmCmp); ASSERT_EQ(1, celix_dependencyManager_nrOfComponents(mng)); - celix_dependencyManager_remove(mng, cmp); + celix_dependencyManager_remove(mng, dmCmp); ASSERT_EQ(0, celix_dependencyManager_nrOfComponents(mng)); - auto *cmp2 = celix_dmComponent_create(ctx, "test2"); - auto *cmp3 = celix_dmComponent_create(ctx, "test3"); - celix_dependencyManager_add(mng, cmp2); - celix_dependencyManager_add(mng, cmp3); + auto *dmCmp2 = celix_dmComponent_create(ctx, "test2"); + auto *dmCmp3 = celix_dmComponent_create(ctx, "test3"); + celix_dependencyManager_add(mng, dmCmp2); + celix_dependencyManager_add(mng, dmCmp3); ASSERT_EQ(2, celix_dependencyManager_nrOfComponents(mng)); celix_dependencyManager_removeAllComponents(mng); @@ -88,8 +89,8 @@ TEST_F(DependencyManagerTestSuite, DmComponentAddRemove) { TEST_F(DependencyManagerTestSuite, DmComponentAddRemoveAsync) { auto *mng = celix_bundleContext_getDependencyManager(ctx); - auto *cmp1 = celix_dmComponent_create(ctx, "test1"); - celix_dependencyManager_addAsync(mng, cmp1); + auto *dmCmp1 = celix_dmComponent_create(ctx, "test1"); + celix_dependencyManager_addAsync(mng, dmCmp1); celix_dependencyManager_wait(mng); EXPECT_EQ(1, celix_dependencyManager_nrOfComponents(mng)); @@ -99,7 +100,7 @@ TEST_F(DependencyManagerTestSuite, DmComponentAddRemoveAsync) { c->fetch_add(1); }; - celix_dependencyManager_removeAsync(mng, cmp1, &count, cb); + celix_dependencyManager_removeAsync(mng, dmCmp1, &count, cb); celix_dependencyManager_wait(mng); EXPECT_EQ(0, celix_dependencyManager_nrOfComponents(mng)); EXPECT_EQ(1, count.load()); @@ -107,10 +108,10 @@ TEST_F(DependencyManagerTestSuite, DmComponentAddRemoveAsync) { TEST_F(DependencyManagerTestSuite, DmComponentRemoveAllAsync) { auto *mng = celix_bundleContext_getDependencyManager(ctx); - auto *cmp1 = celix_dmComponent_create(ctx, "test1"); - auto *cmp2 = celix_dmComponent_create(ctx, "test2"); - celix_dependencyManager_add(mng, cmp1); - celix_dependencyManager_add(mng, cmp2); + auto *dmCmp1 = celix_dmComponent_create(ctx, "test1"); + auto *dmCmp2 = celix_dmComponent_create(ctx, "test2"); + celix_dependencyManager_add(mng, dmCmp1); + celix_dependencyManager_add(mng, dmCmp2); EXPECT_EQ(2, celix_dependencyManager_nrOfComponents(mng)); std::atomic<std::size_t> count{0}; @@ -125,19 +126,19 @@ TEST_F(DependencyManagerTestSuite, DmComponentRemoveAllAsync) { TEST_F(DependencyManagerTestSuite, DmGetInfo) { auto* mng = celix_bundleContext_getDependencyManager(ctx); - auto* cmp = celix_dmComponent_create(ctx, "test1"); + auto* dmCmp = celix_dmComponent_create(ctx, "test1"); void* dummyInterfacePointer = (void*)0x42; auto* p = celix_properties_create(); celix_properties_set(p, "key", "value"); - celix_dmComponent_addInterface(cmp, "test-interface", nullptr, dummyInterfacePointer, p); + celix_dmComponent_addInterface(dmCmp, "test-interface", nullptr, dummyInterfacePointer, p); auto* dep = celix_dmServiceDependency_create(); celix_dmServiceDependency_setService(dep, "test-interface", nullptr, nullptr); - celix_dmComponent_addServiceDependency(cmp, dep); + celix_dmComponent_addServiceDependency(dmCmp, dep); - celix_dependencyManager_add(mng, cmp); + celix_dependencyManager_add(mng, dmCmp); auto* infos = celix_dependencyManager_createInfos(mng); ASSERT_EQ(1, celix_arrayList_size(infos)); @@ -153,15 +154,15 @@ TEST_F(DependencyManagerTestSuite, DmGetInfo) { TEST_F(DependencyManagerTestSuite, TestCheckActive) { auto *mng = celix_bundleContext_getDependencyManager(ctx); - auto *cmp = celix_dmComponent_create(ctx, "test1"); + auto *dmCmp = celix_dmComponent_create(ctx, "test1"); auto *dep = celix_dmServiceDependency_create(); celix_dmServiceDependency_setService(dep, "svcname", nullptr, nullptr); celix_dmServiceDependency_setRequired(dep, true); - celix_dmComponent_addServiceDependency(cmp, dep); //required dep -> cmp not active + celix_dmComponent_addServiceDependency(dmCmp, dep); //required dep -> dmCmp not active - celix_dependencyManager_add(mng, cmp); + celix_dependencyManager_add(mng, dmCmp); ASSERT_FALSE(celix_dependencyManager_areComponentsActive(mng)); } @@ -188,9 +189,9 @@ public: TEST_F(DependencyManagerTestSuite, CxxDmGetInfo) { celix::dm::DependencyManager mng{ctx}; - auto& cmp = mng.createComponent<Cmp1>(); - cmp.createProvidedService<TestService>().addProperty("key", "value"); - cmp.createServiceDependency<TestService>().setVersionRange("[1,2)").setRequired(true); + auto& dmCmp = mng.createComponent<Cmp1>(); + dmCmp.createProvidedService<TestService>().addProperty("key", "value"); + dmCmp.createServiceDependency<TestService>().setVersionRange("[1,2)").setRequired(true); auto infos = mng.getInfos(); EXPECT_EQ(infos.size(), 1); @@ -204,22 +205,22 @@ TEST_F(DependencyManagerTestSuite, CxxDmGetInfo) { info = mng.getInfo(); //new info ASSERT_EQ(info.components.size(), 1); //build - auto& cmpInfo = info.components[0]; - EXPECT_TRUE(!cmpInfo.uuid.empty()); - EXPECT_EQ(cmpInfo.name, "Cmp1"); - EXPECT_EQ(cmpInfo.state, "CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED"); - EXPECT_FALSE(cmpInfo.isActive); - EXPECT_EQ(cmpInfo.nrOfTimesStarted, 0); - EXPECT_EQ(cmpInfo.nrOfTimesResumed, 0); + auto& dmCmpInfo = info.components[0]; + EXPECT_TRUE(!dmCmpInfo.uuid.empty()); + EXPECT_EQ(dmCmpInfo.name, "Cmp1"); + EXPECT_EQ(dmCmpInfo.state, "WAITING_FOR_REQUIRED"); + EXPECT_FALSE(dmCmpInfo.isActive); + EXPECT_EQ(dmCmpInfo.nrOfTimesStarted, 0); + EXPECT_EQ(dmCmpInfo.nrOfTimesResumed, 0); - ASSERT_EQ(cmpInfo.interfacesInfo.size(), 1); - auto& intf = cmpInfo.interfacesInfo[0]; + ASSERT_EQ(dmCmpInfo.interfacesInfo.size(), 1); + auto& intf = dmCmpInfo.interfacesInfo[0]; EXPECT_EQ(intf.serviceName, "TestService"); - EXPECT_TRUE(intf.properties.size() > 0); + EXPECT_TRUE(!intf.properties.empty()); EXPECT_NE(intf.properties.find("key"), intf.properties.end()); - ASSERT_EQ(cmpInfo.dependenciesInfo.size(), 1); - auto& dep = cmpInfo.dependenciesInfo[0]; + ASSERT_EQ(dmCmpInfo.dependenciesInfo.size(), 1); + auto& dep = dmCmpInfo.dependenciesInfo[0]; EXPECT_EQ(dep.serviceName, "TestService"); EXPECT_EQ(dep.isRequired, true); EXPECT_EQ(dep.versionRange, "[1,2)"); @@ -231,13 +232,13 @@ TEST_F(DependencyManagerTestSuite, OnlyActiveAfterBuildCheck) { celix::dm::DependencyManager dm{ctx}; EXPECT_EQ(0, dm.getNrOfComponents()); - auto& cmp = dm.createComponent<TestComponent>(std::make_shared<TestComponent>(), "test1"); + auto& dmCmp = dm.createComponent<TestComponent>(std::make_shared<TestComponent>(), "test1"); EXPECT_EQ(0, dm.getNrOfComponents()); //dm not started yet / comp not build yet - EXPECT_TRUE(cmp.isValid()); + EXPECT_TRUE(dmCmp.isValid()); - cmp.build(); - cmp.build(); //should be ok to call twice - EXPECT_EQ(1, dm.getNrOfComponents()); //cmp "build", so active + dmCmp.build(); + dmCmp.build(); //should be ok to call twice + EXPECT_EQ(1, dm.getNrOfComponents()); //dmCmp "build", so active dm.clear(); dm.clear(); //should be ok to call twice @@ -248,12 +249,12 @@ TEST_F(DependencyManagerTestSuite, StartDmWillBuildCmp) { celix::dm::DependencyManager dm{ctx}; EXPECT_EQ(0, dm.getNrOfComponents()); - auto& cmp = dm.createComponent<TestComponent>(std::make_shared<TestComponent>(), "test1"); + auto& dmCmp = dm.createComponent<TestComponent>(std::make_shared<TestComponent>(), "test1"); EXPECT_EQ(0, dm.getNrOfComponents()); //dm not started yet / comp not build yet - EXPECT_TRUE(cmp.isValid()); + EXPECT_TRUE(dmCmp.isValid()); dm.start(); - EXPECT_EQ(1, dm.getNrOfComponents()); //cmp "build", so active + EXPECT_EQ(1, dm.getNrOfComponents()); //dmCmp "build", so active dm.stop(); EXPECT_EQ(0, dm.getNrOfComponents()); //dm cleared so no components @@ -279,21 +280,21 @@ TEST_F(DependencyManagerTestSuite, AddSvcProvideAfterBuild) { celix::dm::DependencyManager dm{ctx}; EXPECT_EQ(0, dm.getNrOfComponents()); - auto& cmp = dm.createComponent<TestComponent>(std::make_shared<TestComponent>(), "test1"); + auto& dmCmp = dm.createComponent<TestComponent>(std::make_shared<TestComponent>(), "test1"); EXPECT_EQ(0, dm.getNrOfComponents()); //dm not started yet / comp not build yet - EXPECT_TRUE(cmp.isValid()); + EXPECT_TRUE(dmCmp.isValid()); - cmp.build(); - EXPECT_EQ(1, dm.getNrOfComponents()); //cmp "build", so active + dmCmp.build(); + EXPECT_EQ(1, dm.getNrOfComponents()); //dmCmp "build", so active TestService svc{}; - cmp.addCInterface(&svc, "TestService"); + dmCmp.addCInterface(&svc, "TestService"); long svcId = celix_bundleContext_findService(ctx, "TestService"); EXPECT_EQ(-1, svcId); //not build -> not found - cmp.build(); - cmp.build(); //should be ok to call twice + dmCmp.build(); + dmCmp.build(); //should be ok to call twice svcId = celix_bundleContext_findService(ctx, "TestService"); EXPECT_GT(svcId, -1); //(re)build -> found @@ -309,21 +310,21 @@ TEST_F(DependencyManagerTestSuite, BuildSvcProvide) { celix::dm::DependencyManager dm{ctx}; EXPECT_EQ(0, dm.getNrOfComponents()); - auto& cmp = dm.createComponent<Cmp1>(std::make_shared<Cmp1>(), "test2"); + auto& dmCmp = dm.createComponent<Cmp1>(std::make_shared<Cmp1>(), "test2"); EXPECT_EQ(0, dm.getNrOfComponents()); //dm not started yet / comp not build yet - EXPECT_TRUE(cmp.isValid()); + EXPECT_TRUE(dmCmp.isValid()); - cmp.build(); - EXPECT_EQ(1, dm.getNrOfComponents()); //cmp "build", so active + dmCmp.build(); + EXPECT_EQ(1, dm.getNrOfComponents()); //dmCmp "build", so active TestService svc{}; - cmp.createProvidedCService(&svc, "CTestService").addProperty("key1", "val1").addProperty("key2", 3); + dmCmp.createProvidedCService(&svc, "CTestService").addProperty("key1", "val1").addProperty("key2", 3); long svcId = celix_bundleContext_findService(ctx, "CTestService"); EXPECT_EQ(-1, svcId); //not build -> not found - cmp.build(); - cmp.build(); //should be ok to call twice + dmCmp.build(); + dmCmp.build(); //should be ok to call twice dm.wait(); svcId = celix_bundleContext_findService(ctx, "CTestService"); EXPECT_GT(svcId, -1); //(re)build -> found @@ -336,7 +337,7 @@ TEST_F(DependencyManagerTestSuite, BuildSvcProvide) { celix::dm::Properties props{}; props["key1"] = "value"; - cmp.createProvidedService<TestService>().setProperties(props).setVersion("1.0.0").build(); + dmCmp.createProvidedService<TestService>().setProperties(props).setVersion("1.0.0").build(); opts.serviceName = "TestService"; opts.filter = "(key1=value)"; @@ -355,16 +356,16 @@ TEST_F(DependencyManagerTestSuite, AddSvcDepAfterBuild) { celix::dm::DependencyManager dm{ctx}; EXPECT_EQ(0, dm.getNrOfComponents()); - auto& cmp = dm.createComponent<TestComponent>(std::make_shared<TestComponent>(), "test1"); + auto& dmCmp = dm.createComponent<TestComponent>(std::make_shared<TestComponent>(), "test1"); EXPECT_EQ(0, dm.getNrOfComponents()); //dm not started yet / comp not build yet - EXPECT_TRUE(cmp.isValid()); + EXPECT_TRUE(dmCmp.isValid()); - cmp.build(); - cmp.build(); //should be ok to call twice - EXPECT_EQ(1, dm.getNrOfComponents()); //cmp "build", so active + dmCmp.build(); + dmCmp.build(); //should be ok to call twice + EXPECT_EQ(1, dm.getNrOfComponents()); //dmCmp "build", so active std::atomic<int> count{0}; - auto& dep = cmp.createCServiceDependency<TestService>("TestService") + auto& dep = dmCmp.createCServiceDependency<TestService>("TestService") .setCallbacks([&count](const TestService*, celix::dm::Properties&&) { count++; }); @@ -381,13 +382,13 @@ TEST_F(DependencyManagerTestSuite, AddSvcDepAfterBuild) { ASSERT_EQ(1, count); //service dep build -> so count is 1; //create another service dep - cmp.createCServiceDependency<TestService>("TestService") + dmCmp.createCServiceDependency<TestService>("TestService") .setCallbacks([&count](const TestService*, celix::dm::Properties&&) { count++; }); ASSERT_EQ(1, count); //new service dep not yet build -> so count still 1 - cmp.build(); //cmp build, which will build svc dep + dmCmp.build(); //dmCmp build, which will build svc dep ASSERT_EQ(2, count); //new service dep build -> so count is 2 celix_bundleContext_unregisterService(ctx, svcId); @@ -398,28 +399,28 @@ TEST_F(DependencyManagerTestSuite, InCompleteBuildShouldNotLeak) { celix::dm::DependencyManager dm{ctx}; dm.createComponent<TestComponent>(std::make_shared<TestComponent>(), "test1"); //NOTE NOT BUILD - auto& cmp2 = dm.createComponent<Cmp1>(std::make_shared<Cmp1>(), "test2").build(); //NOTE BUILD - cmp2.createCServiceDependency<TestService>("TestService").setFilter("(key=value"); //note not build - cmp2.createServiceDependency<TestService>().setFilter("(key=value)").setName("alternative name"); //note not build + auto& dmCmp2 = dm.createComponent<Cmp1>(std::make_shared<Cmp1>(), "test2").build(); //NOTE BUILD + dmCmp2.createCServiceDependency<TestService>("TestService").setFilter("(key=value"); //note not build + dmCmp2.createServiceDependency<TestService>().setFilter("(key=value)").setName("alternative name"); //note not build TestService svc{}; - cmp2.createProvidedCService(&svc, "CTestService").addProperty("key1", "val1"); //note not build - cmp2.createProvidedService<TestService>().setVersion("1.0.0"); //note not build + dmCmp2.createProvidedCService(&svc, "CTestService").addProperty("key1", "val1"); //note not build + dmCmp2.createProvidedService<TestService>().setVersion("1.0.0"); //note not build } TEST_F(DependencyManagerTestSuite, RemoveAndClear) { celix::dm::DependencyManager dm{ctx}; - auto& cmp1 = dm.createComponent<TestComponent>(std::make_shared<TestComponent>()).build(); - auto& cmp2 = dm.createComponent<TestComponent>(std::make_shared<TestComponent>()).build(); + auto& dmCmp1 = dm.createComponent<TestComponent>(std::make_shared<TestComponent>()).build(); + auto& dmCmp2 = dm.createComponent<TestComponent>(std::make_shared<TestComponent>()).build(); dm.createComponent<TestComponent>(std::make_shared<TestComponent>()).build(); - auto& cmp4 = dm.createComponent<TestComponent>(std::make_shared<TestComponent>()); + auto& dmCmp4 = dm.createComponent<TestComponent>(std::make_shared<TestComponent>()); dm.wait(); - dm.destroyComponent(cmp1); - bool removed = dm.removeComponent(cmp2.getUUID()); + dm.destroyComponent(dmCmp1); + bool removed = dm.removeComponent(dmCmp2.getUUID()); EXPECT_TRUE(removed); - removed = dm.removeComponentAsync(cmp4.getUUID()); + removed = dm.removeComponentAsync(dmCmp4.getUUID()); EXPECT_TRUE(removed); dm.clear(); } @@ -465,13 +466,13 @@ TEST_F(DependencyManagerTestSuite, RequiredDepsAreInjectedDuringStartStop) { }; celix::dm::DependencyManager dm{ctx}; - auto& cmp = dm.createComponent<LifecycleComponent>() + auto& dmCmp = dm.createComponent<LifecycleComponent>() .setCallbacks(nullptr, &LifecycleComponent::start, &LifecycleComponent::stop, nullptr); - cmp.createServiceDependency<TestService>() + dmCmp.createServiceDependency<TestService>() .setRequired(true) .setCallbacks(&LifecycleComponent::setService) .setCallbacks(&LifecycleComponent::addService, &LifecycleComponent::remService); - cmp.build(); + dmCmp.build(); TestService svc; std::string svcName = celix::typeName<TestService>(); @@ -482,124 +483,190 @@ TEST_F(DependencyManagerTestSuite, RequiredDepsAreInjectedDuringStartStop) { long svcId = celix_bundleContext_registerServiceWithOptions(dm.bundleContext(), &opts); EXPECT_GE(svcId, 0); - EXPECT_EQ(cmp.getState(), ComponentState::TRACKING_OPTIONAL); + EXPECT_EQ(dmCmp.getState(), ComponentState::TRACKING_OPTIONAL); celix_bundleContext_unregisterService(dm.bundleContext(), svcId); } +TEST_F(DependencyManagerTestSuite, RemoveOwnDependencyShouldNotLeadToDoubleStop) { + class LifecycleComponent : public TestService { + public: + void start() { + startCount.fetch_add(1); + } + + void stop() { + stopCount.fetch_add(1); + } + + int getStartCount() const { return startCount.load(); } + + int getStopCount() const { return stopCount.load(); } + private: + std::atomic<int> startCount{0}; + std::atomic<int> stopCount{0}; + }; + + celix::dm::DependencyManager dm{ctx}; + auto lifecycleCmp = std::make_shared<LifecycleComponent>(); + auto& dmCmp = dm.createComponent<LifecycleComponent>(lifecycleCmp) + .setCallbacks(nullptr, &LifecycleComponent::start, &LifecycleComponent::stop, nullptr); + dmCmp.createProvidedService<TestService>(); + dmCmp.createServiceDependency<TestService>() + .setRequired(true); + dmCmp.build(); + + using celix::dm::ComponentState; + EXPECT_EQ(dmCmp.getState(), ComponentState::WAITING_FOR_REQUIRED); + + TestService svc{}; + long svcId = celix_bundleContext_registerService(ctx, &svc, "TestService", nullptr); + celix_bundleContext_waitForEvents(ctx); + + EXPECT_EQ(dmCmp.getState(), ComponentState::TRACKING_OPTIONAL); + EXPECT_EQ(lifecycleCmp->getStartCount(), 1); + + celix_bundleContext_unregisterService(ctx, svcId); //removes req dep, but cmp can be depend on own dep + + EXPECT_EQ(dmCmp.getState(), ComponentState::TRACKING_OPTIONAL); + EXPECT_EQ(lifecycleCmp->getStopCount(), 0); + + //add additional req dep -> stop component -> unregister TestService -> rem own TestService dep + dmCmp.createServiceDependency<TestService>("DummyName") + .setRequired(true) + .buildAsync(); + + celix_bundleContext_waitForEvents(ctx); + EXPECT_EQ(dmCmp.getState(), ComponentState::INSTANTIATED_AND_WAITING_FOR_REQUIRED); + EXPECT_EQ(lifecycleCmp->getStopCount(), 1); +} + + TEST_F(DependencyManagerTestSuite, IntermediaStatesDuringInitDeinitStartingAndStopping) { class LifecycleComponent { public: - enum class InMethod { - NO_METHOD, - INIT, - DEINIT, - START, - STOP + enum class LifecycleMethod { + None = 0, + Init = 1, + Start = 2, + Stop = 3, + Deinit = 4, }; void init() { std::cout << "in init callback\n"; - std::unique_lock<std::mutex> lck{mutex}; - inMethod = InMethod::INIT; - cond.notify_all(); - cond.wait_for(lck, std::chrono::seconds{1}); - inMethod = InMethod::NO_METHOD; + setStateAndWaitToContinue(LifecycleMethod::Init); } void deinit() { std::cout << "in deinit callback\n"; - std::unique_lock<std::mutex> lck{mutex}; - inMethod = InMethod::DEINIT; - cond.notify_all(); - cond.wait_for(lck, std::chrono::seconds{1}); - inMethod = InMethod::NO_METHOD; + setStateAndWaitToContinue(LifecycleMethod::Deinit); } void start() { std::cout << "in start callback\n"; - std::unique_lock<std::mutex> lck{mutex}; - inMethod = InMethod::START; - cond.notify_all(); - cond.wait_for(lck, std::chrono::seconds{1}); - inMethod = InMethod::NO_METHOD; + setStateAndWaitToContinue(LifecycleMethod::Start); } void stop() { std::cout << "in stop callback\n"; - std::unique_lock<std::mutex> lck{mutex}; - inMethod = InMethod::STOP; + setStateAndWaitToContinue(LifecycleMethod::Stop); + } + + void setStayInMethod(LifecycleMethod m) { + std::lock_guard<std::mutex> lck{mutex}; + stayInMethod = m; cond.notify_all(); - cond.wait_for(lck, std::chrono::seconds{1}); - inMethod = InMethod::NO_METHOD; } - void waitFor(InMethod s) { + void waitUntilInMethod(LifecycleMethod m) { std::unique_lock<std::mutex> lck{mutex}; - cond.wait_for(lck, std::chrono::seconds{1}, [&]{return inMethod == s;}); + cond.wait_for(lck, std::chrono::seconds{5}, [&]{ return currentMethod == m; }); } - - void cont() { - std::lock_guard<std::mutex> lck{mutex}; + private: + void setStateAndWaitToContinue(LifecycleMethod m) { + std::unique_lock<std::mutex> lck{mutex}; + currentMethod = m; + cond.notify_all(); + cond.wait_for(lck, std::chrono::seconds{5}, [&] { return currentMethod != stayInMethod; }); + currentMethod = LifecycleMethod::None; cond.notify_all(); } - private: std::mutex mutex{}; std::condition_variable cond{}; - InMethod inMethod = InMethod::NO_METHOD; + LifecycleMethod stayInMethod = LifecycleMethod::None; + LifecycleMethod currentMethod = LifecycleMethod::None; }; + using LifecycleMethod = LifecycleComponent::LifecycleMethod; + celix::dm::DependencyManager dm{ctx}; auto lifecycleCmp = std::make_shared<LifecycleComponent>(); - auto& cmp = dm.createComponent<LifecycleComponent>(lifecycleCmp) + lifecycleCmp->setStayInMethod(LifecycleMethod::Init); + auto& dmCmp = dm.createComponent<LifecycleComponent>(lifecycleCmp) .setCallbacks(&LifecycleComponent::init, &LifecycleComponent::start, &LifecycleComponent::stop, &LifecycleComponent::deinit); - cmp.createServiceDependency<TestService>() + dmCmp.createServiceDependency<TestService>() + .setStrategy(celix::dm::DependencyUpdateStrategy::suspend) + .setCallbacks([](std::shared_ptr<TestService> /*service*/, const std::shared_ptr<const celix::Properties>& /*properties*/){ std::cout << "Dummy set for svc callback\n"; }) .setRequired(false); - cmp.build(); + dmCmp.createServiceDependency<TestService>("RequiredTestService") + .setStrategy(celix::dm::DependencyUpdateStrategy::locking) + .setRequired(true); + dmCmp.buildAsync(); + + TestService reqSvc{}; + long reqSvcId = celix_bundleContext_registerServiceAsync(ctx, &reqSvc, "RequiredTestService", nullptr); + EXPECT_GE(reqSvcId, 0); using celix::dm::ComponentState; - lifecycleCmp->waitFor(LifecycleComponent::InMethod::INIT); - EXPECT_EQ(cmp.getState(), ComponentState::INITIALIZING); - lifecycleCmp->cont(); + lifecycleCmp->waitUntilInMethod(LifecycleMethod::Init); + EXPECT_EQ(dmCmp.getState(), ComponentState::INITIALIZING); - lifecycleCmp->waitFor(LifecycleComponent::InMethod::START); - EXPECT_EQ(cmp.getState(), ComponentState::STARTING); - lifecycleCmp->cont(); + lifecycleCmp->setStayInMethod(LifecycleMethod::Start); + lifecycleCmp->waitUntilInMethod(LifecycleMethod::Start); + EXPECT_EQ(dmCmp.getState(), ComponentState::STARTING); - lifecycleCmp->waitFor(LifecycleComponent::InMethod::START); - EXPECT_EQ(cmp.getState(), ComponentState::STARTING); - lifecycleCmp->cont(); + lifecycleCmp->setStayInMethod(LifecycleMethod::None); + lifecycleCmp->waitUntilInMethod(LifecycleMethod::None); //Adding service should lead to a suspend/resume (stop/start) + + lifecycleCmp->setStayInMethod(LifecycleMethod::Stop); TestService svc; std::string svcName = celix::typeName<TestService>(); celix_service_registration_options opts{}; opts.svc = &svc; opts.serviceName = svcName.c_str(); - long svcId = celix_bundleContext_registerServiceWithOptions(dm.bundleContext(), &opts); - EXPECT_GE(svcId, 0); + long optionalSvcId = celix_bundleContext_registerServiceWithOptionsAsync(dm.bundleContext(), &opts); + EXPECT_GE(optionalSvcId, 0); - lifecycleCmp->waitFor(LifecycleComponent::InMethod::STOP); - EXPECT_EQ(cmp.getState(), ComponentState::SUSPENDING); - lifecycleCmp->cont(); + lifecycleCmp->waitUntilInMethod(LifecycleMethod::Stop); + EXPECT_EQ(dmCmp.getState(), ComponentState::SUSPENDING); - //svc will be injected + //svc will be injected -> component will resume + lifecycleCmp->setStayInMethod(LifecycleMethod::Start); + lifecycleCmp->waitUntilInMethod(LifecycleMethod::Start); + EXPECT_EQ(dmCmp.getState(), ComponentState::RESUMING); - lifecycleCmp->waitFor(LifecycleComponent::InMethod::START); - EXPECT_EQ(cmp.getState(), ComponentState::RESUMING); - lifecycleCmp->cont(); - //Adding a required svc should lead to INSTANTIATED_AND_WAITING_FOR_REQUIRED - cmp.createServiceDependency<TestService>() - .setFilter("(non-existing=*)") - .setRequired(true) - .build(); - lifecycleCmp->waitFor(LifecycleComponent::InMethod::STOP); - EXPECT_EQ(cmp.getState(), ComponentState::STOPPING); - lifecycleCmp->cont(); + //Remove required svc so that component stops + lifecycleCmp->setStayInMethod(LifecycleMethod::Stop); + celix_bundleContext_unregisterServiceAsync(ctx, reqSvcId, nullptr, nullptr); + lifecycleCmp->waitUntilInMethod(LifecycleMethod::Stop); + EXPECT_EQ(dmCmp.getState(), ComponentState::STOPPING); - celix_bundleContext_unregisterService(dm.bundleContext(), svcId); + lifecycleCmp->setStayInMethod(LifecycleMethod::None); + lifecycleCmp->waitUntilInMethod(LifecycleMethod::None); + + //Disable component should deinit the component + lifecycleCmp->setStayInMethod(LifecycleMethod::Deinit); + dm.removeComponentAsync(dmCmp.getUUID()); + lifecycleCmp->waitUntilInMethod(LifecycleMethod::Deinit); + EXPECT_EQ(dmCmp.getState(), ComponentState::DEINITIALIZING); + + lifecycleCmp->setStayInMethod(LifecycleMethod::None); + celix_bundleContext_unregisterService(dm.bundleContext(), optionalSvcId); } TEST_F(DependencyManagerTestSuite, DepsAreInjectedAsSharedPointers) { @@ -645,13 +712,13 @@ TEST_F(DependencyManagerTestSuite, DepsAreInjectedAsSharedPointers) { }; celix::dm::DependencyManager dm{ctx}; - auto& cmp = dm.createComponent<LifecycleComponent>() + auto& dmCmp = dm.createComponent<LifecycleComponent>() .setCallbacks(nullptr, &LifecycleComponent::start, &LifecycleComponent::stop, nullptr); - cmp.createServiceDependency<TestService>() + dmCmp.createServiceDependency<TestService>() .setRequired(true) .setCallbacks(&LifecycleComponent::setService) .setCallbacks(&LifecycleComponent::addService, &LifecycleComponent::remService); - cmp.build(); + dmCmp.build(); TestService svc; std::string svcName = celix::typeName<TestService>(); @@ -662,7 +729,7 @@ TEST_F(DependencyManagerTestSuite, DepsAreInjectedAsSharedPointers) { long svcId = celix_bundleContext_registerServiceWithOptions(dm.bundleContext(), &opts); EXPECT_GE(svcId, 0); - EXPECT_EQ(cmp.getState(), ComponentState::TRACKING_OPTIONAL); + EXPECT_EQ(dmCmp.getState(), ComponentState::TRACKING_OPTIONAL); celix_bundleContext_unregisterService(dm.bundleContext(), svcId); } @@ -707,13 +774,13 @@ TEST_F(DependencyManagerTestSuite, DepsNoPropsAreInjectedAsSharedPointers) { }; celix::dm::DependencyManager dm{ctx}; - auto& cmp = dm.createComponent<LifecycleComponent>() + auto& dmCmp = dm.createComponent<LifecycleComponent>() .setCallbacks(nullptr, &LifecycleComponent::start, &LifecycleComponent::stop, nullptr); - cmp.createServiceDependency<TestService>() + dmCmp.createServiceDependency<TestService>() .setRequired(true) .setCallbacks(&LifecycleComponent::setService) .setCallbacks(&LifecycleComponent::addService, &LifecycleComponent::remService); - cmp.build(); + dmCmp.build(); TestService svc; std::string svcName = celix::typeName<TestService>(); @@ -724,7 +791,7 @@ TEST_F(DependencyManagerTestSuite, DepsNoPropsAreInjectedAsSharedPointers) { long svcId = celix_bundleContext_registerServiceWithOptions(dm.bundleContext(), &opts); EXPECT_GE(svcId, 0); - EXPECT_EQ(cmp.getState(), ComponentState::TRACKING_OPTIONAL); + EXPECT_EQ(dmCmp.getState(), ComponentState::TRACKING_OPTIONAL); celix_bundleContext_unregisterService(dm.bundleContext(), svcId); } @@ -756,22 +823,22 @@ TEST_F(DependencyManagerTestSuite, UnneededSuspendIsPrevented) { }; celix::dm::DependencyManager dm{ctx}; - //cmp1 has lifecycle callbacks, but not set or add/rem callbacks for the service dependency -> should not trigger suspend - auto& cmp1 = dm.createComponent<CounterComponent>("CounterCmp1") + //dmCmp1 has lifecycle callbacks, but not set or add/rem callbacks for the service dependency -> should not trigger suspend + auto& dmCmp1 = dm.createComponent<CounterComponent>("CounterCmp1") .setCallbacks(nullptr, &CounterComponent::start, &CounterComponent::stop, nullptr); - cmp1.createServiceDependency<TestService>(); - cmp1.build(); + dmCmp1.createServiceDependency<TestService>(); + dmCmp1.build(); - //cmp2 has lifecycle callbacks and set, add/rem callbacks for the service dependency -> should trigger suspend 2x - auto& cmp2 = dm.createComponent<CounterComponent>("CounterCmp2") + //dmCmp2 has lifecycle callbacks and set, add/rem callbacks for the service dependency -> should trigger suspend 2x + auto& dmCmp2 = dm.createComponent<CounterComponent>("CounterCmp2") .setCallbacks(nullptr, &CounterComponent::start, &CounterComponent::stop, nullptr); - cmp2.createServiceDependency<TestService>() + dmCmp2.createServiceDependency<TestService>() .setCallbacks(&CounterComponent::setService) .setCallbacks(&CounterComponent::addService, &CounterComponent::remService); - cmp2.build(); + dmCmp2.build(); - EXPECT_EQ(cmp1.getState(), celix::dm::ComponentState::TRACKING_OPTIONAL); - EXPECT_EQ(cmp2.getState(), celix::dm::ComponentState::TRACKING_OPTIONAL); + EXPECT_EQ(dmCmp1.getState(), celix::dm::ComponentState::TRACKING_OPTIONAL); + EXPECT_EQ(dmCmp2.getState(), celix::dm::ComponentState::TRACKING_OPTIONAL); TestService svc; std::string svcName = celix::typeName<TestService>(); @@ -782,21 +849,21 @@ TEST_F(DependencyManagerTestSuite, UnneededSuspendIsPrevented) { long svcId = celix_bundleContext_registerServiceWithOptions(dm.bundleContext(), &opts); EXPECT_GE(svcId, 0); - EXPECT_EQ(cmp1.getInstance().startCount, 1); //only once during creation - EXPECT_EQ(cmp1.getInstance().stopCount, 0); - EXPECT_EQ(cmp2.getInstance().startCount, 3); //1x creation, 1x suspend for set, 1x suspend for add - EXPECT_EQ(cmp2.getInstance().stopCount, 2); //1x suspend for set, 1x suspend for add + EXPECT_EQ(dmCmp1.getInstance().startCount, 1); //only once during creation + EXPECT_EQ(dmCmp1.getInstance().stopCount, 0); + EXPECT_EQ(dmCmp2.getInstance().startCount, 3); //1x creation, 1x suspend for set, 1x suspend for add + EXPECT_EQ(dmCmp2.getInstance().stopCount, 2); //1x suspend for set, 1x suspend for add - cmp1.getInstance().startCount = 0; - cmp1.getInstance().stopCount = 0; - cmp2.getInstance().startCount = 0; - cmp2.getInstance().stopCount = 0; + dmCmp1.getInstance().startCount = 0; + dmCmp1.getInstance().stopCount = 0; + dmCmp2.getInstance().startCount = 0; + dmCmp2.getInstance().stopCount = 0; celix_bundleContext_unregisterService(dm.bundleContext(), svcId); - EXPECT_EQ(cmp1.getInstance().startCount, 0); - EXPECT_EQ(cmp1.getInstance().stopCount, 0); - EXPECT_EQ(cmp2.getInstance().startCount, 2); //1x suspend for set nullptr, 1x suspend for rem - EXPECT_EQ(cmp2.getInstance().stopCount, 2); //1x suspend for set nullptr, 1x suspend for rem + EXPECT_EQ(dmCmp1.getInstance().startCount, 0); + EXPECT_EQ(dmCmp1.getInstance().stopCount, 0); + EXPECT_EQ(dmCmp2.getInstance().startCount, 2); //1x suspend for set nullptr, 1x suspend for rem + EXPECT_EQ(dmCmp2.getInstance().stopCount, 2); //1x suspend for set nullptr, 1x suspend for rem } TEST_F(DependencyManagerTestSuite, ExceptionsInLifecycle) { @@ -840,43 +907,43 @@ TEST_F(DependencyManagerTestSuite, ExceptionsInLifecycle) { celix::dm::DependencyManager dm{ctx}; { - auto& cmp = dm.createComponent(std::make_shared<ExceptionComponent>(ExceptionComponent::LifecycleMethod::INIT), "FailAtInitCmp") + auto& dmCmp = dm.createComponent(std::make_shared<ExceptionComponent>(ExceptionComponent::LifecycleMethod::INIT), "FailAtInitCmp") .setCallbacks(&ExceptionComponent::init, &ExceptionComponent::start, &ExceptionComponent::stop, &ExceptionComponent::deinit); - EXPECT_EQ(cmp.getState(), ComponentState::INACTIVE); - cmp.build(); //fails at init and should disable - EXPECT_EQ(cmp.getState(), ComponentState::INACTIVE); + EXPECT_EQ(dmCmp.getState(), ComponentState::INACTIVE); + dmCmp.build(); //fails at init and should disable + EXPECT_EQ(dmCmp.getState(), ComponentState::INACTIVE); dm.clear(); } { - auto& cmp = dm.createComponent(std::make_shared<ExceptionComponent>(ExceptionComponent::LifecycleMethod::START), "FailAtStartCmp") + auto& dmCmp = dm.createComponent(std::make_shared<ExceptionComponent>(ExceptionComponent::LifecycleMethod::START), "FailAtStartCmp") .setCallbacks(&ExceptionComponent::init, &ExceptionComponent::start, &ExceptionComponent::stop, &ExceptionComponent::deinit); - EXPECT_EQ(cmp.getState(), ComponentState::INACTIVE); - cmp.build(); //fails at init and should disable - EXPECT_EQ(cmp.getState(), ComponentState::INACTIVE); + EXPECT_EQ(dmCmp.getState(), ComponentState::INACTIVE); + dmCmp.build(); //fails at init and should disable + EXPECT_EQ(dmCmp.getState(), ComponentState::INACTIVE); dm.clear(); } { - auto& cmp = dm.createComponent(std::make_shared<ExceptionComponent>(ExceptionComponent::LifecycleMethod::STOP), "FailAtStopCmp") + auto& dmCmp = dm.createComponent(std::make_shared<ExceptionComponent>(ExceptionComponent::LifecycleMethod::STOP), "FailAtStopCmp") .setCallbacks(&ExceptionComponent::init, &ExceptionComponent::start, &ExceptionComponent::stop, &ExceptionComponent::deinit); - EXPECT_EQ(cmp.getState(), ComponentState::INACTIVE); - cmp.build(); - EXPECT_EQ(cmp.getState(), ComponentState::TRACKING_OPTIONAL); + EXPECT_EQ(dmCmp.getState(), ComponentState::INACTIVE); + dmCmp.build(); + EXPECT_EQ(dmCmp.getState(), ComponentState::TRACKING_OPTIONAL); //required service -> should stop, but fails at stop and should become inactive (component will disable itself) - cmp.createServiceDependency<TestService>().setRequired(true).build(); - cmp.wait(); - EXPECT_EQ(cmp.getState(), ComponentState::INACTIVE); + dmCmp.createServiceDependency<TestService>().setRequired(true).build(); + dmCmp.wait(); + EXPECT_EQ(dmCmp.getState(), ComponentState::INACTIVE); dm.clear(); } { - auto& cmp = dm.createComponent(std::make_shared<ExceptionComponent>(ExceptionComponent::LifecycleMethod::DEINIT), "FailAtDeinit") + auto& dmCmp = dm.createComponent(std::make_shared<ExceptionComponent>(ExceptionComponent::LifecycleMethod::DEINIT), "FailAtDeinit") .setCallbacks(&ExceptionComponent::init, &ExceptionComponent::start, &ExceptionComponent::stop, &ExceptionComponent::deinit); - EXPECT_EQ(cmp.getState(), ComponentState::INACTIVE); - cmp.build(); - EXPECT_EQ(cmp.getState(), ComponentState::TRACKING_OPTIONAL); + EXPECT_EQ(dmCmp.getState(), ComponentState::INACTIVE); + dmCmp.build(); + EXPECT_EQ(dmCmp.getState(), ComponentState::TRACKING_OPTIONAL); //required service -> should stop, but fails at stop and should become inactive (component will disable itself) dm.clear(); //dm clear will deinit component and this should fail, but not deadlock @@ -906,31 +973,31 @@ TEST_F(DependencyManagerTestSuite, installBundleWithDepManActivator) { TEST_F(DependencyManagerTestSuite, testStateToString) { const char* state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_INACTIVE); - EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_INACTIVE"); + EXPECT_STREQ(state, "INACTIVE"); state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED); - EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED"); + EXPECT_STREQ(state, "WAITING_FOR_REQUIRED"); state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_INITIALIZING); - EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_INITIALIZING"); + EXPECT_STREQ(state, "INITIALIZING"); state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_DEINITIALIZING); - EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_DEINITIALIZING"); + EXPECT_STREQ(state, "DEINITIALIZING"); state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED); - EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED"); + EXPECT_STREQ(state, "INITIALIZED_AND_WAITING_FOR_REQUIRED"); state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_STARTING); - EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_STARTING"); + EXPECT_STREQ(state, "STARTING"); state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_STOPPING); - EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_STOPPING"); + EXPECT_STREQ(state, "STOPPING"); state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_TRACKING_OPTIONAL); - EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_TRACKING_OPTIONAL"); + EXPECT_STREQ(state, "TRACKING_OPTIONAL"); state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_SUSPENDING); - EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_SUSPENDING"); + EXPECT_STREQ(state, "SUSPENDING"); state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_SUSPENDED); - EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_SUSPENDED"); + EXPECT_STREQ(state, "SUSPENDED"); state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_RESUMING); - EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_RESUMING"); + EXPECT_STREQ(state, "RESUMING"); state = celix_dmComponent_stateToString((celix_dm_component_state_enum)0); - EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_INACTIVE"); + EXPECT_STREQ(state, "INACTIVE"); state = celix_dmComponent_stateToString((celix_dm_component_state_enum)16); - EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_INACTIVE"); + EXPECT_STREQ(state, "INACTIVE"); } #if __cplusplus >= 201703L //C++17 or higher @@ -944,10 +1011,10 @@ public: TEST_F(DependencyManagerTestSuite, ProvideInterfaceWithStaticInfo) { class TestComponent : public TestInterfaceWithStaticInfo {}; celix::dm::DependencyManager dm{ctx}; - auto& cmp = dm.createComponent<TestComponent>(); - cmp.createProvidedService<TestInterfaceWithStaticInfo>(); - cmp.build(); - EXPECT_EQ(cmp.getState(), celix::dm::ComponentState::TRACKING_OPTIONAL); + auto& dmCmp = dm.createComponent<TestComponent>(); + dmCmp.createProvidedService<TestInterfaceWithStaticInfo>(); + dmCmp.build(); + EXPECT_EQ(dmCmp.getState(), celix::dm::ComponentState::TRACKING_OPTIONAL); auto info = dm.getInfo(); EXPECT_EQ(info.components[0].interfacesInfo[0].serviceName, "TestName"); @@ -960,10 +1027,10 @@ TEST_F(DependencyManagerTestSuite, ProvideInterfaceWithStaticInfo) { TEST_F(DependencyManagerTestSuite, CreateInterfaceWithStaticInfo) { class TestComponent : public TestInterfaceWithStaticInfo {}; celix::dm::DependencyManager dm{ctx}; - auto& cmp = dm.createComponent<TestComponent>(); - cmp.addInterface<TestInterfaceWithStaticInfo>(); - cmp.build(); - EXPECT_EQ(cmp.getState(), celix::dm::ComponentState::TRACKING_OPTIONAL); + auto& dmCmp = dm.createComponent<TestComponent>(); + dmCmp.addInterface<TestInterfaceWithStaticInfo>(); + dmCmp.build(); + EXPECT_EQ(dmCmp.getState(), celix::dm::ComponentState::TRACKING_OPTIONAL); auto info = dm.getInfo(); EXPECT_EQ(info.components[0].interfacesInfo[0].serviceName, "TestName"); diff --git a/libs/framework/src/dm_component_impl.c b/libs/framework/src/dm_component_impl.c index ca213435..eba1e022 100644 --- a/libs/framework/src/dm_component_impl.c +++ b/libs/framework/src/dm_component_impl.c @@ -34,7 +34,6 @@ struct celix_dm_component_struct { char name[DM_COMPONENT_MAX_NAME_LENGTH]; celix_bundle_context_t* context; - bool setCLanguageProperty; void* implementation; celix_dm_cmp_lifecycle_fpt callbackInit; @@ -42,6 +41,8 @@ struct celix_dm_component_struct { celix_dm_cmp_lifecycle_fpt callbackStop; celix_dm_cmp_lifecycle_fpt callbackDeinit; + celix_dm_component_state_t state; //atomic + celix_thread_mutex_t mutex; //protects below celix_array_list_t* providedInterfaces; //type = dm_interface_t* celix_array_list_t* dependencies; //type = celix_dm_service_dependency_t* @@ -51,8 +52,6 @@ struct celix_dm_component_struct { */ celix_array_list_t* removedDependencies; //type = celix_dm_service_dependency_t* - celix_dm_component_state_t state; - size_t nrOfTimesStarted; size_t nrOfTimesResumed; @@ -90,7 +89,8 @@ static celix_status_t celix_dmComponent_disableDependencies(celix_dm_component_t static bool celix_dmComponent_isDisabled(celix_dm_component_t *component); static void celix_dmComponent_cleanupRemovedDependencies(celix_dm_component_t* component); static bool celix_dmComponent_isActiveInternal(celix_dm_component_t *component); - +static void celix_dmComponent_setCurrentState(celix_dm_component_t* cmp, celix_dm_component_state_t s); +static void celix_dmComponent_logTransition(celix_dm_component_t* cmp, celix_dm_component_state_t currentState, celix_dm_component_state_t desiredState); celix_dm_component_t* celix_dmComponent_create(bundle_context_t *context, const char* name) { return celix_dmComponent_createWithUUID(context, name, NULL); @@ -131,13 +131,13 @@ celix_dm_component_t* celix_dmComponent_createWithUUID(bundle_context_t *context component->callbackStop = NULL; component->callbackDeinit = NULL; component->state = CELIX_DM_CMP_STATE_INACTIVE; - component->setCLanguageProperty = false; component->providedInterfaces = celix_arrayList_create(); component->dependencies = celix_arrayList_create(); component->removedDependencies = celix_arrayList_create(); celixThreadMutex_create(&component->mutex, NULL); component->isEnabled = false; + component->inTransition = false; return component; } @@ -258,7 +258,7 @@ celix_status_t celix_dmComponent_addServiceDependency(celix_dm_component_t *comp celixThreadMutex_lock(&component->mutex); arrayList_add(component->dependencies, dep); - bool startDep = component->state != CELIX_DM_CMP_STATE_INACTIVE; + bool startDep = celix_dmComponent_currentState(component) != CELIX_DM_CMP_STATE_INACTIVE; if (startDep) { celix_dmServiceDependency_enable(dep); } @@ -273,7 +273,7 @@ celix_status_t celix_dmComponent_removeServiceDependency(celix_dm_component_t *c celixThreadMutex_lock(&component->mutex); celix_arrayList_remove(component->dependencies, dep); - bool disableDependency = component->state != CELIX_DM_CMP_STATE_INACTIVE; + bool disableDependency = celix_dmComponent_currentState(component) != CELIX_DM_CMP_STATE_INACTIVE; if (disableDependency) { celix_dmServiceDependency_disable(dep); } @@ -289,12 +289,13 @@ celix_dm_component_state_t component_currentState(celix_dm_component_t *cmp) { return celix_dmComponent_currentState(cmp); } +static void celix_dmComponent_setCurrentState(celix_dm_component_t* cmp, celix_dm_component_state_t s) { + __atomic_store_n(&cmp->state, s, __ATOMIC_RELEASE); +} + celix_dm_component_state_t celix_dmComponent_currentState(celix_dm_component_t *cmp) { - celix_dm_component_state_t state; - celixThreadMutex_lock(&cmp->mutex); - state = cmp->state; - celixThreadMutex_unlock(&cmp->mutex); - return state; + celix_dm_component_state_t state = __atomic_load_n(&cmp->state, __ATOMIC_ACQUIRE); + return state; } void* component_getImplementation(celix_dm_component_t *cmp) { @@ -355,7 +356,7 @@ static celix_status_t celix_dmComponent_disable(celix_dm_component_t *component) */ static void celix_dmComponent_disableDirectly(celix_dm_component_t *component) { component->isEnabled = false; - component->state = CELIX_DM_CMP_STATE_INACTIVE; + celix_dmComponent_setCurrentState(component, CELIX_DM_CMP_STATE_INACTIVE); celix_dmComponent_unregisterServices(component, false); celix_dmComponent_disableDependencies(component); } @@ -400,7 +401,7 @@ static bool celix_dmComponent_isDisabled(celix_dm_component_t *component) { celixThreadMutex_lock(&component->mutex); isStopped = !component->isEnabled && - component->state == CELIX_DM_CMP_STATE_INACTIVE && + celix_dmComponent_currentState(component) == CELIX_DM_CMP_STATE_INACTIVE && celix_dmComponent_areAllDependenciesDisabled(component); celixThreadMutex_unlock(&component->mutex); return isStopped; @@ -443,7 +444,7 @@ celix_status_t celix_dmComponent_addInterface(celix_dm_component_t *component, c interface->properties = properties; interface->svcId= -1L; celix_arrayList_add(component->providedInterfaces, interface); - if (component->state == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL) { + if (celix_dmComponent_currentState(component) == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL) { celix_dmComponent_registerServices(component, false); } celixThreadMutex_unlock(&component->mutex); @@ -529,19 +530,15 @@ static celix_status_t celix_dmComponent_suspend(celix_dm_component_t *component, celix_status_t status = CELIX_SUCCESS; if (component->callbackStop != NULL) { celixThreadMutex_lock(&component->mutex); - component->state = CELIX_DM_CMP_STATE_SUSPENDING; - celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_TRACE, - "Suspending component %s (uuid=%s)", - component->name, - component->uuid); + celix_dmComponent_logTransition(component, celix_dmComponent_currentState(component), + CELIX_DM_CMP_STATE_SUSPENDING); + celix_dmComponent_setCurrentState(component, CELIX_DM_CMP_STATE_SUSPENDING); celix_dmComponent_unregisterServices(component, false); status = component->callbackStop(component->implementation); if (status == CELIX_SUCCESS) { - component->state = CELIX_DM_CMP_STATE_SUSPENDED; - celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_TRACE, - "Suspended component %s (uuid=%s)", - component->name, - component->uuid); + celix_dmComponent_logTransition(component, celix_dmComponent_currentState(component), + CELIX_DM_CMP_STATE_SUSPENDED); + celix_dmComponent_setCurrentState(component, CELIX_DM_CMP_STATE_SUSPENDED); } else { celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_ERROR, "Error stopping component %s (uuid=%s) using the stop callback. Disabling component.", @@ -557,21 +554,17 @@ static celix_status_t celix_dmComponent_suspend(celix_dm_component_t *component, static celix_status_t celix_dmComponent_resume(celix_dm_component_t *component, celix_dm_service_dependency_t *dependency) { celix_status_t status = CELIX_SUCCESS; celixThreadMutex_lock(&component->mutex); - if (component->state == CELIX_DM_CMP_STATE_SUSPENDED) { - component->state = CELIX_DM_CMP_STATE_RESUMING; - celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_TRACE, - "Resuming component %s (uuid=%s)", - component->name, - component->uuid); + if (celix_dmComponent_currentState(component) == CELIX_DM_CMP_STATE_SUSPENDED) { + celix_dmComponent_logTransition(component, celix_dmComponent_currentState(component), + CELIX_DM_CMP_STATE_RESUMING); + celix_dmComponent_setCurrentState(component, CELIX_DM_CMP_STATE_RESUMING); status = component->callbackStart(component->implementation); if (status == CELIX_SUCCESS) { celix_dmComponent_registerServices(component, false); component->nrOfTimesResumed += 1; - component->state = CELIX_DM_CMP_STATE_TRACKING_OPTIONAL; - celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_TRACE, - "Resumed component %s (uuid=%s)", - component->name, - component->uuid); + celix_dmComponent_logTransition(component, celix_dmComponent_currentState(component), + CELIX_DM_CMP_STATE_TRACKING_OPTIONAL); + celix_dmComponent_setCurrentState(component, CELIX_DM_CMP_STATE_TRACKING_OPTIONAL); } else { celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_ERROR, "Error starting component %s (uuid=%s) using the start callback. Disabling component.", @@ -587,26 +580,15 @@ static celix_status_t celix_dmComponent_resume(celix_dm_component_t *component, } static bool celix_dmComponent_needsSuspend(celix_dm_component_t *component, const celix_dm_event_t* event) { - bool cmpActive = false; - celixThreadMutex_lock(&component->mutex); - switch (component->state) { - case CELIX_DM_CMP_STATE_TRACKING_OPTIONAL: - cmpActive = true; - break; - default: //DM_CMP_STATE_INACTIVE - break; - } - celixThreadMutex_unlock(&component->mutex); - if (cmpActive) { + if (celix_dmComponent_currentState(component) == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL) { bool callbackConfigured = event->eventType == CELIX_DM_EVENT_SVC_SET ? celix_dmServiceDependency_isSetCallbackConfigured(event->dep) : /*add or rem*/ celix_dmServiceDependency_isAddRemCallbacksConfigured(event->dep); dm_service_dependency_strategy_t strategy = celix_dmServiceDependency_getStrategy(event->dep); bool suspendStrategy = strategy == DM_SERVICE_DEPENDENCY_STRATEGY_SUSPEND; return suspendStrategy && callbackConfigured; - } else { - return false; } + return false; } static celix_status_t celix_dmComponent_handleEvent(celix_dm_component_t *component, const celix_dm_event_t* event, celix_status_t (*setAddOrRemFp)(celix_dm_service_dependency_t *dependency, void* svc, const celix_properties_t* props), const char *invokeName) { @@ -706,12 +688,12 @@ static void celix_dmComponent_handleChangeOnEventThread(void *data) { celix_dm_component_state_t newState; bool transition = false; do { - oldState = component->state; + oldState = celix_dmComponent_currentState(component); celix_status_t status = celix_dmComponent_calculateNewState(component, oldState, &newState); if (status == CELIX_SUCCESS) { transition = celix_dmComponent_performTransition(component, oldState, newState); if (transition) { - component->state = newState; + celix_dmComponent_setCurrentState(component, newState); } } @@ -808,13 +790,7 @@ static bool celix_dmComponent_performTransition(celix_dm_component_t *component, } component->inTransition = true; - celix_bundleContext_log(component->context, - CELIX_LOG_LEVEL_TRACE, - "performing transition for component '%s' (uuid=%s) from state %s to state %s", - component->name, - component->uuid, - celix_dmComponent_stateToString(currentState), - celix_dmComponent_stateToString(desiredState)); + celix_dmComponent_logTransition(component, currentState, desiredState); celix_status_t status = CELIX_SUCCESS; if (currentState == CELIX_DM_CMP_STATE_INACTIVE && desiredState == CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED) { @@ -1025,7 +1001,8 @@ celix_status_t celix_dmComponent_getComponentInfo(celix_dm_component_t *componen memcpy(info->name, component->name, DM_COMPONENT_MAX_NAME_LENGTH); info->nrOfTimesStarted = component->nrOfTimesStarted; info->nrOfTimesResumed = component->nrOfTimesResumed; - info->state = celix_utils_strdup(celix_dmComponent_stateToString(component->state)); + celix_dm_component_state_t state = celix_dmComponent_currentState(component); + info->state = celix_utils_strdup(celix_dmComponent_stateToString(state)); info->active = celix_dmComponent_isActiveInternal(component); for (int i = 0; i < celix_arrayList_size(component->dependencies); i += 1) { @@ -1073,19 +1050,18 @@ void celix_dmComponent_destroyComponentInfo(dm_component_info_pt info) { static bool celix_dmComponent_isActiveInternal(celix_dm_component_t *component) { //precondition: mutex component->mutex taken. + celix_dm_component_state_t state = celix_dmComponent_currentState(component); return - component->state == CELIX_DM_CMP_STATE_STARTING || - component->state == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL || - component->state == CELIX_DM_CMP_STATE_SUSPENDING || - component->state == CELIX_DM_CMP_STATE_SUSPENDED || - component->state == CELIX_DM_CMP_STATE_RESUMING || - component->state == CELIX_DM_CMP_STATE_STOPPING; + state == CELIX_DM_CMP_STATE_STARTING || + state == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL || + state == CELIX_DM_CMP_STATE_SUSPENDING || + state == CELIX_DM_CMP_STATE_SUSPENDED || + state == CELIX_DM_CMP_STATE_RESUMING || + state == CELIX_DM_CMP_STATE_STOPPING; } bool celix_dmComponent_isActive(celix_dm_component_t *component) { - celixThreadMutex_lock(&component->mutex); bool isActivate = celix_dmComponent_isActiveInternal(component); - celixThreadMutex_unlock(&component->mutex); return isActivate; } @@ -1115,3 +1091,14 @@ const char* celix_dmComponent_stateToString(celix_dm_component_state_t state) { return "INACTIVE"; } } + + +static void celix_dmComponent_logTransition(celix_dm_component_t* cmp, celix_dm_component_state_t currentState, celix_dm_component_state_t desiredState) { + celix_bundleContext_log(cmp->context, + CELIX_LOG_LEVEL_TRACE, + "Component transition from %s -> %s for '%s' (uuid=%s).", + celix_dmComponent_stateToString(currentState), + celix_dmComponent_stateToString(desiredState), + cmp->name, + cmp->uuid); +} \ No newline at end of file
