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:36 UTC

[celix] branch feature/missing_async_calls created (now 10b0aea)

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

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


      at 10b0aea  Adds some missing sync calls.

This branch includes the following new commits:

     new 10b0aea  Adds some missing sync calls.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


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

Posted by pn...@apache.org.
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.