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.