This is an automated email from the ASF dual-hosted git repository. pnoltes pushed a commit to branch feature/async_update in repository https://gitbox.apache.org/repos/asf/celix.git
commit e4da77173539f368b400da2bf23870ced3e715f6 Author: Pepijn Noltes <[email protected]> AuthorDate: Fri Apr 9 10:52:52 2021 +0200 Adds setCallbacks functions with shared pointer support to the celix::dm::ServiceDependency class. --- .../gtest/src/CxxBundleContextTestSuite.cc | 44 ++++- .../gtest/src/DependencyManagerTestSuite.cc | 126 +++++++++++++ .../framework/include/celix/dm/ServiceDependency.h | 131 ++++++++++++-- .../include/celix/dm/ServiceDependency_Impl.h | 194 +++++++++++++++++++-- 4 files changed, 458 insertions(+), 37 deletions(-) diff --git a/libs/framework/gtest/src/CxxBundleContextTestSuite.cc b/libs/framework/gtest/src/CxxBundleContextTestSuite.cc index f77beed..ce019aa 100644 --- a/libs/framework/gtest/src/CxxBundleContextTestSuite.cc +++ b/libs/framework/gtest/src/CxxBundleContextTestSuite.cc @@ -238,21 +238,51 @@ TEST_F(CxxBundleContextTestSuite, TrackServicesTest) { std::atomic<int> count{0}; auto tracker4 = ctx->trackServices<CInterface>() - .addAddCallback([&count](const std::shared_ptr<CInterface>&) { + .addAddCallback([&count](const std::shared_ptr<CInterface>& svc) { + EXPECT_TRUE(svc); count += 1; }) - .addRemCallback([&count](const std::shared_ptr<CInterface>&) { + .addRemCallback([&count](const std::shared_ptr<CInterface>& svc) { + EXPECT_TRUE(svc); count += 1; }) .build(); - tracker4->wait(); - EXPECT_EQ(2, count); //2x add called + auto tracker5 = ctx->trackServices<CInterface>() + .addAddWithPropertiesCallback([&count](std::shared_ptr<CInterface> svc /*note not the default expect const std::sharer_ptr<I>&*/, const std::shared_ptr<const celix::Properties>& props) { + EXPECT_TRUE(svc); + EXPECT_TRUE(props); + count += 1; + }) + .addRemWithPropertiesCallback([&count](const std::shared_ptr<CInterface>& svc, const std::shared_ptr<const celix::Properties>& props) { + EXPECT_TRUE(svc); + EXPECT_TRUE(props); + count += 1; + }) + .build(); + auto tracker6 = ctx->trackServices<CInterface>() + .addAddWithOwnerCallback([&count](const std::shared_ptr<CInterface>& svc, std::shared_ptr<const celix::Properties> props /*note not the default expected const ref*/, std::shared_ptr<const celix::Bundle> bundle /*note not the default expected const ref*/) { + EXPECT_TRUE(svc); + EXPECT_TRUE(props); + EXPECT_TRUE(bundle); + count += 1; + }) + .addRemWithOwnerCallback([&count](const std::shared_ptr<CInterface>& svc, const std::shared_ptr<const celix::Properties>& props, const std::shared_ptr<const celix::Bundle>& bundle) { + EXPECT_TRUE(svc); + EXPECT_TRUE(props); + EXPECT_TRUE(bundle); + count += 1; + }) + .build(); + ctx->waitForEvents(); + EXPECT_EQ(6, count); //2x3 add called svcReg1->unregister(); svcReg1->wait(); - EXPECT_EQ(3, count); //2x add called, 1x rem called + EXPECT_EQ(9, count); //2x3 add called, 1x3 rem called tracker4->close(); - tracker4->wait(); - EXPECT_EQ(4, count); //2x add called, 2x rem called (1 rem call for closing the tracker) + tracker5->close(); + tracker6->close(); + ctx->waitForEvents(); + EXPECT_EQ(12, count); //2x3 add called, 2x3 rem called (1 rem call for closing the tracker) EXPECT_EQ(1, tracker->getServiceCount()); //only 1 left diff --git a/libs/framework/gtest/src/DependencyManagerTestSuite.cc b/libs/framework/gtest/src/DependencyManagerTestSuite.cc index 4bf1745..672a2f4 100644 --- a/libs/framework/gtest/src/DependencyManagerTestSuite.cc +++ b/libs/framework/gtest/src/DependencyManagerTestSuite.cc @@ -486,6 +486,132 @@ TEST_F(DependencyManagerTestSuite, RequiredDepsAreInjectedDuringStartStop) { celix_bundleContext_unregisterService(dm.bundleContext(), svcId); } +TEST_F(DependencyManagerTestSuite, DepsAreInjectedAsSharedPointers) { + class LifecycleComponent { + public: + void start() { + std::lock_guard<std::mutex> lck{mutex}; + EXPECT_TRUE(setSvc != nullptr); + EXPECT_EQ(services.size(), 1); + } + + void stop() { + std::lock_guard<std::mutex> lck{mutex}; + EXPECT_TRUE(setSvc != nullptr); + EXPECT_EQ(services.size(), 1); + } + + void setService(const std::shared_ptr<TestService>& svc, const std::shared_ptr<const celix::Properties>& /*props*/) { + std::lock_guard<std::mutex> lck{mutex}; + setSvc = svc; + } + + void addService(const std::shared_ptr<TestService>& svc, const std::shared_ptr<const celix::Properties>& props) { + EXPECT_TRUE(props); + std::lock_guard<std::mutex> lck{mutex}; + services.emplace_back(svc); + } + + void remService(const std::shared_ptr<TestService>& svc, const std::shared_ptr<const celix::Properties>& props) { + EXPECT_TRUE(props); + std::lock_guard<std::mutex> lck{mutex}; + for (auto it = services.begin(); it != services.end(); ++it) { + if (*it == svc) { + services.erase(it); + break; + } + } + } + private: + std::mutex mutex{}; + std::shared_ptr<TestService> setSvc{}; + std::vector<std::shared_ptr<TestService>> services{}; + }; + + celix::dm::DependencyManager dm{ctx}; + auto& cmp = dm.createComponent<LifecycleComponent>() + .setCallbacks(nullptr, &LifecycleComponent::start, &LifecycleComponent::stop, nullptr); + cmp.createServiceDependency<TestService>() + .setRequired(true) + .setCallbacks(&LifecycleComponent::setService) + .setCallbacks(&LifecycleComponent::addService, &LifecycleComponent::remService); + cmp.build(); + + TestService svc; + std::string svcName = celix::typeName<TestService>(); + celix_service_registration_options opts{}; + opts.svc = &svc; + opts.serviceName = svcName.c_str(); + opts.serviceLanguage = CELIX_FRAMEWORK_SERVICE_CXX_LANGUAGE; + long svcId = celix_bundleContext_registerServiceWithOptions(dm.bundleContext(), &opts); + EXPECT_GE(svcId, 0); + + EXPECT_EQ(cmp.getState(), ComponentState::TRACKING_OPTIONAL); + celix_bundleContext_unregisterService(dm.bundleContext(), svcId); +} + +TEST_F(DependencyManagerTestSuite, DepsNoPropsAreInjectedAsSharedPointers) { + class LifecycleComponent { + public: + void start() { + std::lock_guard<std::mutex> lck{mutex}; + EXPECT_TRUE(setSvc != nullptr); + EXPECT_EQ(services.size(), 1); + } + + void stop() { + std::lock_guard<std::mutex> lck{mutex}; + EXPECT_TRUE(setSvc != nullptr); + EXPECT_EQ(services.size(), 1); + } + + void setService(const std::shared_ptr<TestService>& svc) { + std::lock_guard<std::mutex> lck{mutex}; + setSvc = svc; + } + + void addService(const std::shared_ptr<TestService>& svc) { + std::lock_guard<std::mutex> lck{mutex}; + services.emplace_back(svc); + } + + void remService(const std::shared_ptr<TestService>& svc) { + std::lock_guard<std::mutex> lck{mutex}; + for (auto it = services.begin(); it != services.end(); ++it) { + if (*it == svc) { + services.erase(it); + break; + } + } + } + private: + std::mutex mutex{}; + std::shared_ptr<TestService> setSvc{}; + std::vector<std::shared_ptr<TestService>> services{}; + }; + + celix::dm::DependencyManager dm{ctx}; + auto& cmp = dm.createComponent<LifecycleComponent>() + .setCallbacks(nullptr, &LifecycleComponent::start, &LifecycleComponent::stop, nullptr); + cmp.createServiceDependency<TestService>() + .setRequired(true) + .setCallbacks(&LifecycleComponent::setService) + .setCallbacks(&LifecycleComponent::addService, &LifecycleComponent::remService); + cmp.build(); + + TestService svc; + std::string svcName = celix::typeName<TestService>(); + celix_service_registration_options opts{}; + opts.svc = &svc; + opts.serviceName = svcName.c_str(); + opts.serviceLanguage = CELIX_FRAMEWORK_SERVICE_CXX_LANGUAGE; + long svcId = celix_bundleContext_registerServiceWithOptions(dm.bundleContext(), &opts); + EXPECT_GE(svcId, 0); + + EXPECT_EQ(cmp.getState(), ComponentState::TRACKING_OPTIONAL); + celix_bundleContext_unregisterService(dm.bundleContext(), svcId); +} + TEST_F(DependencyManagerTestSuite, UnneededSuspendIsPrevented) { class CounterComponent { public: diff --git a/libs/framework/include/celix/dm/ServiceDependency.h b/libs/framework/include/celix/dm/ServiceDependency.h index eaa45a0..646969e 100644 --- a/libs/framework/include/celix/dm/ServiceDependency.h +++ b/libs/framework/include/celix/dm/ServiceDependency.h @@ -29,6 +29,7 @@ #include <atomic> #include <vector> #include <cstring> +#include <chrono> #include "dm_service_dependency.h" #include "celix_constants.h" @@ -45,8 +46,9 @@ namespace celix { namespace dm { class BaseServiceDependency { private: - celix_dm_component_t* cCmp; + const std::chrono::milliseconds warningTimoutForNonExpiredSvcObject{5000}; std::atomic<bool> depAddedToCmp{false}; + celix_dm_component_t* cCmp; protected: celix_dm_service_dependency_t *cServiceDep {nullptr}; @@ -57,6 +59,9 @@ namespace celix { namespace dm { celix_dmServiceDependency_setStrategy(this->cServiceDependency(), DM_SERVICE_DEPENDENCY_STRATEGY_SUSPEND); } } + + template<typename U> + void waitForExpired(std::weak_ptr<U> observe, long svcId, const char* observeType); public: BaseServiceDependency(celix_dm_component_t* c) : cCmp{c} { this->cServiceDep = celix_dmServiceDependency_create(); @@ -119,6 +124,12 @@ namespace celix { namespace dm { void setComponentInstance(T* cmp) { componentInstance = cmp;} }; + /** + * @brief A service dependency for a component. + * @tparam T The component type + * @tparam I The service interface type + * @warning CServiceDependency is considered deprecated use celix::dm::ServiceDependency instead. + */ template<class T, typename I> class CServiceDependency : public TypedServiceDependency<T> { using type = I; @@ -246,6 +257,11 @@ namespace celix { namespace dm { void setupService(); }; + /** + * @brief A service dependency for a component. + * @tparam T The component type + * @tparam I The service interface type + */ template<class T, class I> class ServiceDependency : public TypedServiceDependency<T> { using type = I; @@ -261,56 +277,84 @@ namespace celix { namespace dm { /** * Set the service name of the service dependency. * - * @return the C++ service dependency reference for chaining (fluent API) + * @return the service dependency reference for chaining (fluent API) */ ServiceDependency<T,I>& setName(const std::string &_name); /** * Set the service filter of the service dependency. * - * @return the C++ service dependency reference for chaining (fluent API) + * @return the service dependency reference for chaining (fluent API) */ ServiceDependency<T,I>& setFilter(const std::string &filter); /** * Set the service version range of the service dependency. * - * @return the C++ service dependency reference for chaining (fluent API) + * @return the service dependency reference for chaining (fluent API) */ ServiceDependency<T,I>& setVersionRange(const std::string &versionRange); /** * Set the set callback for when the service dependency becomes available * - * @return the C++ service dependency reference for chaining (fluent API) + * @return the service dependency reference for chaining (fluent API) */ ServiceDependency<T,I>& setCallbacks(void (T::*set)(I* service)); /** * Set the set callback for when the service dependency becomes available * - * @return the C++ service dependency reference for chaining (fluent API) + * @return the service dependency reference for chaining (fluent API) */ ServiceDependency<T,I>& setCallbacks(void (T::*set)(I* service, Properties&& properties)); /** * Set the set callback for when the service dependency becomes available * - * @return the C service dependency reference for chaining (fluent API) + * @return the service dependency reference for chaining (fluent API) */ ServiceDependency<T,I>& setCallbacks(std::function<void(I* service, Properties&& properties)> set); /** + * @brief Set the set callback for when the service dependency becomes available using shared pointers + * + * @return the service dependency reference for chaining (fluent API) + */ + ServiceDependency<T,I>& setCallbacks(void (T::*set)(const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& properties)); + + /** + * @brief Set the set callback for when the service dependency becomes available using shared pointers. + * + * @return the service dependency reference for chaining (fluent API) + */ + ServiceDependency<T,I>& setCallbacks(std::function<void(const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& properties)> set); + + /** + * @brief Set the set callback for when the service dependency becomes available using shared pointers + * + * @return the service dependency reference for chaining (fluent API) + */ + ServiceDependency<T,I>& setCallbacks(void (T::*set)(const std::shared_ptr<I>& service)); + + /** + * @brief Set the set callback for when the service dependency becomes available using shared pointers. + * + * @return the service dependency reference for chaining (fluent API) + */ + ServiceDependency<T,I>& setCallbacks(std::function<void(const std::shared_ptr<I>& service)> set); + + /** * Set the add and remove callback for when the services of service dependency are added or removed. * - * @return the C++ service dependency reference for chaining (fluent API) + * @return the service dependency reference for chaining (fluent API) */ ServiceDependency<T,I>& setCallbacks(void (T::*add)(I* service), void (T::*remove)(I* service)); /** * Set the add and remove callback for when the services of service dependency are added or removed. * - * @return the C++ service dependency reference for chaining (fluent API) + * @return the service dependency reference for chaining (fluent API) */ ServiceDependency<T,I>& setCallbacks( void (T::*add)(I* service, Properties&& properties), @@ -320,7 +364,7 @@ namespace celix { namespace dm { /** * Set the add and remove callback for when the services of service dependency are added or removed. * - * @return the C service dependency reference for chaining (fluent API) + * @return the service dependency reference for chaining (fluent API) */ ServiceDependency<T,I>& setCallbacks( std::function<void(I* service, Properties&& properties)> add, @@ -328,16 +372,53 @@ namespace celix { namespace dm { ); /** + * @brief Set the add and remove callback for a service dependency using shared pointers. + * + * @return the service dependency reference for chaining (fluent API) + */ + ServiceDependency<T,I>& setCallbacks( + void (T::*add)(const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& properties), + void (T::*remove)(const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& properties)); + + /** + * @brief Set the add and remove callback for a service dependency using shared pointers. + * + * @return the service dependency reference for chaining (fluent API) + */ + ServiceDependency<T,I>& setCallbacks( + std::function<void(const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& properties)> add, + std::function<void(const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& properties)> remove); + + + /** + * @brief Set the add and remove callback for a service dependency using shared pointers. + * + * @return the service dependency reference for chaining (fluent API) + */ + ServiceDependency<T,I>& setCallbacks( + void (T::*add)(const std::shared_ptr<I>& service), + void (T::*remove)(const std::shared_ptr<I>& service)); + + /** + * @brief Set the add and remove callback for a service dependency using shared pointers. + * + * @return the service dependency reference for chaining (fluent API) + */ + ServiceDependency<T,I>& setCallbacks( + std::function<void(const std::shared_ptr<I>& service)> add, + std::function<void(const std::shared_ptr<I>& service)> remove); + + /** * Specify if the service dependency is required. Default is false * - * @return the C service dependency reference for chaining (fluent API) + * @return the service dependency reference for chaining (fluent API) */ ServiceDependency<T,I>& setRequired(bool req); /** * Specify if the update strategy to use * - * @return the C service dependency reference for chaining (fluent API) + * @return the service dependency reference for chaining (fluent API) */ ServiceDependency<T,I>& setStrategy(DependencyUpdateStrategy strategy); @@ -345,6 +426,8 @@ namespace celix { namespace dm { * Specify if the service dependency should add a service.lang filter part if it is not already present * For C++ service dependencies 'service.lang=C++' will be added. * Should be called before + * + * @return the service dependency reference for chaining (fluent API) */ ServiceDependency<T,I>& setAddLanguageFilter(bool addLang); @@ -354,26 +437,38 @@ namespace celix { namespace dm { * When build the service dependency is active and the service tracker is created. * * Should not be called on the Celix event thread. + * + * @return the service dependency reference for chaining (fluent API) */ ServiceDependency<T,I>& build(); /** * Same a build, but will not wait till the underlining service trackers are opened. * Can be called on the Celix event thread. + * + * @return the service dependency reference for chaining (fluent API) */ ServiceDependency<T,I>& buildAsync(); private: + void setupService(); + void setupCallbacks(); + int invokeCallback(std::function<void(I*, Properties&&)> fp, const celix_properties_t *props, const void* service); + std::string name {}; std::string filter {}; std::string versionRange {}; - std::function<void(I* service, Properties&& properties)> setFp{nullptr}; - std::function<void(I* service, Properties&& properties)> addFp{nullptr}; - std::function<void(I* service, Properties&& properties)> removeFp{nullptr}; + std::function<void(I* service, Properties&& properties)> setFp{}; + std::function<void(I* service, Properties&& properties)> addFp{}; + std::function<void(I* service, Properties&& properties)> removeFp{}; - void setupService(); - void setupCallbacks(); - int invokeCallback(std::function<void(I*, Properties&&)> fp, const celix_properties_t *props, const void* service); + std::function<void(const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& properties)> setFpUsingSharedPtr{}; + std::function<void(const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& properties)> addFpUsingSharedPtr{}; + std::function<void(const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& properties)> removeFpUsingSharedPtr{}; + + //note below is only updated in the Celix event thread. + std::unordered_map<long, std::pair<std::shared_ptr<I>, std::shared_ptr<const celix::Properties>>> addedServices{}; + std::pair<std::shared_ptr<I>, std::shared_ptr<const celix::Properties>> setService{}; }; }} diff --git a/libs/framework/include/celix/dm/ServiceDependency_Impl.h b/libs/framework/include/celix/dm/ServiceDependency_Impl.h index 753b517..2d53adf 100644 --- a/libs/framework/include/celix/dm/ServiceDependency_Impl.h +++ b/libs/framework/include/celix/dm/ServiceDependency_Impl.h @@ -20,6 +20,9 @@ #include <vector> #include <iostream> #include <cstring> +#include <thread> + +#include "celix/Constants.h" #include "celix_constants.h" #include "celix_properties.h" #include "celix_bundle_context.h" @@ -29,6 +32,32 @@ using namespace celix::dm; + +template<typename U> +inline void BaseServiceDependency::waitForExpired(std::weak_ptr<U> observe, long svcId, const char* observeType) { + auto start = std::chrono::system_clock::now(); + while (!observe.expired()) { + auto now = std::chrono::system_clock::now(); + auto durationInMilli = std::chrono::duration_cast<std::chrono::milliseconds>(now - start); + if (durationInMilli > warningTimoutForNonExpiredSvcObject) { + if (cCmp) { + auto* ctx = celix_dmComponent_getBundleContext(cCmp); + if (ctx) { + celix_bundleContext_log( + ctx, + CELIX_LOG_LEVEL_WARNING, + "celix::dm::ServiceDependency: Cannot remove %s associated with service.id %li, because it is still in use. Current shared_ptr use count is %i", + observeType, + svcId, + (int)observe.use_count()); + } + } + start = now; + } + std::this_thread::sleep_for(std::chrono::milliseconds{50}); + } +} + inline void BaseServiceDependency::runBuild() { bool alreadyAdded = depAddedToCmp.exchange(true); if (!alreadyAdded) { @@ -179,19 +208,19 @@ void CServiceDependency<T,I>::setupCallbacks() { int(*cadd)(void*, void *, const celix_properties_t*) {nullptr}; int(*crem)(void*, void *, const celix_properties_t*) {nullptr}; - if (setFp != nullptr) { + if (setFp) { cset = [](void* handle, void *service, const celix_properties_t *props) -> int { auto dep = (CServiceDependency<T,I>*) handle; return dep->invokeCallback(dep->setFp, props, service); }; } - if (addFp != nullptr) { + if (addFp) { cadd = [](void* handle, void *service, const celix_properties_t *props) -> int { auto dep = (CServiceDependency<T,I>*) handle; return dep->invokeCallback(dep->addFp, props, service); }; } - if (removeFp != nullptr) { + if (removeFp) { crem= [](void* handle, void *service, const celix_properties_t *props) -> int { auto dep = (CServiceDependency<T,I>*) handle; return dep->invokeCallback(dep->removeFp, props, service); @@ -317,6 +346,40 @@ ServiceDependency<T,I>& ServiceDependency<T,I>::setCallbacks(std::function<void( return *this; } +template<class T, class I> +ServiceDependency<T,I>& ServiceDependency<T,I>::setCallbacks(void (T::*set)(const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& properties)) { + this->setCallbacks([this, set](const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& properties) { + T *cmp = this->componentInstance; + (cmp->*set)(std::move(service), properties); + }); + return *this; +} + +template<class T, class I> +ServiceDependency<T,I>& ServiceDependency<T,I>::setCallbacks(std::function<void(const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& properties)> set) { + this->setFpUsingSharedPtr = std::move(set); + this->setupCallbacks(); + return *this; +} + +template<class T, class I> +ServiceDependency<T,I>& ServiceDependency<T,I>::setCallbacks(void (T::*set)(const std::shared_ptr<I>& service)) { + this->setCallbacks([this, set](const std::shared_ptr<I>& service) { + T *cmp = this->componentInstance; + (cmp->*set)(service); + }); + return *this; +} + +template<class T, class I> +ServiceDependency<T,I>& ServiceDependency<T,I>::setCallbacks(std::function<void(const std::shared_ptr<I>& service)> set) { + this->setCallbacks([set](const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& /*properties*/) { + set(service); + }); + return *this; +} + + //add remove callbacks template<class T, class I> ServiceDependency<T,I>& ServiceDependency<T,I>::setCallbacks( @@ -353,6 +416,7 @@ ServiceDependency<T,I>& ServiceDependency<T,I>::setCallbacks( return *this; } + template<class T, class I> ServiceDependency<T,I>& ServiceDependency<T,I>::setCallbacks( std::function<void(I* service, Properties&& properties)> add, @@ -364,6 +428,70 @@ ServiceDependency<T,I>& ServiceDependency<T,I>::setCallbacks( } template<class T, class I> +ServiceDependency<T,I>& ServiceDependency<T,I>::setCallbacks( + void (T::*add)(const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& properties), + void (T::*remove)(const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& properties) +) { + this->setCallbacks( + [this, add](const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& properties) { + T *cmp = this->componentInstance; + (cmp->*add)(service, properties); + }, + [this, remove](const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& properties) { + T *cmp = this->componentInstance; + (cmp->*remove)(service, properties); + } + ); + return *this; +} + + +template<class T, class I> +ServiceDependency<T,I>& ServiceDependency<T,I>::setCallbacks( + std::function<void(const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& properties)> add, + std::function<void(const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& properties)> remove) { + this->addFpUsingSharedPtr = std::move(add); + this->removeFpUsingSharedPtr = std::move(remove); + this->setupCallbacks(); + return *this; +} + +template<class T, class I> +ServiceDependency<T,I>& ServiceDependency<T,I>::setCallbacks( + void (T::*add)(const std::shared_ptr<I>& service), + void (T::*remove)(const std::shared_ptr<I>& service) +) { + this->setCallbacks( + [this, add](const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& /*properties*/) { + T *cmp = this->componentInstance; + (cmp->*add)(service); + }, + [this, remove](const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& /*properties*/) { + T *cmp = this->componentInstance; + (cmp->*remove)(service); + } + ); + return *this; +} + + +template<class T, class I> +ServiceDependency<T,I>& ServiceDependency<T,I>::setCallbacks( + std::function<void(const std::shared_ptr<I>& service)> add, + std::function<void(const std::shared_ptr<I>& service)> remove) { + this->setCallbacks( + [add](const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& /*properties*/) { + add(service); + }, + [remove](const std::shared_ptr<I>& service, const std::shared_ptr<const celix::Properties>& /*properties*/) { + remove(service); + } + ); + return *this; +} + + +template<class T, class I> ServiceDependency<T,I>& ServiceDependency<T,I>::setRequired(bool req) { celix_dmServiceDependency_setRequired(this->cServiceDependency(), req); return *this; @@ -403,22 +531,64 @@ void ServiceDependency<T,I>::setupCallbacks() { int(*cadd)(void*, void *, const celix_properties_t*) {nullptr}; int(*crem)(void*, void *, const celix_properties_t*) {nullptr}; - if (setFp != nullptr) { - cset = [](void* handle, void *service, const celix_properties_t* props) -> int { + if (setFp || setFpUsingSharedPtr) { + cset = [](void* handle, void* rawSvc, const celix_properties_t* rawProps) -> int { + int rc = 0; auto dep = (ServiceDependency<T,I>*) handle; - return dep->invokeCallback(dep->setFp, props, service); + if (dep->setFp) { + rc = dep->invokeCallback(dep->setFp, rawProps, rawSvc); + } + if (dep->setFpUsingSharedPtr) { + auto svcId = dep->setService.second ? dep->setService.second->getAsLong(celix::SERVICE_ID, -1) : -1; + std::weak_ptr<I> currentSvc = dep->setService.first; + std::weak_ptr<const celix::Properties> currentProps = dep->setService.second; + auto svc = std::shared_ptr<I>{static_cast<I*>(rawSvc), [](I*){/*nop*/}}; + auto props = rawProps ? celix::Properties::wrap(rawProps) : nullptr; + dep->setService = std::make_pair(std::move(svc), std::move(props)); + dep->setFpUsingSharedPtr(dep->setService.first, dep->setService.second); + dep->waitForExpired(currentSvc, svcId, "service pointer"); + dep->waitForExpired(currentProps, svcId, "service properties"); + } + return rc; }; } - if (addFp != nullptr) { - cadd = [](void* handle, void *service, const celix_properties_t* props) -> int { + if (addFp || addFpUsingSharedPtr) { + cadd = [](void* handle, void *rawSvc, const celix_properties_t* rawProps) -> int { + int rc = 0; auto dep = (ServiceDependency<T,I>*) handle; - return dep->invokeCallback(dep->addFp, props, service); + if (dep->addFp) { + rc = dep->invokeCallback(dep->addFp, rawProps, rawSvc); + } + if (dep->addFpUsingSharedPtr) { + auto props = celix::Properties::wrap(rawProps); + auto svc = std::shared_ptr<I>{static_cast<I*>(rawSvc), [](I*){/*nop*/}}; + auto svcId = props->getAsLong(celix::SERVICE_ID, -1); + dep->addFpUsingSharedPtr(svc, props); + dep->addedServices.template emplace(svcId, std::make_pair(std::move(svc), std::move(props))); + } + return rc; }; } - if (removeFp != nullptr) { - crem = [](void* handle, void *service, const celix_properties_t*props) -> int { + if (removeFp || removeFpUsingSharedPtr) { + crem = [](void* handle, void *rawSvc, const celix_properties_t* rawProps) -> int { + int rc = 0; auto dep = (ServiceDependency<T,I>*) handle; - return dep->invokeCallback(dep->removeFp, props, service); + if (dep->removeFp) { + rc = dep->invokeCallback(dep->removeFp, rawProps, rawSvc); + } + if (dep->removeFpUsingSharedPtr) { + auto svcId = celix_properties_getAsLong(rawProps, celix::SERVICE_ID, -1); + auto it = dep->addedServices.find(svcId); + if (it != dep->addedServices.end()) { + std::weak_ptr<I> currentSvc = it->second.first; + std::weak_ptr<const celix::Properties> currentProps = it->second.second; + dep->removeFpUsingSharedPtr(it->second.first, it->second.second); + dep->addedServices.erase(it); + dep->template waitForExpired(currentSvc, svcId, "service pointer"); + dep->template waitForExpired(currentProps, svcId, "service properties"); + } + } + return rc; }; }
