You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pn...@apache.org on 2021/03/18 18:28:37 UTC

[celix] 01/01: Adds some missing sync calls.

This is an automated email from the ASF dual-hosted git repository.

pnoltes pushed a commit to branch feature/missing_async_calls
in repository https://gitbox.apache.org/repos/asf/celix.git

commit 10b0aeaa7554fc8108bcc678530adc4f40b4d00c
Author: Pepijn Noltes <pe...@gmail.com>
AuthorDate: Thu Mar 18 19:28:21 2021 +0100

    Adds some missing sync calls.
    
    Also adds the option to add a component context object.
---
 .../gtest/src/CxxBundleContextTestSuite.cc         | 22 ++++++++++
 .../gtest/src/DependencyManagerTestSuite.cc        | 11 ++++-
 libs/framework/include/celix/BundleActivator.h     |  2 +-
 libs/framework/include/celix/BundleContext.h       | 48 ++++++++++++++++++----
 libs/framework/include/celix/FrameworkFactory.h    |  5 ++-
 libs/framework/include/celix/dm/Component.h        |  9 ++++
 libs/framework/include/celix/dm/Component_Impl.h   |  7 +++-
 .../framework/include/celix/dm/DependencyManager.h | 41 ++++++++++++++++--
 .../include/celix/dm/DependencyManager_Impl.h      | 18 ++++++++
 libs/framework/include/celix_bundle_context.h      |  2 +-
 10 files changed, 148 insertions(+), 17 deletions(-)

diff --git a/libs/framework/gtest/src/CxxBundleContextTestSuite.cc b/libs/framework/gtest/src/CxxBundleContextTestSuite.cc
index 8aa0658..819729f 100644
--- a/libs/framework/gtest/src/CxxBundleContextTestSuite.cc
+++ b/libs/framework/gtest/src/CxxBundleContextTestSuite.cc
@@ -78,6 +78,7 @@ TEST_F(CxxBundleContextTestSuite, RegisterServiceTest) {
     }
 
     ctx->waitForEvents();
+    ctx->waitIfAbleForEvents();
     svcId = ctx->findService<TestInterface>();
     EXPECT_EQ(svcId, -1L);
 }
@@ -359,10 +360,13 @@ TEST_F(CxxBundleContextTestSuite, OnRegisterAndUnregisterCallbacks) {
 }
 
 TEST_F(CxxBundleContextTestSuite, InstallCxxBundle) {
+    EXPECT_EQ(0, ctx->listBundleIds().size());
+
     std::string loc{SIMPLE_CXX_BUNDLE_LOC};
     ASSERT_FALSE(loc.empty());
     long bndId = ctx->installBundle(loc);
     EXPECT_GE(bndId, 0);
+    EXPECT_EQ(1, ctx->listBundleIds().size());
 }
 
 TEST_F(CxxBundleContextTestSuite, LoggingUsingContext) {
@@ -561,3 +565,21 @@ TEST_F(CxxBundleContextTestSuite, setServicesWithTrackerWhenMultipleRegistration
     //Also look into why this is not happening in the C service tracker test.
     EXPECT_EQ(3, count.load());
 }
+
+TEST_F(CxxBundleContextTestSuite, WaitForAllEvents) {
+    long svcId = ctx->findService<TestInterface>();
+    EXPECT_EQ(svcId, -1L);
+
+    {
+        auto impl = std::make_shared<TestImplementation>();
+        auto svcReg = ctx->registerService<TestInterface>(impl).build();
+        svcReg->wait();
+        svcId = ctx->findService<TestInterface>();
+        EXPECT_GE(svcId, 0L);
+    }
+
+    ctx->waitForAllEvents();
+    ctx->waitIfAbleForAllEvents();
+    svcId = ctx->findService<TestInterface>();
+    EXPECT_EQ(svcId, -1L);
+}
\ No newline at end of file
diff --git a/libs/framework/gtest/src/DependencyManagerTestSuite.cc b/libs/framework/gtest/src/DependencyManagerTestSuite.cc
index 31152f0..643e8b4 100644
--- a/libs/framework/gtest/src/DependencyManagerTestSuite.cc
+++ b/libs/framework/gtest/src/DependencyManagerTestSuite.cc
@@ -299,6 +299,7 @@ TEST_F(DependencyManagerTestSuite, AddSvcProvideAfterBuild) {
 
     dm.clear();
     dm.wait();
+    dm.waitIfAble();
     EXPECT_EQ(0, dm.getNrOfComponents()); //dm cleared so no components
     svcId = celix_bundleContext_findService(ctx, "TestService");
     EXPECT_EQ(svcId, -1); //cleared -> not found
@@ -418,7 +419,7 @@ TEST_F(DependencyManagerTestSuite, RemoveAndClear) {
     dm.destroyComponent(cmp1);
     bool removed = dm.removeComponent(cmp2.getUUID());
     EXPECT_TRUE(removed);
-    removed = dm.removeComponent(cmp4.getUUID());
+    removed = dm.removeComponentAsync(cmp4.getUUID());
     EXPECT_TRUE(removed);
     dm.clear();
 }
@@ -638,4 +639,12 @@ TEST_F(DependencyManagerTestSuite, ExceptionsInLifecycle) {
         //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
     }
+}
+
+TEST_F(DependencyManagerTestSuite, ComponentContext) {
+    celix::dm::DependencyManager dm{ctx};
+    dm.createComponent<TestComponent>()
+            .addContext(std::make_shared<std::vector<std::string>>())
+            .build();
+    //note should not leak mem
 }
\ No newline at end of file
diff --git a/libs/framework/include/celix/BundleActivator.h b/libs/framework/include/celix/BundleActivator.h
index bc3e845..5ef2336 100644
--- a/libs/framework/include/celix/BundleActivator.h
+++ b/libs/framework/include/celix/BundleActivator.h
@@ -85,8 +85,8 @@ namespace celix {
             auto *data = static_cast<BundleActivatorData<I> *>(userData);
             std::weak_ptr<celix::BundleContext> ctx = data->ctx;
             std::weak_ptr<celix::dm::DependencyManager> dm = data->dm;
-            data->bundleActivator = nullptr;
             data->dm->clear();
+            data->bundleActivator = nullptr;
             data->dm = nullptr;
             data->ctx = nullptr;
             waitForExpired(data->bndId, ctx, "celix::BundleContext", ctx);
diff --git a/libs/framework/include/celix/BundleContext.h b/libs/framework/include/celix/BundleContext.h
index 3e676d4..2c58982 100644
--- a/libs/framework/include/celix/BundleContext.h
+++ b/libs/framework/include/celix/BundleContext.h
@@ -188,7 +188,7 @@ namespace celix {
          */
         template<typename I>
         long findServiceWithName(const std::string& name, const std::string& filter = {}, const std::string& versionRange = {}) {
-            waitIfAble();
+            waitIfAbleForEvents();
             celix_service_filter_options_t opts{};
             opts.serviceName = name.empty() ? nullptr : name.c_str();
             opts.filter = filter.empty() ? nullptr : filter.c_str();
@@ -224,7 +224,7 @@ namespace celix {
          */
         template<typename I>
         std::vector<long> findServicesWithName(const std::string& name, const std::string& filter = {}, const std::string& versionRange = {}) {
-            waitIfAble();
+            waitIfAbleForEvents();
             celix_service_filter_options_t opts{};
             opts.serviceName = name.empty() ? nullptr : name.c_str();
             opts.filter = filter.empty() ? nullptr : filter.c_str();
@@ -378,6 +378,21 @@ namespace celix {
         }
 
         /**
+         * @brief List the installed and started bundle ids.
+         * The bundle ids does not include the framework bundle (bundle id celix::FRAMEWORK_BUNDLE_ID).
+         */
+        std::vector<long> listBundleIds() const {
+            std::vector<long> result{};
+            auto* ids = celix_bundleContext_listBundles(cCtx.get());
+            result.reserve(celix_arrayList_size(ids));
+            for (int i = 0 ; i < celix_arrayList_size(ids); ++i) {
+                result.push_back(celix_arrayList_getLong(ids, i));
+            }
+            celix_arrayList_destroy(ids);
+            return result;
+        }
+
+        /**
          * @brief Gets the config property for the provided name.
          *
          * First the provided name will be used to lookup an environment variable of that name. If this is not found,
@@ -555,22 +570,41 @@ namespace celix {
         }
 
         /**
-         * @brief Wait until all Celix event for this bundle are completed.
+         * @brief Wait until all Celix events for this bundle are completed.
          */
         void waitForEvents() const {
             celix_bundleContext_waitForEvents(cCtx.get());
         }
-    private:
+
         /**
-         * @brief Wait (if not on the Celix event thread) for all events for this bundle context to be finished.
+         * @brief Wait (if not on the Celix event thread) until all Celix events for this bundle are completed.
          */
-        void waitIfAble() const {
+        void waitIfAbleForEvents() const {
             auto* fw = celix_bundleContext_getFramework(cCtx.get());
-            if (celix_framework_isCurrentThreadTheEventLoop(fw)) {
+            if (!celix_framework_isCurrentThreadTheEventLoop(fw)) {
                 celix_bundleContext_waitForEvents(cCtx.get());
             }
         }
 
+        /**
+         * @brief Wait until all Celix events (for all bundles) are completed.
+         */
+        void waitForAllEvents() const {
+            auto* fw = celix_bundleContext_getFramework(cCtx.get());
+            celix_framework_waitForEmptyEventQueue(fw);
+        }
+
+        /**
+         * @brief Wait (if not on the Celix event thread) until all Celix events (for all bundles) are completed.
+         */
+         void waitIfAbleForAllEvents() const {
+            auto* fw = celix_bundleContext_getFramework(cCtx.get());
+            if (!celix_framework_isCurrentThreadTheEventLoop(fw)) {
+                celix_framework_waitForEmptyEventQueue(fw);
+            }
+         }
+    private:
+
         const std::shared_ptr<celix_bundle_context_t> cCtx;
         const std::shared_ptr<celix::dm::DependencyManager> dm;
         const Bundle bnd;
diff --git a/libs/framework/include/celix/FrameworkFactory.h b/libs/framework/include/celix/FrameworkFactory.h
index ab4bd69..ad5effc 100644
--- a/libs/framework/include/celix/FrameworkFactory.h
+++ b/libs/framework/include/celix/FrameworkFactory.h
@@ -35,9 +35,10 @@ namespace celix {
         auto* cFw= celix_frameworkFactory_createFramework(copy);
         auto fwCtx = std::make_shared<celix::BundleContext>(celix_framework_getFrameworkContext(cFw));
         std::shared_ptr<celix::Framework> framework{new celix::Framework{std::move(fwCtx), cFw}, [](celix::Framework* fw) {
-            celix_framework_waitForEmptyEventQueue(fw->getCFramework());
-            celix_frameworkFactory_destroyFramework(fw->getCFramework());
+            auto* cFw = fw->getCFramework();
             delete fw;
+            celix_framework_waitForEmptyEventQueue(cFw);
+            celix_frameworkFactory_destroyFramework(cFw);
         }};
         return framework;
     }
diff --git a/libs/framework/include/celix/dm/Component.h b/libs/framework/include/celix/dm/Component.h
index 4aa301a..6c9004d 100644
--- a/libs/framework/include/celix/dm/Component.h
+++ b/libs/framework/include/celix/dm/Component.h
@@ -128,6 +128,7 @@ namespace celix { namespace dm {
         std::mutex mutex{}; //protects below
         std::vector<std::shared_ptr<BaseServiceDependency>> dependencies{};
         std::vector<std::shared_ptr<BaseProvidedService>> providedServices{};
+        std::vector<std::shared_ptr<void>> componentContexts{};
     };
 
 
@@ -327,6 +328,7 @@ namespace celix { namespace dm {
                 int (T::*stop)(),
                 int (T::*deinit)()
         );
+
         /**
          * Remove the previously registered callbacks for the component life cycle control
          *
@@ -334,6 +336,13 @@ namespace celix { namespace dm {
          */
         Component<T>& removeCallbacks();
 
+
+        /**
+         * @brief Add context to the component. This can be used to ensure a object lifespan at least
+         * matches that of the component.
+         */
+        Component<T>& addContext(std::shared_ptr<void>);
+
         /**
          * Build the component.
          *
diff --git a/libs/framework/include/celix/dm/Component_Impl.h b/libs/framework/include/celix/dm/Component_Impl.h
index 6d7bebb..da3ff5d 100644
--- a/libs/framework/include/celix/dm/Component_Impl.h
+++ b/libs/framework/include/celix/dm/Component_Impl.h
@@ -359,9 +359,14 @@ Component<T>& Component<T>::setCallbacks(
 
 template<class T>
 Component<T>& Component<T>::removeCallbacks() {
-
     celix_dmComponent_setCallbacks(this->cComponent(), nullptr, nullptr, nullptr, nullptr);
+    return *this;
+}
 
+template<class T>
+Component<T>& Component<T>::addContext(std::shared_ptr<void> context) {
+    std::lock_guard<std::mutex> lock{mutex};
+    componentContexts.template emplace_back(std::move(context));
     return *this;
 }
 
diff --git a/libs/framework/include/celix/dm/DependencyManager.h b/libs/framework/include/celix/dm/DependencyManager.h
index 05a672c..a7d9c93 100644
--- a/libs/framework/include/celix/dm/DependencyManager.h
+++ b/libs/framework/include/celix/dm/DependencyManager.h
@@ -127,7 +127,8 @@ namespace celix { namespace dm {
         void start();
 
         /**
-         * Wait for an empty Celix event queue.
+         * @brief Wait for an empty Celix event queue.
+         *
          * Should not be called on the Celix event queue thread.
          *
          * Can be used to ensure that all created/updated components are completely processed (services registered
@@ -136,17 +137,37 @@ namespace celix { namespace dm {
         void wait() const;
 
         /**
-         * Removes a component from the  Dependency Manager and destroys it
+         * @brief Wait (if not called on the Celix event thread) for an empty Celix event queue
+         *
+         * Can be used to ensure that all created/updated components are completely processed (services registered
+         * and/or service trackers are created).
+         */
+        void waitIfAble() const;
+
+        /**
+         * @brief Removes a component from the Dependency Manager and wait until the component is destroyed.
+         *
+         * The use of removeComponent is preferred.
          */
         template<typename T>
         void destroyComponent(Component<T> &component);
 
         /**
-         * Clears the dependency manager, which removes all configured components.
+         * @brief Clears the dependency manager, which removes all configured components and waits until all components
+         * are removed from the dependency manager.
+         *
+         * Should not be called from the Celix event thread.
          */
         void clear();
 
         /**
+         * Clears the dependency manager, which removes all configured components.
+         *
+         * This is done async and this can be called from the Celix event thread.
+         */
+        void clearAsync();
+
+        /**
          * Stops the Dependency Manager
          * Note this call is considered deprecated, please use clear() instead.
          */
@@ -166,12 +187,24 @@ namespace celix { namespace dm {
         std::shared_ptr<Component<T>> findComponent(const std::string& uuid) const;
 
         /**
-         * Removes component with provided UUID from the dependency manager.
+         * @brief Removes component with provided UUID from the dependency manager and wail until the component is destroyed
+         *
+         * Should not be called from the Celix event thread.
+         *
          * @return whether the component is found and removed.
          */
         bool removeComponent(const std::string& uuid);
 
         /**
+         * @brief Removes a component from the Dependency Manager and destroys it async.
+         *
+         * Can be called from the Celix event thread.
+         *
+         * @return whether the component is found and removed.
+         */
+        bool removeComponentAsync(const std::string& uuid);
+
+        /**
          * Get Dependency Management info for this component manager.
          * @return A DependencyManagerInfo struct.
          */
diff --git a/libs/framework/include/celix/dm/DependencyManager_Impl.h b/libs/framework/include/celix/dm/DependencyManager_Impl.h
index d3ab158..e982f34 100644
--- a/libs/framework/include/celix/dm/DependencyManager_Impl.h
+++ b/libs/framework/include/celix/dm/DependencyManager_Impl.h
@@ -86,6 +86,12 @@ void DependencyManager::destroyComponent(Component<T> &component) {
 }
 
 inline bool DependencyManager::removeComponent(const std::string& uuid) {
+    bool removed = removeComponentAsync(uuid);
+    wait();
+    return removed;
+}
+
+inline bool DependencyManager::removeComponentAsync(const std::string& uuid) {
     std::shared_ptr<BaseComponent> tmpStore{};
     {
         std::lock_guard<std::mutex> lck{mutex};
@@ -102,6 +108,11 @@ inline bool DependencyManager::removeComponent(const std::string& uuid) {
 }
 
 inline void DependencyManager::clear() {
+    clearAsync();
+    wait();
+}
+
+inline void DependencyManager::clearAsync() {
     std::vector<std::shared_ptr<BaseComponent>> swappedComponents{};
     {
         std::lock_guard<std::mutex> lck{mutex};
@@ -114,6 +125,13 @@ inline void DependencyManager::wait() const {
     celix_dependencyManager_wait(cDepMan.get());
 }
 
+inline void DependencyManager::waitIfAble() const {
+    auto* fw = celix_bundleContext_getFramework(context.get());
+    if (!celix_framework_isCurrentThreadTheEventLoop(fw)) {
+        celix_dependencyManager_wait(cDepMan.get());
+    }
+}
+
 inline void DependencyManager::stop() {
     clear();
 }
diff --git a/libs/framework/include/celix_bundle_context.h b/libs/framework/include/celix_bundle_context.h
index 18aca93..c6836a5 100644
--- a/libs/framework/include/celix_bundle_context.h
+++ b/libs/framework/include/celix_bundle_context.h
@@ -797,7 +797,7 @@ size_t celix_bundleContext_useServicesWithOptions(
 
 /**
  * List the installed and started bundle ids.
- * The bundle ids does not include the framework bundle (bundle id 0).
+ * The bundle ids does not include the framework bundle (bundle id CELIX_FRAMEWORK_BUNDLE_ID).
  *
  * @param ctx The bundle context
  * @return A array with bundle ids (long). The caller is responsible for destroying the array.