From b5ee724a43a4bfc7def24102f8648b4c58388d8e Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sat, 10 Feb 2024 17:15:29 +0100 Subject: [PATCH 01/14] gh-509: Add bundle context functions to use underlying svc tracker --- .../CelixBundleContextServicesTestSuite.cc | 168 ++++++ libs/framework/include/celix_bundle_context.h | 555 +++++++++++++----- .../include_deprecated/service_tracker.h | 17 +- libs/framework/src/bundle.c | 5 +- libs/framework/src/bundle_context.c | 261 ++++++-- libs/framework/src/bundle_context_private.h | 29 +- libs/framework/src/service_tracker.c | 30 +- 7 files changed, 821 insertions(+), 244 deletions(-) diff --git a/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc b/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc index d17b3892b..02c499496 100644 --- a/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc +++ b/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc @@ -52,6 +52,8 @@ class CelixBundleContextServicesTestSuite : public ::testing::Test { fw = celix_frameworkFactory_createFramework(properties); ctx = framework_getContext(fw); + + celix_err_resetErrors(); } ~CelixBundleContextServicesTestSuite() override { @@ -1741,3 +1743,169 @@ TEST_F(CelixBundleContextServicesTestSuite, SetServicesWithTrackerWhenMultipleRe celix_bundleContext_unregisterService(ctx, svcId2); celix_bundleContext_unregisterService(ctx, svcId3); } + + +TEST_F(CelixBundleContextServicesTestSuite, InvalidArgumentsForUseTrackedServicesTest) { + EXPECT_FALSE(celix_bundleContext_useTrackedService(ctx, -1, nullptr, nullptr)); + EXPECT_FALSE(celix_bundleContext_useTrackedService(ctx, 1 /*non existing*/, nullptr, nullptr)); + + EXPECT_EQ(0, celix_bundleContext_useTrackedServices(ctx, -1, nullptr, nullptr)); + EXPECT_EQ(0, celix_bundleContext_useTrackedServices(ctx, 1 /*non existing*/, nullptr, nullptr)); + + celix_tracked_service_use_options_t useOpts{}; + EXPECT_FALSE(celix_bundleContext_useTrackedServiceWithOptions(ctx, -1, &useOpts)); + EXPECT_FALSE(celix_bundleContext_useTrackedServiceWithOptions(ctx, 1 /*non existing*/, &useOpts)); + + EXPECT_EQ(0, celix_bundleContext_useTrackedServicesWithOptions(ctx, -1, &useOpts)); + EXPECT_EQ(0, celix_bundleContext_useTrackedServicesWithOptions(ctx, 1 /*non existing*/, &useOpts)); + + EXPECT_EQ(0, celix_bundleContext_getTrackedServiceCount(ctx, -1)); + EXPECT_EQ(0, celix_bundleContext_getTrackedServiceCount(ctx, 1 /*non existing*/)); + + EXPECT_EQ(nullptr, celix_bundleContext_getTrackedServiceName(ctx, -1)); + EXPECT_EQ(nullptr, celix_bundleContext_getTrackedServiceName(ctx, 1 /*non existing*/)); + + EXPECT_EQ(nullptr, celix_bundleContext_getTrackedServiceFilter(ctx, -1)); + EXPECT_EQ(nullptr, celix_bundleContext_getTrackedServiceFilter(ctx, 1 /*non existing*/)); + + EXPECT_FALSE(celix_bundleContext_isValidTrackerId(ctx, -1)); +} + +TEST_F(CelixBundleContextServicesTestSuite, IsValidTrackerIdTest) { + long trkId = celix_bundleContext_trackServices(ctx, "test", nullptr, nullptr, nullptr); + EXPECT_TRUE(celix_bundleContext_isValidTrackerId(ctx, trkId)); + celix_bundleContext_stopTracker(ctx, trkId); + EXPECT_FALSE(celix_bundleContext_isValidTrackerId(ctx, trkId)); +} + +TEST_F(CelixBundleContextServicesTestSuite, UseTrackedServiceTest) { + // Given 3 foo services with different service properties + celix_properties_t* props1 = celix_properties_create(); + celix_properties_set(props1, "key", "1"); + long svcId1 = celix_bundleContext_registerService(ctx, (void*)0x42, "test", props1); + celix_auto(celix_service_registration_guard_t) guard1 = celix_serviceRegistrationGuard_init(ctx, svcId1); + + celix_properties_t* props2 = celix_properties_create(); + celix_properties_set(props2, "key", "2"); + long svcId2 = celix_bundleContext_registerService(ctx, (void*)0x42, "test", props2); + celix_auto(celix_service_registration_guard_t) guard2 = celix_serviceRegistrationGuard_init(ctx, svcId2); + + celix_properties_t* props3 = celix_properties_create(); + celix_properties_set(props3, "key", "3"); + long svcId3 = celix_bundleContext_registerService(ctx, (void*)0x42, "test", props3); + celix_auto(celix_service_registration_guard_t) guard3 = celix_serviceRegistrationGuard_init(ctx, svcId3); + + // When tracking services for a service name + long trkId = celix_bundleContext_trackServices(ctx, "test", nullptr, nullptr, nullptr); + celix_auto(celix_tracker_guard_t) trkGuard = celix_trackerGuard_init(ctx, trkId); + + // Then the useTrackedService function should be called for each service + struct use_data { + long bndId{0}; + int count{0}; //note atomic not needed because the use function is called in the same thread. + }; + use_data data{}; + data.bndId = celix_bundleContext_getBundleId(ctx); + celix_tracked_service_use_options_t useOpts{}; + useOpts.callbackHandle = (void*)&data; + useOpts.use = [](void* handle, void* svc) { + EXPECT_EQ((void*)0x42, svc); + auto *d = static_cast(handle); + d->count++; + }; + useOpts.useWithProperties = [](void* handle, void* svc, const celix_properties_t* props) { + EXPECT_EQ((void*)0x42, svc); + auto* val = celix_properties_get(props, "key", nullptr); + EXPECT_TRUE(val != nullptr); + auto *d = static_cast(handle); + d->count++; + }; + useOpts.useWithOwner = [](void* handle, void* svc, const celix_properties_t* props, const celix_bundle_t* owner) { + EXPECT_EQ((void*)0x42, svc); + auto* val = celix_properties_get(props, "key", nullptr); + EXPECT_TRUE(val != nullptr); + auto *d = static_cast(handle); + d->count++; + EXPECT_EQ(celix_bundle_getId(owner), d->bndId); + }; + auto count = celix_bundleContext_useTrackedServicesWithOptions(ctx, trkId, &useOpts); + EXPECT_EQ(3, count); + EXPECT_EQ(9, data.count); // 3x use, 3x useWithProperties, 3x useWithOwner + + // And the useTrackedServiceWithOptions function should be called a single time + data.count = 0; + bool called = celix_bundleContext_useTrackedServiceWithOptions(ctx, trkId, &useOpts); + EXPECT_TRUE(called); + EXPECT_EQ(3, data.count); // 1x use, 1x useWithProperties, 1x useWithOwner + + // And the useTrackedServices function should be called 3 times + data.count = 0; + count = celix_bundleContext_useTrackedServices(ctx, trkId, useOpts.callbackHandle, useOpts.use); + EXPECT_EQ(3, count); + EXPECT_EQ(3, data.count); // 3x use + + // And the useTrackedService function should be called a single time + data.count = 0; + called = celix_bundleContext_useTrackedService(ctx, trkId, useOpts.callbackHandle, useOpts.use); + EXPECT_TRUE(called); + EXPECT_EQ(1, data.count); // 1x use + + // When tracking a service with a filter + celix_service_tracking_options_t opts{}; + opts.filter.serviceName = "test"; + opts.filter.filter = "(key=1)"; + long trkId2 = celix_bundleContext_trackServicesWithOptions(ctx, &opts); + celix_auto(celix_tracker_guard_t) trkGuard2 = celix_trackerGuard_init(ctx, trkId2); + + // Then the useTrackedServiceWithOption function should be called for the service with the matching filter + useOpts.use = nullptr; + useOpts.useWithOwner = nullptr; + useOpts.useWithProperties = [](void* handle, void* svc, const celix_properties_t* props) { + EXPECT_EQ((void*)0x42, svc); + auto* val = celix_properties_get(props, "key", nullptr); + EXPECT_TRUE(val != nullptr); + EXPECT_STREQ("1", val); + auto *d = static_cast(handle); + d->count++; + }; + data.count = 0; + called = celix_bundleContext_useTrackedServiceWithOptions(ctx, trkId2, &useOpts); + EXPECT_TRUE(called); + EXPECT_EQ(1, data.count); // 1x useWithProperties + + // And the useTrackedServicesWithOption function should be called a single time + data.count = 0; + count = celix_bundleContext_useTrackedServicesWithOptions(ctx, trkId2, &useOpts); + EXPECT_EQ(1, count); + EXPECT_EQ(1, data.count); // 1x useWithProperties +} + +TEST_F(CelixBundleContextServicesTestSuite, GetTrackedServicesInfoTest) { + //When a service tracker for a specific service name and with a filter + celix_service_tracking_options_t opts{}; + opts.filter.serviceName = "test"; + opts.filter.filter = "(key=1)"; + long trkId = celix_bundleContext_trackServicesWithOptions(ctx, &opts); + celix_auto(celix_tracker_guard_t) trkGuard = celix_trackerGuard_init(ctx, trkId); + + // And a service is registered with a matching service name and filter + celix_properties_t* props = celix_properties_create(); + celix_properties_set(props, "key", "1"); + long svcId = celix_bundleContext_registerService(ctx, (void*)0x42, "test", props); + celix_auto(celix_service_registration_guard_t) svcGuard = celix_serviceRegistrationGuard_init(ctx, svcId); + + // Then the tracked services info should be available + EXPECT_EQ(celix_bundleContext_getTrackedServiceCount(ctx, trkId), 1); + EXPECT_STREQ(celix_bundleContext_getTrackedServiceName(ctx, trkId), "test"); + EXPECT_STREQ(celix_bundleContext_getTrackedServiceFilter(ctx, trkId), "(&(objectClass=test)(key=1))"); + + + // When a tracker for all services is created + long trkId2 = celix_bundleContext_trackServices(ctx, nullptr, nullptr, nullptr, nullptr); + celix_auto(celix_tracker_guard_t) trkGuard2 = celix_trackerGuard_init(ctx, trkId2); + + // Then the tracked services info should be available + EXPECT_EQ(celix_bundleContext_getTrackedServiceCount(ctx, trkId2), 1); + EXPECT_STREQ(celix_bundleContext_getTrackedServiceName(ctx, trkId2), "*"); + EXPECT_TRUE(strstr(celix_bundleContext_getTrackedServiceFilter(ctx, trkId2), "(objectClass=*)") != nullptr); +} diff --git a/libs/framework/include/celix_bundle_context.h b/libs/framework/include/celix_bundle_context.h index d582fabd6..e18992f57 100644 --- a/libs/framework/include/celix_bundle_context.h +++ b/libs/framework/include/celix_bundle_context.h @@ -392,6 +392,197 @@ CELIX_FRAMEWORK_EXPORT long celix_bundleContext_findServiceWithOptions(celix_bun */ CELIX_FRAMEWORK_EXPORT celix_array_list_t* celix_bundleContext_findServicesWithOptions(celix_bundle_context_t *ctx, const celix_service_filter_options_t *opts); +/** + * @brief Use the service with the provided service id using the provided callback. The Celix framework will ensure that + * the targeted service cannot be removed during the callback. + * + * The svc is should only be considered valid during the callback. + * If no service is found, the callback will not be invoked and this function will return false immediately. + * + * This function will block until the callback is finished. As result it is possible to provide callback data from the + * stack. + * + * @param ctx The bundle context + * @param serviceId the service id. + * @param serviceName the service name of the service. Should match with the registered service name of the provided service id (sanity check) + * @param callbackHandle The data pointer, which will be used in the callbacks + * @param use The callback, which will be called when service is retrieved. + * @param bool returns true if a service was found. + */ +CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useServiceWithId( + celix_bundle_context_t *ctx, + long serviceId, + const char* serviceName /*sanity check*/, + void *callbackHandle, + void (*use)(void *handle, void* svc) +); + +/** + * @brief Use the highest ranking service with the provided service name using the provided callback. + * + * The Celix framework will ensure that the targeted service cannot be removed during the callback. + * + * The svc is should only be considered valid during the callback. + * If no service is found, the callback will not be invoked and this function will return false immediately. + * + * This function will block until the callback is finished. As result it is possible to provide callback data from the + * stack. + * + * @param ctx The bundle context + * @param serviceName the required service name. + * @param callbackHandle The data pointer, which will be used in the callbacks + * @param use The callback, which will be called when service is retrieved. + * @return True if a service was found. + */ +CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useService( + celix_bundle_context_t *ctx, + const char* serviceName, + void *callbackHandle, + void (*use)(void *handle, void *svc) +); + +/** + * @brief Use the services with the provided service name using the provided callback. + * + * The Celix framework will ensure that the targeted service cannot be removed during the callback. + * + * The svc is should only be considered valid during the callback. + * If no service is found, the callback will not be invoked and this function will return 0 immediately. + * + * This function will block until the callback is finished. As result it is possible to provide callback data from the + * stack. + * + * @param ctx The bundle context + * @param serviceName the required service name. + * @param callbackHandle The data pointer, which will be used in the callbacks + * @param use The callback, which will be called for every service found. + * @return The number of services found and called + */ +CELIX_FRAMEWORK_EXPORT size_t celix_bundleContext_useServices( + celix_bundle_context_t *ctx, + const char* serviceName, + void *callbackHandle, + void (*use)(void *handle, void *svc) +); + +/** + * @brief Service Use Options used to fine tune which services to use and which callbacks to use. + * + * If multiple use callbacks are set, all set callbacks will be called for every service found. + */ +typedef struct celix_service_use_options { + /** + * @brief The service filter options, used to setup the filter for the service to track. + */ + celix_service_filter_options_t filter CELIX_OPTS_INIT; + + /** + * @brief An optional timeout (in seconds), if > 0 the use service call will block until the timeout is expired or + * when at least one service is found. Note that it will be ignored when use service on the event loop. + * Default (0) + * + * Only applicable when using the celix_bundleContext_useService or + * celix_bundleContext_useServiceWithOptions (use single service calls). + */ + double waitTimeoutInSeconds CELIX_OPTS_INIT; + + /** + * @brief The optional callback pointer used in all the provided callback function (use, useWithProperties, and + * useWithOwner). + */ + void* callbackHandle CELIX_OPTS_INIT; + + /** + * @brief The optional use callback will be called when for every services found conform the service filter options + * - in case of findServices - or only for the highest ranking service found - in case of findService -. + * + * @param handle The callbackHandle pointer as provided in the service tracker options. + * @param svc The service pointer of the highest ranking service. + */ + void (*use)(void* handle, void* svc) CELIX_OPTS_INIT; + + /** + * @brief The optional useWithProperties callback is handled as the use callback, but with the addition that the + * service properties will also be provided to the callback. + */ + void (*useWithProperties)(void* handle, void* svc, const celix_properties_t* props) CELIX_OPTS_INIT; + + /** + * @brief The optional useWithOwner callback is handled as the yse callback, but with the addition that the service + * properties and the bundle owning the service will also be provided to the callback. + */ + void (*useWithOwner)(void* handle, void* svc, const celix_properties_t* props, const celix_bundle_t* svcOwner) + CELIX_OPTS_INIT; + /** + * @brief Call the provided callbacks from the caller thread directly if set, otherwise the callbacks will be called + * from the Celix event loop (most likely indirectly). Note that using blocking service in the Celix event loop is + * generally a bad idea, which should be avoided if possible. + */ +#define CELIX_SERVICE_USE_DIRECT (1) + /** + * @brief Whether "service on demand" pattern is supported when CELIX_SERVICE_USE_DIRECT is set. + * Note that it has no effect in indirect mode, in which case "service on demand" is supported. + */ +#define CELIX_SERVICE_USE_SOD (2) + int flags CELIX_OPTS_INIT; +} celix_service_use_options_t; + +#ifndef __cplusplus +/*! + * @brief C Macro to create a empty celix_service_use_options_t type. + */ +#define CELIX_EMPTY_SERVICE_USE_OPTIONS {.filter.serviceName = NULL, \ + .filter.versionRange = NULL, \ + .filter.filter = NULL, \ + .waitTimeoutInSeconds = 0.0F, \ + .callbackHandle = NULL, \ + .use = NULL, \ + .useWithProperties = NULL, \ + .useWithOwner = NULL, \ + .flags=0} +#endif + +/** + * @brief Use the highest ranking service satisfying the provided service filter options using the provided callback. + * + * The Celix framework will ensure that the targeted service cannot be removed during the callback. + * + * The svc is should only be considered valid during the callback. + * If no service is found the callback will not be invoked. In such cases, if a non-zero waitTimeoutInSeconds is specified in opts, + * this function will block until the timeout is expired or when at least one service is found, otherwise it will return false immediately. + * + * This function will block until the callback is finished. As result it is possible to provide callback data from the + * stack. + * + * @param ctx The bundle context. + * @param opts The required options. Note that the serviceName is required. + * @return True if a service was found. + */ +CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useServiceWithOptions( + celix_bundle_context_t *ctx, + const celix_service_use_options_t *opts); + + +/** + * @brief Use the services with the provided service filter options using the provided callback. + * + * The Celix framework will ensure that the targeted service cannot be removed during the callback. + * + * The svc is should only be considered valid during the callback. + * If no service is found, the callback will not be invoked and this function will return 0 immediately. + * Note that waitTimeoutInSeconds in opts has no effect. + * + * This function will block until the callback is finished. As result it is possible to provide callback data from the + * stack. + * + * @param ctx The bundle context. + * @param opts The required options. Note that the serviceName is required. + * @return The number of services found and called + */ +CELIX_FRAMEWORK_EXPORT size_t celix_bundleContext_useServicesWithOptions( + celix_bundle_context_t *ctx, + const celix_service_use_options_t *opts); + /** * @brief Track the highest ranking service with the provided serviceName. * @@ -615,7 +806,6 @@ CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServicesWithOptionsAsync(ce * The tracker options are only using during this call and can safely be freed/reused after this call returns. * Note: Please use the celix_bundleContext_registerServiceFactoryAsync instead. * - * * @param ctx The bundle context. * @param opts The pointer to the tracker options. * @return the tracker id (>=0) or < 0 if unsuccessful. @@ -623,143 +813,71 @@ CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServicesWithOptionsAsync(ce CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServicesWithOptions(celix_bundle_context_t *ctx, const celix_service_tracking_options_t *opts); /** - * @brief Stop the tracker with the provided track id. - * - * Could be a service tracker, bundle tracker or service tracker tracker. - * Only works for the trackers owned by the bundle of the bundle context. + * @brief Use the highest ranking service, tracked by the provided tracker id, using the provided callback. * - * The service tracker will be destroyed async on the Celix event loop thread. This means that the function can return - * before the tracker is destroyed. + * If an service is found the use callback will be called on the thread that called this function and when function + * returns the callback is finished. * - * if the doneCallback is not NULL, this will be called when the destruction of the service tracker is done. - * (will be called on the event loop thread). + * An tracker id < 0 will be silently ignored. + * An invalid (non existing) tracker id >= 0 will be logged and the function will return false. * - * Will log a error if the provided tracker id is unknown. Will silently ignore trackerId < 0. + * @param[in] ctx The bundle context. + * @param[in] trackerId The tracker id. + * @param[in] callbackHandle The data pointer, which will be used in the callbacks. + * @param[in] use The callback, which will be called when service is retrieved. + * @return True if a service was found and the use callback was called. */ -CELIX_FRAMEWORK_EXPORT void celix_bundleContext_stopTrackerAsync( +CELIX_FRAMEWORK_EXPORT +bool celix_bundleContext_useTrackedService( celix_bundle_context_t *ctx, long trackerId, - void *doneCallbackData, - void (*doneCallback)(void* doneCallbackData)); - -/** - * @brief Wait for (async) creation of tracker - */ -CELIX_FRAMEWORK_EXPORT void celix_bundleContext_waitForAsyncTracker(celix_bundle_context_t *ctx, long trackerId); - -/** - * @brief Wait for (async) stopping of tracking. - */ -CELIX_FRAMEWORK_EXPORT void celix_bundleContext_waitForAsyncStopTracker(celix_bundle_context_t *ctx, long trackerId); - -/** - * @brief Stop the tracker with the provided track id. - * - * Could be a service tracker, bundle tracker or service tracker tracker. - * Only works for the trackers owned by the bundle of the bundle context. - * Note: Please use the celix_bundleContext_registerServiceFactoryAsync instead. - * - * Will log a error if the provided tracker id is unknown. Will silently ignore trackerId < 0. - */ -CELIX_FRAMEWORK_EXPORT void celix_bundleContext_stopTracker(celix_bundle_context_t *ctx, long trackerId); - -/** - * @brief Use the service with the provided service id using the provided callback. The Celix framework will ensure that - * the targeted service cannot be removed during the callback. - * - * The svc is should only be considered valid during the callback. - * If no service is found, the callback will not be invoked and this function will return false immediately. - * - * This function will block until the callback is finished. As result it is possible to provide callback data from the - * stack. - * - * @param ctx The bundle context - * @param serviceId the service id. - * @param serviceName the service name of the service. Should match with the registered service name of the provided service id (sanity check) - * @param callbackHandle The data pointer, which will be used in the callbacks - * @param use The callback, which will be called when service is retrieved. - * @param bool returns true if a service was found. - */ -CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useServiceWithId( - celix_bundle_context_t *ctx, - long serviceId, - const char* serviceName /*sanity check*/, void *callbackHandle, void (*use)(void *handle, void* svc) ); /** - * @brief Use the highest ranking service with the provided service name using the provided callback. + * @brief Use the services, tracked by the provided tracker id, using the provided callback. * - * The Celix framework will ensure that the targeted service cannot be removed during the callback. - * - * The svc is should only be considered valid during the callback. - * If no service is found, the callback will not be invoked and this function will return false immediately. + * If 1 or more services is found the use callback will be called for every service found on the thread that called this + * function and when function returns the callbacks are finished. * - * This function will block until the callback is finished. As result it is possible to provide callback data from the - * stack. + * An tracker id < 0 will be silently ignored. + * An invalid (non existing) tracker id >= 0 will be logged and the function will return 0. * - * @param ctx The bundle context - * @param serviceName the required service name. - * @param callbackHandle The data pointer, which will be used in the callbacks - * @param use The callback, which will be called when service is retrieved. - * @return True if a service was found. - */ -CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useService( - celix_bundle_context_t *ctx, - const char* serviceName, - void *callbackHandle, - void (*use)(void *handle, void *svc) -); - -/** - * @brief Use the services with the provided service name using the provided callback. - * - * The Celix framework will ensure that the targeted service cannot be removed during the callback. - * - * The svc is should only be considered valid during the callback. - * If no service is found, the callback will not be invoked and this function will return 0 immediately. - * - * This function will block until the callback is finished. As result it is possible to provide callback data from the - * stack. - * - * @param ctx The bundle context - * @param serviceName the required service name. - * @param callbackHandle The data pointer, which will be used in the callbacks - * @param use The callback, which will be called for every service found. - * @return The number of services found and called + * @param[in] ctx The bundle context. + * @param[in] trackerId The tracker id. + * @param[in] callbackHandle The data pointer, which will be used in the callbacks. + * @param[in] use The callback, which will be called for every service found. + * @return The number of services found and therefore the number of times the use callback was called. */ -CELIX_FRAMEWORK_EXPORT size_t celix_bundleContext_useServices( - celix_bundle_context_t *ctx, - const char* serviceName, - void *callbackHandle, - void (*use)(void *handle, void *svc) -); +CELIX_FRAMEWORK_EXPORT +size_t celix_bundleContext_useTrackedServices(celix_bundle_context_t* ctx, + long trackerId, + void* callbackHandle, + void (*use)(void* handle, void* svc)); /** - * @brief Service Use Options used to fine tune which services to use and which callbacks to use. + * @brief Use tracked service options used to configure which use callback to use on the tracked services. + * If multiple use callbacks are set, all set callbacks will be called for every service found. */ -typedef struct celix_service_use_options { - /** - * @brief The service filter options, used to setup the filter for the service to track. - */ - celix_service_filter_options_t filter CELIX_OPTS_INIT; - +typedef struct celix_tracked_service_use_options { /** - * @brief An optional timeout (in seconds), if > 0 the use service call will block until the timeout is expired or - * when at least one service is found. Note that it will be ignored when use service on the event loop. + * @brief An optional timeout (in seconds), if > 0 the use tracked service(s) call will block until the timeout is + * expired or when at least one service is found. Note that it will be ignored when use service on the event loop. * Default (0) + * + * Only applicable when using the celix_bundleContext_useTrackedService or + * celix_bundleContext_useTrackedServiceWithOptions (use single service calls). */ - double waitTimeoutInSeconds CELIX_OPTS_INIT; + double waitTimeoutInSeconds CELIX_OPTS_INIT; /** - * @brief The optional callback pointer used in all the provided callback function (set, add, remove, setWithProperties, etc). + * @brief The optional callback pointer used in all the provided callback function (use, useWithProperties and useWithOwner). */ void *callbackHandle CELIX_OPTS_INIT; /** - * @brief The optional use callback will be called when for every services found conform the service filter options - * - in case of findServices - or only for the highest ranking service found - in case of findService -. + * @brief The optional use callback will be called when for every service tracked. * * @param handle The callbackHandle pointer as provided in the service tracker options. * @param svc The service pointer of the highest ranking service. @@ -767,84 +885,197 @@ typedef struct celix_service_use_options { void (*use)(void *handle, void *svc) CELIX_OPTS_INIT; /** - * @brief The optional useWithProperties callback is handled as the use callback, but with the addition that the service properties - * will also be provided to the callback. + * @brief The optional useWithProperties callback will be called when for every service tracked and the + * service properties will also be provided to the callback. */ void (*useWithProperties)(void *handle, void *svc, const celix_properties_t *props) CELIX_OPTS_INIT; /** - * @brief The optional useWithOwner callback is handled as the yse callback, but with the addition that the service properties - * and the bundle owning the service will also be provided to the callback. + * @brief The optional useWithOwner callback will be called when for every service tracked and the + * service properties and the bundle owning the service will also be provided to the callback. */ void (*useWithOwner)(void *handle, void *svc, const celix_properties_t *props, const celix_bundle_t *svcOwner) CELIX_OPTS_INIT; - /** - * @brief Call the provided callbacks from the caller thread directly if set, otherwise the callbacks will be called from the Celix event loop (most likely indirectly). - * Note that using blocking service in the Celix event loop is generally a bad idea, which should be avoided if possible. - */ -#define CELIX_SERVICE_USE_DIRECT (1) - /** - * @brief Whether "service on demand" pattern is supported when CELIX_SERVICE_USE_DIRECT is set. - * Note that it has no effect in indirect mode, in which case "service on demand" is supported. - */ -#define CELIX_SERVICE_USE_SOD (2) - int flags CELIX_OPTS_INIT; -} celix_service_use_options_t; +} celix_tracked_service_use_options_t; #ifndef __cplusplus /*! - * @brief C Macro to create a empty celix_service_use_options_t type. + * @brief C Macro to create a empty celix_tracked_service_use_options_t type. */ -#define CELIX_EMPTY_SERVICE_USE_OPTIONS {.filter.serviceName = NULL, \ - .filter.versionRange = NULL, \ - .filter.filter = NULL, \ - .waitTimeoutInSeconds = 0.0F, \ - .callbackHandle = NULL, \ - .use = NULL, \ - .useWithProperties = NULL, \ - .useWithOwner = NULL, \ - .flags=0} +#define CELIX_EMPTY_TRACKER_SERVICE_USE_OPTIONS \ + { \ + .waitTimeoutInSeconds = 0.0F, .callbackHandle = NULL, .use = NULL, .useWithProperties = NULL, \ + .useWithOwner = NULL \ + } #endif /** - * @brief Use the highest ranking service satisfying the provided service filter options using the provided callback. + * @brief Use the highest ranking service, tracked by the provided tracker id, using the callbacks in the provided + * options. * - * The Celix framework will ensure that the targeted service cannot be removed during the callback. + * If an service is found the use callbacks will be called on the thread that called this function and when function + * returns the callbacks are finished. * - * The svc is should only be considered valid during the callback. - * If no service is found the callback will not be invoked. In such cases, if a non-zero waitTimeoutInSeconds is specified in opts, - * this function will block until the timeout is expired or when at least one service is found, otherwise it will return false immediately. + * An tracker id < 0 will be silently ignored. + * An invalid (non existing) tracker id >= 0 will be logged and the function will return false. * - * This function will block until the callback is finished. As result it is possible to provide callback data from the - * stack. + * @note the field USE_DIRECT in the provided options has no effect with this function. * - * @param ctx The bundle context. - * @param opts The required options. Note that the serviceName is required. - * @return True if a service was found. + * @param[in] ctx The bundle context. + * @param[in] trackerId The tracker id. + * @param[in] callbackHandle The data pointer, which will be used in the callbacks. + * @param[in] opts The service use options. + * @return True if a service was found and the use callbacks where called. */ -CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useServiceWithOptions( - celix_bundle_context_t *ctx, - const celix_service_use_options_t *opts); +CELIX_FRAMEWORK_EXPORT +bool celix_bundleContext_useTrackedServiceWithOptions(celix_bundle_context_t* ctx, + long trackerId, + const celix_tracked_service_use_options_t* opts); +/** + * @brief Use the services, tracked by the provided tracker id, using the callbacks in the provided options. + * + * If 1 or more services is found the use callbacks will be called for every service found on the thread that called + * this function and when function returns the callbacks are finished. + * + * An tracker id < 0 will be silently ignored. + * An invalid (non existing) tracker id >= 0 will be logged and the function will return 0. + * + * @note the field USE_DIRECT in the provided options has no effect with this function. + * + * @param[in] ctx The bundle context. + * @param[in] trackerId The tracker id. + * @param[in] callbackHandle The data pointer, which will be used in the callbacks. + * @param[in] opts The service use options. + * @return The number of services found and therefore the number of times the callbacks in the provided options where + * called. + */ +CELIX_FRAMEWORK_EXPORT +size_t celix_bundleContext_useTrackedServicesWithOptions(celix_bundle_context_t* ctx, + long trackerId, + const celix_tracked_service_use_options_t* opts); /** - * @brief Use the services with the provided service filter options using the provided callback. + * @brief Get the number of tracked services for the provided tracker id. * - * The Celix framework will ensure that the targeted service cannot be removed during the callback. + * Silently ignore tracker ids < 0 and invalid tracker ids. * - * The svc is should only be considered valid during the callback. - * If no service is found, the callback will not be invoked and this function will return 0 immediately. - * Note that waitTimeoutInSeconds in opts has no effect. + * @param[in] ctx The bundle context. + * @param[in] trackerId The tracker id. + * @return The number of tracked services or 0 if the tracker id is unknown or < 0. + */ +CELIX_FRAMEWORK_EXPORT +size_t celix_bundleContext_getTrackedServiceCount(celix_bundle_context_t *ctx, long trackerId); + +/** + * @brief Get the service name of the tracked services for the provided tracker id. * - * This function will block until the callback is finished. As result it is possible to provide callback data from the - * stack. + * Silently ignore tracker ids < 0 and invalid tracker ids. * - * @param ctx The bundle context. - * @param opts The required options. Note that the serviceName is required. - * @return The number of services found and called + * @param ctx The bundle context. + * @param trackerId The tracker id. + * @return The service name of the tracked services or NULL if the tracker id is unknown or < 0. */ -CELIX_FRAMEWORK_EXPORT size_t celix_bundleContext_useServicesWithOptions( +CELIX_FRAMEWORK_EXPORT +const char* celix_bundleContext_getTrackedServiceName(celix_bundle_context_t *ctx, long trackerId); + +/** + * @brief Get the service filter of the tracked services for the provided tracker id. + * + * The returned filter is the combination of the service name and the filter from the provided service filter options. + * For example serviceName="foo" and filter="(location=middle)" will result in a filter of + * "(&(objectClass=foo)(location=middle))" + * + * Silently ignore tracker ids < 0 and invalid tracker ids. + * + * @param ctx The bundle context. + * @param trackerId The tracker id. + * @return The service filter of the tracked services or NULL if the tracker id is unknown or < 0. + */ +CELIX_FRAMEWORK_EXPORT +const char* celix_bundleContext_getTrackedServiceFilter(celix_bundle_context_t* ctx, long trackerId); + +/** + * @brief Returns true if the provided tracker id is a tracker id for an existing tracker for the provided bundle + * context. + * @param ctx The bundle context. + * @param trackerId The tracker id. + * @return True if the tracker id is valid. + */ +CELIX_FRAMEWORK_EXPORT +bool celix_bundleContext_isValidTrackerId(celix_bundle_context_t* ctx, long trackerId); + +/** + * @brief Stop the tracker with the provided track id. + * + * Could be a service tracker, bundle tracker or service tracker tracker. + * Only works for the trackers owned by the bundle of the bundle context. + * + * The service tracker will be destroyed async on the Celix event loop thread. This means that the function can return + * before the tracker is destroyed. + * + * if the doneCallback is not NULL, this will be called when the destruction of the service tracker is done. + * (will be called on the event loop thread). + * + * Will log a error if the provided tracker id is unknown. Will silently ignore trackerId < 0. + */ +CELIX_FRAMEWORK_EXPORT void celix_bundleContext_stopTrackerAsync( celix_bundle_context_t *ctx, - const celix_service_use_options_t *opts); + long trackerId, + void *doneCallbackData, + void (*doneCallback)(void* doneCallbackData)); + +/** + * @brief Wait for (async) creation of tracker + */ +CELIX_FRAMEWORK_EXPORT void celix_bundleContext_waitForAsyncTracker(celix_bundle_context_t *ctx, long trackerId); + +/** + * @brief Wait for (async) stopping of tracking. + */ +CELIX_FRAMEWORK_EXPORT void celix_bundleContext_waitForAsyncStopTracker(celix_bundle_context_t *ctx, long trackerId); + +/** + * @brief Stop the tracker with the provided track id. + * + * Could be a service tracker, bundle tracker or service tracker tracker. + * Only works for the trackers owned by the bundle of the bundle context. + * Note: Please use the celix_bundleContext_registerServiceFactoryAsync instead. + * + * Will log a error if the provided tracker id is unknown. Will silently ignore trackerId < 0. + */ +CELIX_FRAMEWORK_EXPORT void celix_bundleContext_stopTracker(celix_bundle_context_t *ctx, long trackerId); + +/** + * @brief Tracker guard. + */ +typedef struct celix_tracker_guard { + celix_bundle_context_t* ctx; + long trackerId; +} celix_tracker_guard_t; + +/** + * @brief Initialize a scope guard for an existing bundle, service or meta tracker. + * @param [in] ctx The bundle context associated with the service registration. + * @param [in] trackerId The tracker id. + * @return An initialized service registration guard. + */ +static CELIX_UNUSED inline celix_tracker_guard_t +celix_trackerGuard_init(celix_bundle_context_t* ctx, long trackerId) { + return (celix_tracker_guard_t) { .ctx = ctx, .trackerId = trackerId }; +} + +/** + * @brief De-initialize a tracker guard. + * Will stop the tracker if the tracker id is >= 0. + * @param [in] trackerGuard A tracker guard + */ +static CELIX_UNUSED inline void celix_trackerGuard_deinit(celix_tracker_guard_t* trackerGuard) { + if (trackerGuard->trackerId >= 0) { + celix_bundleContext_stopTracker(trackerGuard->ctx, trackerGuard->trackerId); + } +} + +CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_tracker_guard_t, celix_trackerGuard_deinit) /** * @brief List the installed and started bundle ids. diff --git a/libs/framework/include_deprecated/service_tracker.h b/libs/framework/include_deprecated/service_tracker.h index 5f75c8abf..cba45a098 100644 --- a/libs/framework/include_deprecated/service_tracker.h +++ b/libs/framework/include_deprecated/service_tracker.h @@ -66,7 +66,7 @@ CELIX_FRAMEWORK_DEPRECATED_EXPORT size_t serviceTracker_nrOfTrackedServices(serv /********************************************************************************************************************** ********************************************************************************************************************** - * Updated API + * Updated API. Note API is still deprecated for external usage, the api will become private in the future. ********************************************************************************************************************** **********************************************************************************************************************/ @@ -140,6 +140,21 @@ CELIX_FRAMEWORK_DEPRECATED_EXPORT size_t celix_serviceTracker_useServices( void (*useWithOwner)(void *handle, void *svc, const celix_properties_t *props, const celix_bundle_t *owner) ); +/** + * @brief Get the number of tracked services. + */ +CELIX_FRAMEWORK_DEPRECATED_EXPORT size_t celix_serviceTracker_getTrackedServiceCount(celix_service_tracker_t *tracker); + +/** + * @brief Get the tracker service name. + */ +CELIX_FRAMEWORK_DEPRECATED_EXPORT const char* celix_serviceTracker_getTrackedServiceName(celix_service_tracker_t *tracker); + +/** + * @brief Get the tracker filter. + */ +CELIX_FRAMEWORK_DEPRECATED_EXPORT const char* celix_serviceTracker_getTrackedServiceFilter(celix_service_tracker_t *tracker); + #ifdef __cplusplus } diff --git a/libs/framework/src/bundle.c b/libs/framework/src/bundle.c index 6a687696a..8eaf7112e 100644 --- a/libs/framework/src/bundle.c +++ b/libs/framework/src/bundle.c @@ -583,9 +583,8 @@ void celix_bundle_destroyRegisteredServicesList(celix_array_list_t* list) { celix_array_list_t* celix_bundle_listServiceTrackers(const celix_bundle_t *bnd) { celix_array_list_t* result = celix_arrayList_create(); celixThreadMutex_lock(&bnd->context->mutex); - hash_map_iterator_t iter = hashMapIterator_construct(bnd->context->serviceTrackers); - while (hashMapIterator_hasNext(&iter)) { - celix_bundle_context_service_tracker_entry_t *trkEntry = hashMapIterator_nextValue(&iter); + CELIX_LONG_HASH_MAP_ITERATE(bnd->context->serviceTrackers, iter) { + celix_bundle_context_service_tracker_entry_t *trkEntry = iter.value.ptrValue; if (trkEntry->tracker != NULL) { celix_bundle_service_tracker_list_entry_t *entry = calloc(1, sizeof(*entry)); entry->filter = celix_utils_strdup(trkEntry->tracker->filter); diff --git a/libs/framework/src/bundle_context.c b/libs/framework/src/bundle_context.c index 0f0bc3441..ccc49ddf0 100644 --- a/libs/framework/src/bundle_context.c +++ b/libs/framework/src/bundle_context.c @@ -68,10 +68,10 @@ celix_status_t bundleContext_create(framework_pt framework, celix_framework_logg celixThreadMutex_create(&context->mutex, NULL); context->svcRegistrations = celix_arrayList_create(); - context->bundleTrackers = hashMap_create(NULL,NULL,NULL,NULL); - context->serviceTrackers = hashMap_create(NULL,NULL,NULL,NULL); - context->metaTrackers = hashMap_create(NULL,NULL,NULL,NULL); - context->stoppingTrackerEventIds = hashMap_create(NULL,NULL,NULL,NULL); + context->bundleTrackers = celix_longHashMap_create(); + context->serviceTrackers = celix_longHashMap_create(); + context->metaTrackers = celix_longHashMap_create(); + context->stoppingTrackerEventIds = celix_longHashMap_create(); context->nextTrackerId = 1L; *bundle_context = context; @@ -84,20 +84,19 @@ celix_status_t bundleContext_create(framework_pt framework, celix_framework_logg } celix_status_t bundleContext_destroy(bundle_context_pt context) { - celix_status_t status = CELIX_SUCCESS; - - if(context == NULL) { + if (context == NULL) { return CELIX_ILLEGAL_ARGUMENT; } - assert(hashMap_size(context->bundleTrackers) == 0); - hashMap_destroy(context->bundleTrackers, false, false); - assert(hashMap_size(context->serviceTrackers) == 0); - hashMap_destroy(context->serviceTrackers, false, false); - assert(hashMap_size(context->metaTrackers) == 0); - hashMap_destroy(context->metaTrackers, false, false); + + assert(celix_longHashMap_size(context->bundleTrackers) == 0); + celix_longHashMap_destroy(context->bundleTrackers); + assert(celix_longHashMap_size(context->serviceTrackers) == 0); + celix_longHashMap_destroy(context->serviceTrackers); + assert(celix_longHashMap_size(context->metaTrackers) == 0); + celix_longHashMap_destroy(context->metaTrackers); assert(celix_arrayList_size(context->svcRegistrations) == 0); celix_arrayList_destroy(context->svcRegistrations); - hashMap_destroy(context->stoppingTrackerEventIds, false, false); + celix_longHashMap_destroy(context->stoppingTrackerEventIds); celixThreadMutex_destroy(&context->mutex); @@ -108,7 +107,7 @@ celix_status_t bundleContext_destroy(bundle_context_pt context) { } free(context); - return status; + return CELIX_SUCCESS; } void celix_bundleContext_cleanup(celix_bundle_context_t* ctx) { @@ -593,7 +592,7 @@ static long celix_bundleContext_trackBundlesWithOptionsInternal( celixThreadMutex_lock(&ctx->mutex); entry->trackerId = ctx->nextTrackerId++; - hashMap_put(ctx->bundleTrackers, (void*)(entry->trackerId), entry); + celix_longHashMap_put(ctx->bundleTrackers, entry->trackerId, entry); long trackerId = entry->trackerId; celixThreadMutex_unlock(&ctx->mutex); @@ -670,20 +669,24 @@ bool celix_bundleContext_useBundle( return celix_framework_useBundle(ctx->framework, false, bundleId, callbackHandle, use); } -static void bundleContext_cleanupBundleTrackers(bundle_context_t *ctx) { +static void bundleContext_cleanupBundleTrackers(bundle_context_t* ctx) { module_pt module; - const char *symbolicName; + const char* symbolicName; bundle_getCurrentModule(ctx->bundle, &module); module_getSymbolicName(module, &symbolicName); celix_array_list_t* danglingTrkIds = NULL; celixThreadMutex_lock(&ctx->mutex); - hash_map_iterator_t iter = hashMapIterator_construct(ctx->bundleTrackers); - while (hashMapIterator_hasNext(&iter)) { - long trkId = (long)hashMapIterator_nextKey(&iter); - fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Dangling bundle tracker with id %li for bundle %s. Add missing 'celix_bundleContext_stopTracker' calls.", trkId, symbolicName); - if (danglingTrkIds == NULL) { + CELIX_LONG_HASH_MAP_ITERATE(ctx->bundleTrackers, iter) { + long trkId = iter.key; + fw_log( + ctx->framework->logger, + CELIX_LOG_LEVEL_ERROR, + "Dangling bundle tracker with id %li for bundle %s. Add missing 'celix_bundleContext_stopTracker' calls.", + trkId, + symbolicName); + if (!danglingTrkIds) { danglingTrkIds = celix_arrayList_create(); } celix_arrayList_addLong(danglingTrkIds, trkId); @@ -699,20 +702,25 @@ static void bundleContext_cleanupBundleTrackers(bundle_context_t *ctx) { } } -static void bundleContext_cleanupServiceTrackers(bundle_context_t *ctx) { +static void bundleContext_cleanupServiceTrackers(bundle_context_t* ctx) { module_pt module; - const char *symbolicName; + const char* symbolicName; bundle_getCurrentModule(ctx->bundle, &module); module_getSymbolicName(module, &symbolicName); celix_array_list_t* danglingTrkIds = NULL; celixThreadMutex_lock(&ctx->mutex); - hash_map_iterator_t iter = hashMapIterator_construct(ctx->serviceTrackers); - while (hashMapIterator_hasNext(&iter)) { - long trkId = (long)hashMapIterator_nextKey(&iter); - celix_bundle_context_service_tracker_entry_t* entry = hashMap_get(ctx->serviceTrackers, (void*)trkId); - fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Dangling service tracker with trkId %li, for bundle %s and with filter %s. Add missing 'celix_bundleContext_stopTracker' calls.", trkId, symbolicName, entry->tracker->filter); + CELIX_LONG_HASH_MAP_ITERATE(ctx->serviceTrackers, iter) { + long trkId = iter.key; + celix_bundle_context_service_tracker_entry_t* entry = celix_longHashMap_get(ctx->serviceTrackers, trkId); + fw_log(ctx->framework->logger, + CELIX_LOG_LEVEL_ERROR, + "Dangling service tracker with trkId %li, for bundle %s and with filter %s. Add missing " + "'celix_bundleContext_stopTracker' calls.", + trkId, + symbolicName, + entry->tracker->filter); if (danglingTrkIds == NULL) { danglingTrkIds = celix_arrayList_create(); } @@ -729,20 +737,25 @@ static void bundleContext_cleanupServiceTrackers(bundle_context_t *ctx) { } } -static void bundleContext_cleanupServiceTrackerTrackers(bundle_context_t *ctx) { +static void bundleContext_cleanupServiceTrackerTrackers(bundle_context_t* ctx) { module_pt module; - const char *symbolicName; + const char* symbolicName; bundle_getCurrentModule(ctx->bundle, &module); module_getSymbolicName(module, &symbolicName); celix_array_list_t* danglingTrkIds = NULL; celixThreadMutex_lock(&ctx->mutex); - hash_map_iterator_t iter = hashMapIterator_construct(ctx->metaTrackers); - while (hashMapIterator_hasNext(&iter)) { - long trkId = (long)hashMapIterator_nextKey(&iter); - celix_bundle_context_service_tracker_tracker_entry_t *entry = hashMap_get(ctx->metaTrackers, (void*)trkId); - fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Dangling meta tracker (service tracker tracker) with trkId %li, for bundle %s and for the services %s. Add missing 'celix_bundleContext_stopTracker' calls.", trkId, symbolicName, entry->serviceName); + CELIX_LONG_HASH_MAP_ITERATE(ctx->metaTrackers, iter) { + long trkId = iter.key; + celix_bundle_context_service_tracker_tracker_entry_t* entry = celix_longHashMap_get(ctx->metaTrackers, trkId); + fw_log(ctx->framework->logger, + CELIX_LOG_LEVEL_ERROR, + "Dangling meta tracker (service tracker tracker) with trkId %li, for bundle %s and for the services %s. " + "Add missing 'celix_bundleContext_stopTracker' calls.", + trkId, + symbolicName, + entry->serviceName); if (danglingTrkIds == NULL) { danglingTrkIds = celix_arrayList_create(); } @@ -791,7 +804,7 @@ static void celix_bundleContext_removeBundleTracker(void *data) { celix_bundle_context_bundle_tracker_entry_t *tracker = data; fw_removeBundleListener(tracker->ctx->framework, tracker->ctx->bundle, &tracker->listener); celixThreadMutex_lock(&tracker->ctx->mutex); - hashMap_remove(tracker->ctx->stoppingTrackerEventIds, (void*)tracker->trackerId); + celix_longHashMap_remove(tracker->ctx->stoppingTrackerEventIds, tracker->trackerId); celixThreadMutex_unlock(&tracker->ctx->mutex); free(tracker); } @@ -800,7 +813,7 @@ static void celix_bundleContext_removeServiceTracker(void *data) { celix_bundle_context_service_tracker_entry_t *tracker = data; celix_serviceTracker_destroy(tracker->tracker); celixThreadMutex_lock(&tracker->ctx->mutex); - hashMap_remove(tracker->ctx->stoppingTrackerEventIds, (void*)tracker->trackerId); + celix_longHashMap_remove(tracker->ctx->stoppingTrackerEventIds, tracker->trackerId); celixThreadMutex_unlock(&tracker->ctx->mutex); if (tracker->isFreeFilterNeeded) { free((char*)tracker->opts.filter.serviceName); @@ -814,7 +827,7 @@ static void celix_bundleContext_removeServiceTrackerTracker(void *data) { celix_bundle_context_service_tracker_tracker_entry_t *tracker = data; celix_framework_unregister(tracker->ctx->framework, tracker->ctx->bundle, tracker->serviceId); celixThreadMutex_lock(&tracker->ctx->mutex); - hashMap_remove(tracker->ctx->stoppingTrackerEventIds, (void*)tracker->trackerId); + celix_longHashMap_remove(tracker->ctx->stoppingTrackerEventIds, tracker->trackerId); celixThreadMutex_unlock(&tracker->ctx->mutex); free(tracker->serviceName); free(tracker); @@ -833,25 +846,28 @@ static void celix_bundleContext_stopTrackerInternal(bundle_context_t *ctx, long celixThreadMutex_lock(&ctx->mutex); - if (hashMap_containsKey(ctx->bundleTrackers, (void *) trackerId)) { + if (celix_longHashMap_hasKey(ctx->bundleTrackers, trackerId)) { found = true; - bundleTracker = hashMap_remove(ctx->bundleTrackers, (void *) trackerId); + bundleTracker = celix_longHashMap_get(ctx->bundleTrackers, trackerId); + (void)celix_longHashMap_remove(ctx->bundleTrackers, trackerId); if (!bundleTracker->created && !async) { //note tracker not yet created, so cancel instead of removing bundleTracker->cancelled = true; cancelled = true; } - } else if (hashMap_containsKey(ctx->serviceTrackers, (void *) trackerId)) { + } else if (celix_longHashMap_hasKey(ctx->serviceTrackers, trackerId)) { found = true; - serviceTracker = hashMap_remove(ctx->serviceTrackers, (void *) trackerId); + serviceTracker = celix_longHashMap_get(ctx->serviceTrackers, trackerId); + (void)celix_longHashMap_remove(ctx->serviceTrackers, trackerId); if (serviceTracker->tracker == NULL && !async) { //note tracker not yet created, so cancel instead of removing serviceTracker->cancelled = true; cancelled = true; } - } else if (hashMap_containsKey(ctx->metaTrackers, (void *) trackerId)) { + } else if (celix_longHashMap_hasKey(ctx->metaTrackers, trackerId)) { found = true; - svcTrackerTracker = hashMap_remove(ctx->metaTrackers, (void *) trackerId); + svcTrackerTracker = celix_longHashMap_get(ctx->metaTrackers, trackerId); + (void)celix_longHashMap_remove(ctx->metaTrackers, trackerId); //note because a meta tracker is a service listener hook under waiter, no additional cancel is needed (svc reg will be cancelled) } @@ -885,7 +901,7 @@ static void celix_bundleContext_stopTrackerInternal(bundle_context_t *ctx, long } else if (found && async) { //NOTE: for async stopping of tracking we need to ensure we cant wait for the tracker destroy id event. long eventId = celix_framework_nextEventId(ctx->framework); - hashMap_put(ctx->stoppingTrackerEventIds, (void*)trackerId, (void*)eventId); + celix_longHashMap_put(ctx->stoppingTrackerEventIds, trackerId, (void*)eventId); if (bundleTracker != NULL) { celix_framework_fireGenericEvent(ctx->framework, eventId, celix_bundle_getId(ctx->bundle), "stop tracker", bundleTracker, celix_bundleContext_removeBundleTracker, doneData, doneCallback); @@ -937,25 +953,25 @@ static void celix_bundleContext_waitForTrackerInternal(celix_bundle_context_t* c if (waitForStart) { celixThreadMutex_lock(&ctx->mutex); - if (hashMap_containsKey(ctx->bundleTrackers, (void *) trackerId)) { + if (celix_longHashMap_hasKey(ctx->bundleTrackers, trackerId)) { found = true; - celix_bundle_context_bundle_tracker_entry_t* bundleTracker = hashMap_get(ctx->bundleTrackers, (void *) trackerId); + celix_bundle_context_bundle_tracker_entry_t* bundleTracker = celix_longHashMap_get(ctx->bundleTrackers, trackerId); eventId = bundleTracker->createEventId; - } else if (hashMap_containsKey(ctx->serviceTrackers, (void *) trackerId)) { + } else if (celix_longHashMap_hasKey(ctx->serviceTrackers, trackerId)) { found = true; - celix_bundle_context_service_tracker_entry_t* serviceTracker = hashMap_get(ctx->serviceTrackers, (void *) trackerId); + celix_bundle_context_service_tracker_entry_t* serviceTracker = celix_longHashMap_get(ctx->serviceTrackers, trackerId); eventId = serviceTracker->createEventId; - } else if (hashMap_containsKey(ctx->metaTrackers, (void *) trackerId)) { + } else if (celix_longHashMap_hasKey(ctx->metaTrackers, trackerId)) { found = true; - celix_bundle_context_service_tracker_tracker_entry_t* svcTrackerTracker = hashMap_get(ctx->metaTrackers, (void *) trackerId); + celix_bundle_context_service_tracker_tracker_entry_t* svcTrackerTracker = celix_longHashMap_get(ctx->metaTrackers, trackerId); svcId = svcTrackerTracker->serviceId; } celixThreadMutex_unlock(&ctx->mutex); } else { celixThreadMutex_lock(&ctx->mutex); - if (hashMap_containsKey(ctx->stoppingTrackerEventIds, (void*)trackerId)) { + if (celix_longHashMap_hasKey(ctx->stoppingTrackerEventIds, trackerId)) { found = true; - eventId = (long)hashMap_get(ctx->stoppingTrackerEventIds, (void*)trackerId); + eventId = celix_longHashMap_getLong(ctx->stoppingTrackerEventIds, trackerId, -1); } celixThreadMutex_unlock(&ctx->mutex); } @@ -1310,7 +1326,7 @@ static long celix_bundleContext_trackServicesWithOptionsInternal(celix_bundle_co celixThreadMutex_lock(&ctx->mutex); entry->trackerId = ctx->nextTrackerId++; trackerId = entry->trackerId; - hashMap_put(ctx->serviceTrackers, (void *) trackerId, entry); + celix_longHashMap_put(ctx->serviceTrackers, trackerId, entry); celixThreadMutex_unlock(&ctx->mutex); } return trackerId; @@ -1334,7 +1350,7 @@ static long celix_bundleContext_trackServicesWithOptionsInternal(celix_bundle_co celixThreadMutex_lock(&ctx->mutex); entry->trackerId = ctx->nextTrackerId++; long trackerId = entry->trackerId; - hashMap_put(ctx->serviceTrackers, (void *)entry->trackerId, entry); + celix_longHashMap_put(ctx->serviceTrackers, entry->trackerId, entry); celixThreadMutex_unlock(&ctx->mutex); long id = celix_framework_fireGenericEvent(ctx->framework, entry->createEventId, celix_bundle_getId(ctx->bundle), "create service tracker event", entry, celix_bundleContext_createTrackerOnEventLoop, entry, celix_bundleContext_doneCreatingTrackerOnEventLoop); @@ -1355,6 +1371,135 @@ long celix_bundleContext_trackServicesWithOptionsAsync(celix_bundle_context_t *c return celix_bundleContext_trackServicesWithOptionsInternal(ctx, opts, true); } +bool celix_bundleContext_useTrackedService( + celix_bundle_context_t *ctx, + long trackerId, + void *callbackHandle, + void (*use)(void *handle, void* svc) +) { + celix_tracked_service_use_options_t opts = CELIX_EMPTY_TRACKER_SERVICE_USE_OPTIONS; + opts.callbackHandle = callbackHandle; + opts.use = use; + return celix_bundleContext_useTrackedServiceWithOptions(ctx, trackerId, &opts); +} + +size_t celix_bundleContext_useTrackedServices(celix_bundle_context_t* ctx, + long trackerId, + void* callbackHandle, + void (*use)(void* handle, void* svc)) { + celix_tracked_service_use_options_t opts = CELIX_EMPTY_TRACKER_SERVICE_USE_OPTIONS; + opts.callbackHandle = callbackHandle; + opts.use = use; + return celix_bundleContext_useTrackedServicesWithOptions(ctx, trackerId, &opts); +} + +/** + * @brief Find a service tracker with the given tracker id. ctx->mutex must be locked. + */ +static celix_service_tracker_t* celix_bundleContext_findServiceTracker(celix_bundle_context_t* ctx, long trackerId) { + if (trackerId < 0) { + return NULL; //silent ignore + } + + celix_bundle_context_service_tracker_entry_t* entry = celix_longHashMap_get(ctx->serviceTrackers, trackerId); + if (!entry) { + fw_log(ctx->framework->logger, + CELIX_LOG_LEVEL_ERROR, + "Cannot use tracked service with tracker id %li, because no tracker with that id is found", + trackerId); + return NULL; + } + return entry->tracker; +} + +static size_t celix_bundleContext_useTrackedServiceWithOptionsInternal(celix_bundle_context_t* ctx, + long trackerId, + const celix_tracked_service_use_options_t* opts, + bool singleUse) { + celix_auto(celix_mutex_lock_guard_t) lck = celixMutexLockGuard_init(&ctx->mutex); + celix_service_tracker_t* trk = celix_bundleContext_findServiceTracker(ctx, trackerId); + if (!trk) { + return 0; + } + + if (singleUse) { + bool called = celix_serviceTracker_useHighestRankingService(trk, + NULL, + opts->waitTimeoutInSeconds, + opts->callbackHandle, + opts->use, + opts->useWithProperties, + opts->useWithOwner); + return called ? 1 : 0; + } else { + return celix_serviceTracker_useServices( + trk, NULL, opts->callbackHandle, opts->use, opts->useWithProperties, opts->useWithOwner); + } +} + +bool celix_bundleContext_useTrackedServiceWithOptions(celix_bundle_context_t* ctx, + long trackerId, + const celix_tracked_service_use_options_t* opts) { + return celix_bundleContext_useTrackedServiceWithOptionsInternal(ctx, trackerId, opts, true) > 0; +} + +size_t celix_bundleContext_useTrackedServicesWithOptions(celix_bundle_context_t* ctx, + long trackerId, + const celix_tracked_service_use_options_t* opts) { + return celix_bundleContext_useTrackedServiceWithOptionsInternal(ctx, trackerId, opts, false); +} + +void celix_bundleContext_getTrackerInfo(celix_bundle_context_t *ctx, long trackerId, size_t *trackedServiceCount, const char **trackedServiceName, const char **trackedServiceFilter) { + if (trackedServiceCount) { + *trackedServiceCount = 0; + } + if (trackedServiceName) { + *trackedServiceName = NULL; + } + if (trackedServiceFilter) { + *trackedServiceFilter = NULL; + } + + celix_auto(celix_mutex_lock_guard_t) lck = celixMutexLockGuard_init(&ctx->mutex); + celix_service_tracker_t* trk = celix_bundleContext_findServiceTracker(ctx, trackerId); + if (!trk) { + return; + } + + if (trackedServiceCount) { + *trackedServiceCount = celix_serviceTracker_getTrackedServiceCount(trk); + } + if (trackedServiceName) { + *trackedServiceName = celix_serviceTracker_getTrackedServiceName(trk); + } + if (trackedServiceFilter) { + *trackedServiceFilter = celix_serviceTracker_getTrackedServiceFilter(trk); + } +} + +size_t celix_bundleContext_getTrackedServiceCount(celix_bundle_context_t *ctx, long trackerId) { + size_t result = 0; + celix_bundleContext_getTrackerInfo(ctx, trackerId, &result, NULL, NULL); + return result; +} + +const char* celix_bundleContext_getTrackedServiceName(celix_bundle_context_t *ctx, long trackerId) { + const char* result = NULL; + celix_bundleContext_getTrackerInfo(ctx, trackerId, NULL, &result, NULL); + return result; +} + +const char* celix_bundleContext_getTrackedServiceFilter(celix_bundle_context_t* ctx, long trackerId) { + const char* result = NULL; + celix_bundleContext_getTrackerInfo(ctx, trackerId, NULL, NULL, &result); + return result; +} + +bool celix_bundleContext_isValidTrackerId(celix_bundle_context_t* ctx, long trackerId) { + celix_auto(celix_mutex_lock_guard_t) lck = celixMutexLockGuard_init(&ctx->mutex); + return celix_longHashMap_hasKey(ctx->serviceTrackers, trackerId); +} + long celix_bundleContext_findService(celix_bundle_context_t *ctx, const char *serviceName) { celix_service_filter_options_t opts = CELIX_EMPTY_SERVICE_FILTER_OPTIONS; opts.serviceName = serviceName; @@ -1471,7 +1616,7 @@ long celix_bundleContext_trackServiceTrackersInternal( if (entry->serviceId >= 0) { celixThreadMutex_lock(&ctx->mutex); - hashMap_put(ctx->metaTrackers, (void*)entry->trackerId, entry); + celix_longHashMap_put(ctx->metaTrackers, entry->trackerId, entry); long trkId = entry->trackerId; celixThreadMutex_unlock(&ctx->mutex); return trkId; diff --git a/libs/framework/src/bundle_context_private.h b/libs/framework/src/bundle_context_private.h index 38482d1d6..13c3f350d 100644 --- a/libs/framework/src/bundle_context_private.h +++ b/libs/framework/src/bundle_context_private.h @@ -20,13 +20,15 @@ #ifndef BUNDLE_CONTEXT_PRIVATE_H_ #define BUNDLE_CONTEXT_PRIVATE_H_ +#include + #include "bundle_context.h" #include "bundle_listener.h" #include "celix_bundle_context.h" #include "celix_log.h" +#include "celix_long_hash_map.h" #include "listener_hook_service.h" #include "service_tracker.h" -#include #ifdef __cplusplus extern "C" { @@ -76,17 +78,20 @@ typedef struct celix_bundle_context_service_tracker_tracker_entry { } celix_bundle_context_service_tracker_tracker_entry_t; struct celix_bundle_context { - celix_framework_t *framework; - celix_bundle_t *bundle; - - celix_thread_mutex_t mutex; //protects fields below (NOTE/FIXME also used by bundle.c for listing service tracker usage) - celix_array_list_t *svcRegistrations; //serviceIds - celix_dependency_manager_t *mng; - long nextTrackerId; - hash_map_t *bundleTrackers; //key = trackerId, value = celix_bundle_context_bundle_tracker_entry_t* - hash_map_t *serviceTrackers; //key = trackerId, value = celix_bundle_context_service_tracker_entry_t* - hash_map_t *metaTrackers; //key = trackerId, value = celix_bundle_context_service_tracker_tracker_entry_t* - hash_map_t *stoppingTrackerEventIds; //key = trackerId, value = eventId for stopping the tracker. Note id are only present if the stop tracking is queued. + celix_framework_t* framework; + celix_bundle_t* bundle; + + celix_thread_mutex_t + mutex; // protects fields below (NOTE/FIXME also used by bundle.c for listing service tracker usage) + celix_array_list_t* svcRegistrations; // serviceIds + celix_dependency_manager_t* mng; + long nextTrackerId; + celix_long_hash_map_t* bundleTrackers; // key = trackerId, value = celix_bundle_context_bundle_tracker_entry_t* + celix_long_hash_map_t* serviceTrackers; // key = trackerId, value = celix_bundle_context_service_tracker_entry_t* + celix_long_hash_map_t* + metaTrackers; // key = trackerId, value = celix_bundle_context_service_tracker_tracker_entry_t* + celix_long_hash_map_t* stoppingTrackerEventIds; // key = trackerId, value = eventId for stopping the tracker. Note + // id are only present if the stop tracking is queued. }; /** diff --git a/libs/framework/src/service_tracker.c b/libs/framework/src/service_tracker.c index 481bcfced..d3bbbb21d 100644 --- a/libs/framework/src/service_tracker.c +++ b/libs/framework/src/service_tracker.c @@ -712,20 +712,19 @@ void celix_serviceTracker_destroy(celix_service_tracker_t *tracker) { } } -static celix_tracked_entry_t* celix_serviceTracker_findHighestRankingService(service_tracker_t *tracker, const char* serviceName) { - //precondition tracker->mutex locked +static celix_tracked_entry_t* celix_serviceTracker_findHighestRankingService(service_tracker_t* tracker, + const char* serviceName) { + // precondition tracker->mutex locked celix_tracked_entry_t* highest = NULL; for (int i = 0; i < celix_arrayList_size(tracker->trackedServices); ++i) { celix_tracked_entry_t* tracked = celix_arrayList_get(tracker->trackedServices, i); - if (serviceName == NULL || (serviceName != NULL && tracked->serviceName != NULL && - celix_utils_stringEquals(tracked->serviceName, serviceName))) { + if (serviceName == NULL || + (tracked->serviceName != NULL && celix_utils_stringEquals(tracked->serviceName, serviceName))) { if (highest == NULL) { highest = tracked; } else { int compare = celix_utils_compareServiceIdsAndRanking( - tracked->serviceId, tracked->serviceRanking, - highest->serviceId, highest->serviceRanking - ); + tracked->serviceId, tracked->serviceRanking, highest->serviceId, highest->serviceRanking); if (compare < 0) { highest = tracked; } @@ -817,4 +816,19 @@ size_t celix_serviceTracker_useServices( tracked_release(entry); } return count; -} \ No newline at end of file +} + +size_t celix_serviceTracker_getTrackedServiceCount(celix_service_tracker_t *tracker) { + celix_auto(celix_mutex_lock_guard_t) lck = celixMutexLockGuard_init(&tracker->mutex); + return (size_t) celix_arrayList_size(tracker->trackedServices); +} + +const char* celix_serviceTracker_getTrackedServiceName(celix_service_tracker_t *tracker) { + celix_auto(celix_mutex_lock_guard_t) lck = celixMutexLockGuard_init(&tracker->mutex); + return tracker->serviceName; +} + +const char* celix_serviceTracker_getTrackedServiceFilter(celix_service_tracker_t *tracker) { + celix_auto(celix_mutex_lock_guard_t) lck = celixMutexLockGuard_init(&tracker->mutex); + return tracker->filter; +} From 7b37d720a06b756b3a07576de8708bc400afd460 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 11 Feb 2024 15:28:06 +0100 Subject: [PATCH 02/14] gh-87: Change signature celix_bundleContext_trackServices Also update the celix_bundleContext_trackServices usage. --- CHANGES.md | 3 + .../log_admin/gtest/src/LogAdminTestSuite.cc | 6 +- .../shell/shell/gtest/src/ShellTestSuite.cc | 9 +- .../src/simple_consumer_example.c | 7 +- .../track_tracker_example/src/activator.c | 23 +-- .../src/CelixBundleContextBundlesTestSuite.cc | 2 +- .../CelixBundleContextServicesTestSuite.cc | 142 +++++++++++++----- libs/framework/include/celix_bundle_context.h | 114 ++++++-------- .../include_deprecated/service_tracker.h | 9 +- libs/framework/src/bundle_context.c | 76 +++++----- libs/framework/src/bundle_context_private.h | 32 ++-- 11 files changed, 238 insertions(+), 185 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4dd00c48d..416ca2300 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -58,6 +58,9 @@ limitations under the License. - linked_list.h is removed and no longer supported. Use celix_array_list.h instead. - ip_utils.h is removed and no longer supported. - array_list.h is removed and no longer supported. Use celix_array_list.h instead. +- The signature of `celix_bundleContext_trackServices` has changed. The signature is now simpler to better support + the use-case of using a service tracker with the `celix_bundleContext_useTrackedService*` functions. + The `celix_bundleContext_trackServicesWithOptions` is still available for more advanced use-cases. ## New Features diff --git a/bundles/logging/log_admin/gtest/src/LogAdminTestSuite.cc b/bundles/logging/log_admin/gtest/src/LogAdminTestSuite.cc index 859a24a72..606b07ecf 100644 --- a/bundles/logging/log_admin/gtest/src/LogAdminTestSuite.cc +++ b/bundles/logging/log_admin/gtest/src/LogAdminTestSuite.cc @@ -84,11 +84,11 @@ TEST_F(LogBundleTestSuite, NrOfLogServices) { EXPECT_EQ(1, control->nrOfLogServices(control->handle, nullptr)); //default the framework log services is available //request "default" log service - long trkId1 = celix_bundleContext_trackService(ctx.get(), CELIX_LOG_SERVICE_NAME, nullptr, nullptr); + long trkId1 = celix_bundleContext_trackService(ctx.get(), CELIX_LOG_SERVICE_NAME); EXPECT_EQ(2, control->nrOfLogServices(control->handle, nullptr)); //request "default" log service -> already created - long trkId2 = celix_bundleContext_trackService(ctx.get(), CELIX_LOG_SERVICE_NAME, nullptr, nullptr); + long trkId2 = celix_bundleContext_trackService(ctx.get(), CELIX_LOG_SERVICE_NAME); EXPECT_EQ(2, control->nrOfLogServices(control->handle, nullptr)); //request a 'logger1' log service @@ -225,7 +225,7 @@ TEST_F(LogBundleTestSuite, SinkLogControl) { TEST_F(LogBundleTestSuite, LogServiceControl) { //request "default" log service - long trkId1 = celix_bundleContext_trackService(ctx.get(), CELIX_LOG_SERVICE_NAME, nullptr, nullptr); + long trkId1 = celix_bundleContext_trackService(ctx.get(), CELIX_LOG_SERVICE_NAME); celix_framework_waitForEmptyEventQueue(fw.get()); EXPECT_EQ(2, control->nrOfLogServices(control->handle, nullptr)); diff --git a/bundles/shell/shell/gtest/src/ShellTestSuite.cc b/bundles/shell/shell/gtest/src/ShellTestSuite.cc index e9ffc53f9..318a83790 100644 --- a/bundles/shell/shell/gtest/src/ShellTestSuite.cc +++ b/bundles/shell/shell/gtest/src/ShellTestSuite.cc @@ -77,8 +77,10 @@ static void callCommand(std::shared_ptr& ctx, const char data.cmdLine = cmdLine; data.cmdShouldSucceed = cmdShouldSucceed; data.context = ctx.get(); - data.tracker = celix_bundleContext_trackService(ctx.get(), CELIX_SHELL_SERVICE_NAME, - static_cast(&data), [](void * handle, void * svc) { + celix_service_tracking_options_t opts{}; + opts.filter.serviceName = CELIX_SHELL_SERVICE_NAME; + opts.callbackHandle = &data; + opts.set = [](void * handle, void * svc) { if (svc == nullptr) { return; } @@ -93,7 +95,8 @@ static void callCommand(std::shared_ptr& ctx, const char } celix_bundleContext_stopTracker(d->context, d->tracker); d->barrier.set_value(); - }); + }; + data.tracker = celix_bundleContext_trackServicesWithOptions(ctx.get(), &opts); data.barrier.get_future().wait(); } diff --git a/examples/celix-examples/services_example_c/src/simple_consumer_example.c b/examples/celix-examples/services_example_c/src/simple_consumer_example.c index 51a9fb68b..6d9b5b0ae 100644 --- a/examples/celix-examples/services_example_c/src/simple_consumer_example.c +++ b/examples/celix-examples/services_example_c/src/simple_consumer_example.c @@ -54,7 +54,12 @@ static celix_status_t activator_start(activator_data_t *data, celix_bundle_conte data->trkId = -1L; printf("Starting service tracker\n"); - data->trkId = celix_bundleContext_trackServices(data->ctx, EXAMPLE_CALC_NAME, data, (void*)addSvc, (void*)removeSvc); + celix_service_tracking_options_t opts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS; + opts.filter.serviceName = EXAMPLE_CALC_NAME; + opts.callbackHandle = data; + opts.addWithProperties = (void*)addSvc; + opts.remove = (void*)removeSvc; + data->trkId = celix_bundleContext_trackServicesWithOptions(data->ctx, &opts); printf("Trying to use calc service\n"); celix_bundleContext_useService(data->ctx, EXAMPLE_CALC_NAME, data, (void*)useCalc); diff --git a/examples/celix-examples/track_tracker_example/src/activator.c b/examples/celix-examples/track_tracker_example/src/activator.c index effefccd0..7b10b947e 100644 --- a/examples/celix-examples/track_tracker_example/src/activator.c +++ b/examples/celix-examples/track_tracker_example/src/activator.c @@ -74,15 +74,20 @@ celix_status_t activator_start(activator_data_t* act, celix_bundle_context_t *ct act->trackerId = celix_bundleContext_trackServiceTrackers(ctx, CALC_SERVICE_NAME, act, addCalcTracker, removeCalcTracker); - act->calcTrk1 = celix_bundleContext_trackServices(ctx, CALC_SERVICE_NAME, act, addCalcSvc, removeCalcSvc); - - celix_service_tracking_options_t opts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS; - opts.filter.serviceName = CALC_SERVICE_NAME; - opts.filter.filter = "(&(prop1=val1)(prop2=val2))"; - opts.callbackHandle = act; - opts.add = addCalcSvc; - opts.remove = removeCalcSvc; - act->calcTrk2 = celix_bundleContext_trackServicesWithOptions(ctx, &opts); + celix_service_tracking_options_t opts1 = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS; + opts1.filter.serviceName = CALC_SERVICE_NAME; + opts1.callbackHandle = act; + opts1.add = addCalcSvc; + opts1.remove = removeCalcSvc; + act->calcTrk1 = celix_bundleContext_trackServicesWithOptions(ctx, &opts1); + + celix_service_tracking_options_t opts2 = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS; + opts2.filter.serviceName = CALC_SERVICE_NAME; + opts2.filter.filter = "(&(prop1=val1)(prop2=val2))"; + opts2.callbackHandle = act; + opts2.add = addCalcSvc; + opts2.remove = removeCalcSvc; + act->calcTrk2 = celix_bundleContext_trackServicesWithOptions(ctx, &opts2); act->svc.handle = act; act->svc.calc = calc; diff --git a/libs/framework/gtest/src/CelixBundleContextBundlesTestSuite.cc b/libs/framework/gtest/src/CelixBundleContextBundlesTestSuite.cc index 4f21bf9e2..88d900c7c 100644 --- a/libs/framework/gtest/src/CelixBundleContextBundlesTestSuite.cc +++ b/libs/framework/gtest/src/CelixBundleContextBundlesTestSuite.cc @@ -756,7 +756,7 @@ TEST_F(CelixBundleContextBundlesTestSuite, BundleInfoTests) { long svcId = celix_bundleContext_registerService(ctx, (void*)0x42, "NopService", NULL); - long trackerId = celix_bundleContext_trackServices(ctx, "AService", NULL, NULL, NULL); + long trackerId = celix_bundleContext_trackServices(ctx, "AService"); called = celix_bundleContext_useBundle(ctx, 0, &data, updateCountFp); EXPECT_TRUE(called); diff --git a/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc b/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc index 02c499496..3f57f7ae1 100644 --- a/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc +++ b/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc @@ -19,13 +19,12 @@ #include - #include #include #include #include #include -#include +#include #include #include "celix_api.h" @@ -65,13 +64,13 @@ class CelixBundleContextServicesTestSuite : public ::testing::Test { CelixBundleContextServicesTestSuite& operator=(CelixBundleContextServicesTestSuite&&) = delete; CelixBundleContextServicesTestSuite& operator=(const CelixBundleContextServicesTestSuite&) = delete; - void registerAndUseServiceWithCorrectVersion(bool direct) { + void registerAndUseServiceWithCorrectVersion(bool direct) const { struct calc { int (*calc)(int); }; const char *calcName = "calc"; - struct calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -106,13 +105,13 @@ class CelixBundleContextServicesTestSuite : public ::testing::Test { celix_bundleContext_unregisterService(ctx, svcId); } - void registerAndUseServiceWithIncorrectVersion(bool direct) { + void registerAndUseServiceWithIncorrectVersion(bool direct) const { struct calc { int (*calc)(int); }; const char *calcName = "calc"; - struct calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -146,7 +145,7 @@ class CelixBundleContextServicesTestSuite : public ::testing::Test { celix_bundleContext_unregisterService(ctx, svcId); } - void registerAndUseServiceWithTimeout(bool direct) { + void registerAndUseServiceWithTimeout(bool direct) const { const int NR_ITERATIONS = 5; //NOTE this test is sensitive for triggering race condition in the celix framework, therefore is used a few times. for (int i = 0; i < NR_ITERATIONS; ++i) { printf("Iter %i\n", i); @@ -155,7 +154,7 @@ class CelixBundleContextServicesTestSuite : public ::testing::Test { }; const char *calcName = "calc"; - struct calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -198,7 +197,7 @@ class CelixBundleContextServicesTestSuite : public ::testing::Test { } } - void registerAsyncAndUseServiceWithTimeout(bool direct) { + void registerAsyncAndUseServiceWithTimeout(bool direct) const { const int NR_ITERATIONS = 5; //NOTE this test is sensitive for triggering race condition in the celix framework, therefore is used a few times. for (int i = 0; i < NR_ITERATIONS; ++i) { printf("Iter %i\n", i); @@ -207,7 +206,7 @@ class CelixBundleContextServicesTestSuite : public ::testing::Test { }; const char *calcName = "calc"; - struct calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -258,7 +257,7 @@ TEST_F(CelixBundleContextServicesTestSuite, RegisterServiceTest) { }; const char *calcName = "calc"; - calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -274,7 +273,7 @@ TEST_F(CelixBundleContextServicesTestSuite, TegisterServiceAsyncTest) { }; const char *calcName = "calc"; - calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -309,7 +308,7 @@ TEST_F(CelixBundleContextServicesTestSuite, UseServicesWithoutNameTest) { }; const char *calcName = "calc"; - struct calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -362,7 +361,7 @@ TEST_F(CelixBundleContextServicesTestSuite, TegisterMultipleAndUseServicesTest) }; const char *calcName = "calc"; - struct calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -443,7 +442,7 @@ TEST_F(CelixBundleContextServicesTestSuite, UseServiceInUseCallbackTest) { }; const char *calcName = "calc"; - struct calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -486,7 +485,7 @@ TEST_F(CelixBundleContextServicesTestSuite, RegisterAndUseServiceTest) { }; const char *calcName = "calc"; - struct calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -509,7 +508,7 @@ TEST_F(CelixBundleContextServicesTestSuite, RegisterAndUseServiceTest) { long nonExistingSvcId = 101; called = celix_bundleContext_useServiceWithId(ctx, nonExistingSvcId, calcName, &result, [](void *handle, void *svc) { int *result = static_cast(handle); - struct calc *calc = static_cast(svc); + auto* calc = static_cast(svc); int tmp = calc->calc(2); *result = tmp; }); @@ -557,7 +556,7 @@ TEST_F(CelixBundleContextServicesTestSuite, RegisterAndUseWithForcedRaceConditio }; const char *calcName = "calc"; - struct calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -578,7 +577,7 @@ TEST_F(CelixBundleContextServicesTestSuite, RegisterAndUseWithForcedRaceConditio auto use = [](void *handle, void *svc) { ASSERT_TRUE(svc != nullptr); - struct sync *h = static_cast(handle); + auto* h = static_cast(handle); std::cout << "setting isUseCall to true and syncing on readyToExitUseCall" << std::endl; std::unique_lock lock(h->mutex); @@ -588,7 +587,7 @@ TEST_F(CelixBundleContextServicesTestSuite, RegisterAndUseWithForcedRaceConditio lock.unlock(); std::cout << "Calling calc " << std::endl; - struct calc *calc = static_cast(svc); + auto* calc = static_cast(svc); int tmp = calc->calc(2); h->result = tmp; }; @@ -644,7 +643,12 @@ TEST_F(CelixBundleContextServicesTestSuite, ServicesTrackerTest) { *c -= 1; }; - long trackerId = celix_bundleContext_trackServices(ctx, "calc", &count, add, remove); + celix_service_tracking_options_t opts{}; + opts.filter.serviceName = "calc"; + opts.callbackHandle = &count; + opts.add = add; + opts.remove = remove; + long trackerId = celix_bundleContext_trackServicesWithOptions(ctx, &opts); ASSERT_TRUE(trackerId >= 0); ASSERT_EQ(0, count); @@ -676,7 +680,12 @@ TEST_F(CelixBundleContextServicesTestSuite, ServicesTrackerTestAsync) { *c -= 1; }; - long trackerId = celix_bundleContext_trackServicesAsync(ctx, "calc", &count, add, remove); + celix_service_tracking_options_t opts{}; + opts.filter.serviceName = "calc"; + opts.callbackHandle = &count; + opts.add = add; + opts.remove = remove; + long trackerId = celix_bundleContext_trackServicesWithOptionsAsync(ctx, &opts); ASSERT_TRUE(trackerId >= 0); ASSERT_EQ(0, count); @@ -694,16 +703,16 @@ TEST_F(CelixBundleContextServicesTestSuite, ServicesTrackerTestAsync) { celix_bundleContext_waitForAsyncUnregistration(ctx, svcId1); ASSERT_EQ(1, count); - celix_bundleContext_stopTrackerAsync(ctx, trackerId, NULL, NULL); - celix_bundleContext_unregisterServiceAsync(ctx, svcId2, NULL, NULL); + celix_bundleContext_stopTrackerAsync(ctx, trackerId, nullptr, nullptr); + celix_bundleContext_unregisterServiceAsync(ctx, svcId2, nullptr, nullptr); celix_framework_waitForEmptyEventQueue(fw); } TEST_F(CelixBundleContextServicesTestSuite, ServicesTrackerInvalidArgsTest) { - long trackerId = celix_bundleContext_trackServices(nullptr, nullptr, nullptr, nullptr, nullptr); + long trackerId = celix_bundleContext_trackServices(nullptr, nullptr); ASSERT_TRUE(trackerId < 0); //required ctx missing - trackerId = celix_bundleContext_trackServices(ctx, "calc", nullptr, nullptr, nullptr); + trackerId = celix_bundleContext_trackServices(ctx, "calc"); ASSERT_TRUE(trackerId >= 0); //valid celix_bundleContext_stopTracker(ctx, trackerId); @@ -742,7 +751,12 @@ TEST_F(CelixBundleContextServicesTestSuite, ServicesTrackerTestWithAlreadyRegist - long trackerId = celix_bundleContext_trackServices(ctx, "calc", &count, add, remove); + celix_service_tracking_options_t opts{}; + opts.filter.serviceName = "calc"; + opts.callbackHandle = &count; + opts.add = add; + opts.remove = remove; + long trackerId = celix_bundleContext_trackServicesWithOptions(ctx, &opts); ASSERT_TRUE(trackerId >= 0); ASSERT_EQ(2, count); @@ -858,7 +872,7 @@ TEST_F(CelixBundleContextServicesTestSuite, ServiceTrackerWithRaceConditionTest) }; const char *calcName = "calc"; - struct calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -877,7 +891,7 @@ TEST_F(CelixBundleContextServicesTestSuite, ServiceTrackerWithRaceConditionTest) auto add = [](void *handle, void *svc) { ASSERT_TRUE(svc != nullptr); - struct data *d = static_cast(handle); + auto *d = static_cast(handle); std::unique_lock lock(d->mutex); d->inAddCall = true; @@ -885,7 +899,7 @@ TEST_F(CelixBundleContextServicesTestSuite, ServiceTrackerWithRaceConditionTest) d->sync.wait(lock, [d]{return d->readyToExit;}); lock.unlock(); - struct calc *calc = static_cast(svc); + auto *calc = static_cast(svc); int tmp = calc->calc(2); lock.lock(); @@ -906,7 +920,12 @@ TEST_F(CelixBundleContextServicesTestSuite, ServiceTrackerWithRaceConditionTest) lock.unlock(); }; - long trackerId = celix_bundleContext_trackServices(ctx, calcName, &data, add, remove); + celix_service_tracking_options_t opts{}; + opts.filter.serviceName = "calc"; + opts.callbackHandle = &data; + opts.add = add; + opts.remove = remove; + long trackerId = celix_bundleContext_trackServicesWithOptions(ctx, &opts); std::thread registerThread{[&]{ long id = celix_bundleContext_registerService(ctx, &svc, calcName, nullptr); @@ -1179,9 +1198,9 @@ TEST_F(CelixBundleContextServicesTestSuite, MetaTrackServiceTrackersFromOtherBun long * bid= static_cast(handle); *bid = info->bundleId; }; - long trkId1 = celix_bundleContext_trackServiceTrackers(ctx, NULL, &bndIdInAdd, add, NULL); + long trkId1 = celix_bundleContext_trackServiceTrackers(ctx, nullptr, &bndIdInAdd, add, nullptr); EXPECT_TRUE(trkId1 >= 0); - long trkId2 = celix_bundleContext_trackServiceTrackers(ctx, NULL, &bndIdInRemove, NULL, remove); + long trkId2 = celix_bundleContext_trackServiceTrackers(ctx, nullptr, &bndIdInRemove, nullptr, remove); EXPECT_TRUE(trkId2 >= 0); EXPECT_EQ(bndIdInAdd, bndId); EXPECT_EQ(bndIdInRemove, -1); @@ -1330,15 +1349,15 @@ TEST_F(CelixBundleContextServicesTestSuite, TrackServiceTrackerTest) { EXPECT_TRUE(trackerId >= 0); EXPECT_EQ(0, count); - long tracker2 = celix_bundleContext_trackService(ctx, "example", nullptr, nullptr); + long tracker2 = celix_bundleContext_trackService(ctx, "example"); EXPECT_TRUE(tracker2 >= 0); EXPECT_EQ(1, count); - long tracker3 = celix_bundleContext_trackServices(ctx, "example", nullptr, nullptr, nullptr); + long tracker3 = celix_bundleContext_trackServices(ctx, "example"); EXPECT_TRUE(tracker3 >= 0); EXPECT_EQ(2, count); - long tracker4 = celix_bundleContext_trackServices(ctx, "no-match", nullptr, nullptr, nullptr); + long tracker4 = celix_bundleContext_trackServices(ctx, "no-match"); EXPECT_TRUE(tracker4 >= 0); EXPECT_EQ(2, count); @@ -1380,7 +1399,7 @@ TEST_F(CelixBundleContextServicesTestSuite, FloodEventLoopTest) { long id = celix_bundleContext_registerServiceAsync(ctx, (void*)0x42, "test", nullptr); //note cannot be completed because the first service registration in blocking in the event loop. EXPECT_GE(id, 0); svcIds.push_back(id); - trackerIds.push_back(celix_bundleContext_trackServicesAsync(ctx, "test", nullptr, nullptr, nullptr)); + trackerIds.push_back(celix_bundleContext_trackServicesAsync(ctx, "test")); //CHECK if celix_bundleContext_isServiceRegistered work EXPECT_FALSE(celix_bundleContext_isServiceRegistered(ctx, id)); @@ -1772,7 +1791,7 @@ TEST_F(CelixBundleContextServicesTestSuite, InvalidArgumentsForUseTrackedService } TEST_F(CelixBundleContextServicesTestSuite, IsValidTrackerIdTest) { - long trkId = celix_bundleContext_trackServices(ctx, "test", nullptr, nullptr, nullptr); + long trkId = celix_bundleContext_trackServices(ctx, "test"); EXPECT_TRUE(celix_bundleContext_isValidTrackerId(ctx, trkId)); celix_bundleContext_stopTracker(ctx, trkId); EXPECT_FALSE(celix_bundleContext_isValidTrackerId(ctx, trkId)); @@ -1796,7 +1815,7 @@ TEST_F(CelixBundleContextServicesTestSuite, UseTrackedServiceTest) { celix_auto(celix_service_registration_guard_t) guard3 = celix_serviceRegistrationGuard_init(ctx, svcId3); // When tracking services for a service name - long trkId = celix_bundleContext_trackServices(ctx, "test", nullptr, nullptr, nullptr); + long trkId = celix_bundleContext_trackServices(ctx, "test"); celix_auto(celix_tracker_guard_t) trkGuard = celix_trackerGuard_init(ctx, trkId); // Then the useTrackedService function should be called for each service @@ -1901,7 +1920,7 @@ TEST_F(CelixBundleContextServicesTestSuite, GetTrackedServicesInfoTest) { // When a tracker for all services is created - long trkId2 = celix_bundleContext_trackServices(ctx, nullptr, nullptr, nullptr, nullptr); + long trkId2 = celix_bundleContext_trackServices(ctx, nullptr); celix_auto(celix_tracker_guard_t) trkGuard2 = celix_trackerGuard_init(ctx, trkId2); // Then the tracked services info should be available @@ -1909,3 +1928,46 @@ TEST_F(CelixBundleContextServicesTestSuite, GetTrackedServicesInfoTest) { EXPECT_STREQ(celix_bundleContext_getTrackedServiceName(ctx, trkId2), "*"); EXPECT_TRUE(strstr(celix_bundleContext_getTrackedServiceFilter(ctx, trkId2), "(objectClass=*)") != nullptr); } + +TEST_F(CelixBundleContextServicesTestSuite, UseTrackedServiceDuringTrackerCreation) { + // Given a registered service + long svcId = celix_bundleContext_registerService(ctx, (void*)0x42, "test", nullptr); + celix_auto(celix_service_registration_guard_t) svcGuard = celix_serviceRegistrationGuard_init(ctx, svcId); + + // And a generic event on the event thread that will wait for promise before continuing + std::promise promise; + std::future future = promise.get_future(); + auto eventCallback = [](void *data) { + auto* f = static_cast*>(data); + auto status = f->wait_for(std::chrono::seconds{5}); + EXPECT_EQ(std::future_status::ready, status); + }; + celix_framework_fireGenericEvent( + fw, + -1, + celix_bundle_getId(celix_framework_getFrameworkBundle(fw)), + "wait for promise", + (void*)&future, + eventCallback, + nullptr, + nullptr); + + // When a service tracker is created async (and cannot be completed because the event thread is waiting) + long trkId = celix_bundleContext_trackServicesAsync(ctx, "test"); + celix_auto(celix_tracker_guard_t) trkGuard = celix_trackerGuard_init(ctx, trkId); + + // Then a call to useTrackedService will not deadlock and the tracked service is not found, because the + // tracker is not yet created + bool serviceFound = celix_bundleContext_useTrackedService(ctx, trkId, nullptr, nullptr); + EXPECT_FALSE(serviceFound); + + // When the promise is set + promise.set_value(); + + // And the tracker is created + celix_bundleContext_waitForAsyncTracker(ctx, trkId); + + // Then the use service function will return true, because the tracker is now created and tracking the service + serviceFound = celix_bundleContext_useTrackedService(ctx, trkId, nullptr, nullptr); + EXPECT_TRUE(serviceFound); +} diff --git a/libs/framework/include/celix_bundle_context.h b/libs/framework/include/celix_bundle_context.h index e18992f57..dea42003d 100644 --- a/libs/framework/include/celix_bundle_context.h +++ b/libs/framework/include/celix_bundle_context.h @@ -586,48 +586,25 @@ CELIX_FRAMEWORK_EXPORT size_t celix_bundleContext_useServicesWithOptions( /** * @brief Track the highest ranking service with the provided serviceName. * - * The highest ranking services will used for the callback. - * If a new and higher ranking services the callback with be called again with the new service. - * If a service is removed a the callback with be called with next highest ranking service or NULL as service. - * * The service tracker will be created async on the Celix event loop thread. This means that the function can return * before the tracker is created. * * @param ctx The bundle context. * @param serviceName The required service name to track. * If NULL is all service are tracked. - * @param callbackHandle The data pointer, which will be used in the callbacks - * @param set is a required callback, which will be called when a new highest ranking service is set. * @return the tracker id (>=0) or < 0 if unsuccessful. */ -CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServiceAsync( - celix_bundle_context_t *ctx, - const char* serviceName, - void* callbackHandle, - void (*set)(void* handle, void* svc) -); +CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServiceAsync(celix_bundle_context_t* ctx, const char* serviceName); /** - * @brief Track the highest ranking service with the provided serviceName. + * @brief Track the highest ranking service with the provided serviceName * - * The highest ranking services will used for the callback. - * If a new and higher ranking services the callback with be called again with the new service. - * If a service is removed a the callback with be called with next highest ranking service or NULL as service. - * Note: Please use the celix_bundleContext_trackServiceAsync instead. + * Note: If possible, use the celix_bundleContext_trackServiceAsync instead. * * @param ctx The bundle context. - * @param serviceName The required service name to track. - * If NULL is all service are tracked. - * @param callbackHandle The data pointer, which will be used in the callbacks - * @param set is a required callback, which will be called when a new highest ranking service is set. * @return the tracker id (>=0) or < 0 if unsuccessful. */ -CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackService( - celix_bundle_context_t *ctx, - const char* serviceName, - void* callbackHandle, - void (*set)(void* handle, void* svc) -); +CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackService(celix_bundle_context_t* ctx, const char* serviceName); /** * @brief Track services with the provided serviceName. @@ -638,39 +615,22 @@ CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackService( * @param ctx The bundle context. * @param serviceName The required service name to track * If NULL is all service are tracked. - * @param callbackHandle The data pointer, which will be used in the callbacks - * @param add is a required callback, which will be called when a service is added and initially for the existing service. - * @param remove is a required callback, which will be called when a service is removed * @return the tracker id (>=0) or < 0 if unsuccessful. */ -CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServicesAsync( - celix_bundle_context_t *ctx, - const char* serviceName, - void* callbackHandle, - void (*add)(void* handle, void* svc), - void (*remove)(void* handle, void* svc) -); +CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServicesAsync(celix_bundle_context_t* ctx, + const char* serviceName); /** * @brief Track services with the provided serviceName. * - * Note: Please use the celix_bundleContext_trackServicesAsync instead. + * Note: If possible, use the celix_bundleContext_trackServicesAsync instead. * * @param ctx The bundle context. * @param serviceName The required service name to track * If NULL is all service are tracked. - * @param callbackHandle The data pointer, which will be used in the callbacks - * @param add is a required callback, which will be called when a service is added and initially for the existing service. - * @param remove is a required callback, which will be called when a service is removed * @return the tracker id (>=0) or < 0 if unsuccessful. */ -CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServices( - celix_bundle_context_t *ctx, - const char* serviceName, - void* callbackHandle, - void (*add)(void* handle, void* svc), - void (*remove)(void* handle, void* svc) -); +CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServices(celix_bundle_context_t* ctx, const char* serviceName); /** * @brief Service Tracker Options used to fine tune which services to track and the callback to be used for the tracked services. @@ -825,15 +785,14 @@ CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServicesWithOptions(celix_b * @param[in] trackerId The tracker id. * @param[in] callbackHandle The data pointer, which will be used in the callbacks. * @param[in] use The callback, which will be called when service is retrieved. - * @return True if a service was found and the use callback was called. + * @return True if a service was found and the use callback was called. Returns false if the tracker is not yet active + * (async tracker creation). */ CELIX_FRAMEWORK_EXPORT -bool celix_bundleContext_useTrackedService( - celix_bundle_context_t *ctx, - long trackerId, - void *callbackHandle, - void (*use)(void *handle, void* svc) -); +bool celix_bundleContext_useTrackedService(celix_bundle_context_t* ctx, + long trackerId, + void* callbackHandle, + void (*use)(void* handle, void* svc)); /** * @brief Use the services, tracked by the provided tracker id, using the provided callback. @@ -848,7 +807,8 @@ bool celix_bundleContext_useTrackedService( * @param[in] trackerId The tracker id. * @param[in] callbackHandle The data pointer, which will be used in the callbacks. * @param[in] use The callback, which will be called for every service found. - * @return The number of services found and therefore the number of times the use callback was called. + * @return The number of services found and therefore the number of times the use callback was called. Returns 0 if the + * tracker is not yet active (async tracker creation). */ CELIX_FRAMEWORK_EXPORT size_t celix_bundleContext_useTrackedServices(celix_bundle_context_t* ctx, @@ -924,7 +884,8 @@ typedef struct celix_tracked_service_use_options { * @param[in] trackerId The tracker id. * @param[in] callbackHandle The data pointer, which will be used in the callbacks. * @param[in] opts The service use options. - * @return True if a service was found and the use callbacks where called. + * @return True if a service was found and the use callbacks where called. Returns false if the tracker is not yet + * active (async tracker creation). */ CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useTrackedServiceWithOptions(celix_bundle_context_t* ctx, @@ -947,7 +908,8 @@ bool celix_bundleContext_useTrackedServiceWithOptions(celix_bundle_context_t* ct * @param[in] callbackHandle The data pointer, which will be used in the callbacks. * @param[in] opts The service use options. * @return The number of services found and therefore the number of times the callbacks in the provided options where - * called. + * called. Returns 0 if the tracker is not yet active + * (async tracker creation). */ CELIX_FRAMEWORK_EXPORT size_t celix_bundleContext_useTrackedServicesWithOptions(celix_bundle_context_t* ctx, @@ -994,16 +956,6 @@ const char* celix_bundleContext_getTrackedServiceName(celix_bundle_context_t *ct CELIX_FRAMEWORK_EXPORT const char* celix_bundleContext_getTrackedServiceFilter(celix_bundle_context_t* ctx, long trackerId); -/** - * @brief Returns true if the provided tracker id is a tracker id for an existing tracker for the provided bundle - * context. - * @param ctx The bundle context. - * @param trackerId The tracker id. - * @return True if the tracker id is valid. - */ -CELIX_FRAMEWORK_EXPORT -bool celix_bundleContext_isValidTrackerId(celix_bundle_context_t* ctx, long trackerId); - /** * @brief Stop the tracker with the provided track id. * @@ -1025,12 +977,24 @@ CELIX_FRAMEWORK_EXPORT void celix_bundleContext_stopTrackerAsync( void (*doneCallback)(void* doneCallbackData)); /** - * @brief Wait for (async) creation of tracker + * @brief Wait, if able, for (async) creation of tracker. + * + * Will silently ignore trackerId < 0 and log an error if the tracker id is unknown. + * If called on the Apache Celix event loop thread, the function will log a warning and return immediately. + * + * @param[in] ctx The bundle context. + * @param[in] trackerId The tracker id. */ CELIX_FRAMEWORK_EXPORT void celix_bundleContext_waitForAsyncTracker(celix_bundle_context_t *ctx, long trackerId); /** - * @brief Wait for (async) stopping of tracking. + * @brief Wait, if able, for (async) stopping of tracking. + * + * Will silently ignore trackerId < 0 and log an error if the tracker id is unknown. + * If called on the Apache Celix event loop thread, the function will log a warning and return immediately. + * + * @param[in] ctx The bundle context. + * @param[in] trackerId The tracker id. */ CELIX_FRAMEWORK_EXPORT void celix_bundleContext_waitForAsyncStopTracker(celix_bundle_context_t *ctx, long trackerId); @@ -1045,6 +1009,16 @@ CELIX_FRAMEWORK_EXPORT void celix_bundleContext_waitForAsyncStopTracker(celix_bu */ CELIX_FRAMEWORK_EXPORT void celix_bundleContext_stopTracker(celix_bundle_context_t *ctx, long trackerId); +/** + * @brief Returns true if the provided tracker id is a tracker id for an existing tracker for the provided bundle + * context. + * @param ctx The bundle context. + * @param trackerId The tracker id. + * @return True if the tracker id is valid. + */ +CELIX_FRAMEWORK_EXPORT +bool celix_bundleContext_isValidTrackerId(celix_bundle_context_t* ctx, long trackerId); + /** * @brief Tracker guard. */ diff --git a/libs/framework/include_deprecated/service_tracker.h b/libs/framework/include_deprecated/service_tracker.h index cba45a098..bc9df3941 100644 --- a/libs/framework/include_deprecated/service_tracker.h +++ b/libs/framework/include_deprecated/service_tracker.h @@ -143,18 +143,19 @@ CELIX_FRAMEWORK_DEPRECATED_EXPORT size_t celix_serviceTracker_useServices( /** * @brief Get the number of tracked services. */ -CELIX_FRAMEWORK_DEPRECATED_EXPORT size_t celix_serviceTracker_getTrackedServiceCount(celix_service_tracker_t *tracker); +CELIX_FRAMEWORK_DEPRECATED_EXPORT size_t celix_serviceTracker_getTrackedServiceCount(celix_service_tracker_t* tracker); /** * @brief Get the tracker service name. */ -CELIX_FRAMEWORK_DEPRECATED_EXPORT const char* celix_serviceTracker_getTrackedServiceName(celix_service_tracker_t *tracker); +CELIX_FRAMEWORK_DEPRECATED_EXPORT const char* +celix_serviceTracker_getTrackedServiceName(celix_service_tracker_t* tracker); /** * @brief Get the tracker filter. */ -CELIX_FRAMEWORK_DEPRECATED_EXPORT const char* celix_serviceTracker_getTrackedServiceFilter(celix_service_tracker_t *tracker); - +CELIX_FRAMEWORK_DEPRECATED_EXPORT const char* +celix_serviceTracker_getTrackedServiceFilter(celix_service_tracker_t* tracker); #ifdef __cplusplus } diff --git a/libs/framework/src/bundle_context.c b/libs/framework/src/bundle_context.c index ccc49ddf0..ec81025a0 100644 --- a/libs/framework/src/bundle_context.c +++ b/libs/framework/src/bundle_context.c @@ -946,6 +946,12 @@ static void celix_bundleContext_waitForTrackerInternal(celix_bundle_context_t* c return; } + if (celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) { + fw_log(ctx->framework->logger, + CELIX_LOG_LEVEL_WARNING, + "Cannot wait for tracker on the event loop thread. This can cause a deadlock. " + "Ignoring call."); + } bool found = false; long eventId = -1; @@ -982,6 +988,11 @@ static void celix_bundleContext_waitForTrackerInternal(celix_bundle_context_t* c } else { celix_framework_waitForGenericEvent(ctx->framework, eventId); } + } else if (waitForStart) { + fw_log(ctx->framework->logger, + CELIX_LOG_LEVEL_ERROR, + "Cannot wait for tracker, no tracker with id %li found'", + trackerId); } } @@ -1203,61 +1214,30 @@ size_t celix_bundleContext_useServicesWithOptions( return data.count; } - -long celix_bundleContext_trackService( - bundle_context_t* ctx, - const char* serviceName, - void* callbackHandle, - void (*set)(void* handle, void* svc)) { +long celix_bundleContext_trackService(bundle_context_t* ctx, const char* serviceName) { celix_service_tracking_options_t opts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS; opts.filter.serviceName = serviceName; - opts.callbackHandle = callbackHandle; - opts.set = set; return celix_bundleContext_trackServicesWithOptionsInternal(ctx, &opts, false); } -long celix_bundleContext_trackServiceAsync( - bundle_context_t* ctx, - const char* serviceName, - void* callbackHandle, - void (*set)(void* handle, void* svc)) { +long celix_bundleContext_trackServiceAsync(bundle_context_t* ctx, const char* serviceName) { celix_service_tracking_options_t opts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS; opts.filter.serviceName = serviceName; - opts.callbackHandle = callbackHandle; - opts.set = set; return celix_bundleContext_trackServicesWithOptionsInternal(ctx, &opts, true); } - -long celix_bundleContext_trackServices( - bundle_context_t* ctx, - const char* serviceName, - void* callbackHandle, - void (*add)(void* handle, void* svc), - void (*remove)(void* handle, void* svc)) { +long celix_bundleContext_trackServices(bundle_context_t* ctx, const char* serviceName) { celix_service_tracking_options_t opts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS; opts.filter.serviceName = serviceName; - opts.callbackHandle = callbackHandle; - opts.add = add; - opts.remove = remove; return celix_bundleContext_trackServicesWithOptionsInternal(ctx, &opts, false); } -long celix_bundleContext_trackServicesAsync( - celix_bundle_context_t* ctx, - const char* serviceName, - void* callbackHandle, - void (*add)(void* handle, void* svc), - void (*remove)(void* handle, void* svc)) { +long celix_bundleContext_trackServicesAsync(celix_bundle_context_t* ctx, const char* serviceName) { celix_service_tracking_options_t opts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS; opts.filter.serviceName = serviceName; - opts.callbackHandle = callbackHandle; - opts.add = add; - opts.remove = remove; return celix_bundleContext_trackServicesWithOptionsInternal(ctx, &opts, true); } - static void celix_bundleContext_createTrackerOnEventLoop(void *data) { celix_bundle_context_service_tracker_entry_t* entry = data; assert(celix_framework_isCurrentThreadTheEventLoop(entry->ctx->framework)); @@ -1331,9 +1311,10 @@ static long celix_bundleContext_trackServicesWithOptionsInternal(celix_bundle_co } return trackerId; } else { + long createEventId = celix_framework_nextEventId(ctx->framework); celix_bundle_context_service_tracker_entry_t* entry = calloc(1, sizeof(*entry)); entry->ctx = ctx; - entry->createEventId = celix_framework_nextEventId(ctx->framework); + entry->createEventId = createEventId; entry->tracker = NULL; //will be set async entry->opts = *opts; @@ -1347,13 +1328,22 @@ static long celix_bundleContext_trackServicesWithOptionsInternal(celix_bundle_co entry->opts.filter.versionRange = celix_utils_strdup(opts->filter.versionRange); entry->opts.filter.filter = celix_utils_strdup(opts->filter.filter); } + celixThreadMutex_lock(&ctx->mutex); entry->trackerId = ctx->nextTrackerId++; long trackerId = entry->trackerId; celix_longHashMap_put(ctx->serviceTrackers, entry->trackerId, entry); celixThreadMutex_unlock(&ctx->mutex); - long id = celix_framework_fireGenericEvent(ctx->framework, entry->createEventId, celix_bundle_getId(ctx->bundle), "create service tracker event", entry, celix_bundleContext_createTrackerOnEventLoop, entry, celix_bundleContext_doneCreatingTrackerOnEventLoop); + long id = celix_framework_fireGenericEvent(ctx->framework, + createEventId, + celix_bundle_getId(ctx->bundle), + "create service tracker event", + entry, + celix_bundleContext_createTrackerOnEventLoop, + entry, + celix_bundleContext_doneCreatingTrackerOnEventLoop); + if (!async) { celix_framework_waitForGenericEvent(ctx->framework, id); @@ -1398,7 +1388,7 @@ size_t celix_bundleContext_useTrackedServices(celix_bundle_context_t* ctx, */ static celix_service_tracker_t* celix_bundleContext_findServiceTracker(celix_bundle_context_t* ctx, long trackerId) { if (trackerId < 0) { - return NULL; //silent ignore + return NULL; // silent ignore } celix_bundle_context_service_tracker_entry_t* entry = celix_longHashMap_get(ctx->serviceTrackers, trackerId); @@ -1409,6 +1399,16 @@ static celix_service_tracker_t* celix_bundleContext_findServiceTracker(celix_bun trackerId); return NULL; } + + if (!entry->tracker && entry->createEventId >= 0) { // tracker not yet created (async creation) + if (celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) { + fw_log( + ctx->framework->logger, + CELIX_LOG_LEVEL_DEBUG, + "Tracker with id %li is not yet created.", + entry->trackerId); + } + } return entry->tracker; } diff --git a/libs/framework/src/bundle_context_private.h b/libs/framework/src/bundle_context_private.h index 13c3f350d..1eacc48ba 100644 --- a/libs/framework/src/bundle_context_private.h +++ b/libs/framework/src/bundle_context_private.h @@ -48,32 +48,32 @@ typedef struct celix_bundle_context_bundle_tracker_entry { } celix_bundle_context_bundle_tracker_entry_t; typedef struct celix_bundle_context_service_tracker_entry { - celix_bundle_context_t *ctx; - long trackerId; + celix_bundle_context_t* ctx; + long trackerId; celix_service_tracking_options_t opts; - celix_service_tracker_t* tracker; - void *trackerCreatedCallbackData; - void (*trackerCreatedCallback)(void *trackerCreatedCallbackData); + celix_service_tracker_t* tracker; + void* trackerCreatedCallbackData; + void (*trackerCreatedCallback)(void* trackerCreatedCallbackData); bool isFreeFilterNeeded; - //used for sync + // used for sync long createEventId; - bool cancelled; //if tracker is stopped before created async + bool cancelled; // if tracker is stopped before created async } celix_bundle_context_service_tracker_entry_t; typedef struct celix_bundle_context_service_tracker_tracker_entry { - celix_bundle_context_t* ctx; - long trackerId; + celix_bundle_context_t* ctx; + long trackerId; - struct listener_hook_service hook; - long serviceId; + struct listener_hook_service hook; + long serviceId; - char *serviceName; - void *callbackHandle; - void (*add)(void *handle, const celix_service_tracker_info_t *info); - void (*remove)(void *handle, const celix_service_tracker_info_t *info); + char* serviceName; + void* callbackHandle; + void (*add)(void* handle, const celix_service_tracker_info_t* info); + void (*remove)(void* handle, const celix_service_tracker_info_t* info); - //used for sync + // used for sync long createEventId; } celix_bundle_context_service_tracker_tracker_entry_t; From 9d2e7a51c66221ccf71ac77a7932ba9d5637cc60 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 11 Feb 2024 15:37:45 +0100 Subject: [PATCH 03/14] gh-87: Add celix_bundle_context_type.h header. --- libs/framework/include/celix_bundle_context.h | 11 ++--- .../include/celix_bundle_context_type.h | 41 +++++++++++++++++++ libs/framework/include/celix_types.h | 4 +- 3 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 libs/framework/include/celix_bundle_context_type.h diff --git a/libs/framework/include/celix_bundle_context.h b/libs/framework/include/celix_bundle_context.h index dea42003d..e46996e9d 100644 --- a/libs/framework/include/celix_bundle_context.h +++ b/libs/framework/include/celix_bundle_context.h @@ -20,17 +20,12 @@ #ifndef CELIX_BUNDLE_CONTEXT_H_ #define CELIX_BUNDLE_CONTEXT_H_ -#include - -#include "celix_cleanup.h" -#include "celix_types.h" -#include "celix_service_factory.h" -#include "celix_properties.h" -#include "celix_array_list.h" +#include "celix_bundle_context_type.h" +#include "celix_framework_export.h" #include "celix_filter.h" +#include "celix_service_factory.h" #include "celix_bundle_event.h" #include "celix_log_level.h" -#include "celix_framework_export.h" #ifdef __cplusplus extern "C" { diff --git a/libs/framework/include/celix_bundle_context_type.h b/libs/framework/include/celix_bundle_context_type.h new file mode 100644 index 000000000..1ccedef01 --- /dev/null +++ b/libs/framework/include/celix_bundle_context_type.h @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef CELIX_BUNDLE_CONTEXT_TYPE_H +#define CELIX_BUNDLE_CONTEXT_TYPE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file celix_bundle_context_type.h + * @brief Header file for celix_bundle_context_t opaque type. Can be used for forward declaration. + */ + +/** + * @brief The opaque type representing a bundle context. + */ +typedef struct celix_bundle_context celix_bundle_context_t; + +#ifdef __cplusplus +} +#endif + +#endif //CELIX_BUNDLE_CONTEXT_TYPE_H diff --git a/libs/framework/include/celix_types.h b/libs/framework/include/celix_types.h index 201875fec..a73bac6f0 100644 --- a/libs/framework/include/celix_types.h +++ b/libs/framework/include/celix_types.h @@ -20,12 +20,13 @@ #ifndef CELIX_CELIX_TYPES_H #define CELIX_CELIX_TYPES_H +#include "celix_bundle_context_type.h" + /** * Celix types contains the declaration of many of the celix types. * These types are declared in a separate header for forward declaration */ - #ifdef ADD_CELIX_DEPRECATED_WARNING #define CELIX_DEPRECATED_ATTR __attribute__ ((deprecated)) #define CELIX_DEPRECATED_ATTR_MSG(msg) __attribute__ ((deprecated(msg))) @@ -40,7 +41,6 @@ extern "C" { typedef struct celix_framework celix_framework_t; typedef struct celix_bundle celix_bundle_t; -typedef struct celix_bundle_context celix_bundle_context_t; typedef struct celix_dependency_manager celix_dependency_manager_t; typedef struct celix_dm_component_struct celix_dm_component_t; typedef struct celix_dm_service_dependency celix_dm_service_dependency_t; From 9321f514d4613b577debab7f3e8e9fc0a46410d0 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 11 Feb 2024 17:17:34 +0100 Subject: [PATCH 04/14] gh-87: Add useService(s) methods to C++ ServiceTracker --- .../gtest/src/CxxBundleContextTestSuite.cc | 103 +++++++++++++++ libs/framework/include/celix/Trackers.h | 119 ++++++++++++++++++ 2 files changed, 222 insertions(+) diff --git a/libs/framework/gtest/src/CxxBundleContextTestSuite.cc b/libs/framework/gtest/src/CxxBundleContextTestSuite.cc index 5155c2bb1..b987d28b8 100644 --- a/libs/framework/gtest/src/CxxBundleContextTestSuite.cc +++ b/libs/framework/gtest/src/CxxBundleContextTestSuite.cc @@ -784,3 +784,106 @@ TEST_F(CxxBundleContextTestSuite, TestOldCStyleTrackerWithCxxMetaTracker) { serviceTracker_close(tracker); serviceTracker_destroy(tracker); } + +TEST_F(CxxBundleContextTestSuite, UseTrackedServidesTest) { + // Given 2 registered services + auto svc1 = std::make_shared(CInterface{nullptr, nullptr}); + auto svcReg1 = ctx->registerService(svc1).build(); + auto svc2 = std::make_shared(CInterface{nullptr, nullptr}); + auto svcReg2 = ctx->registerService(svc2).build(); + + // And a tracker for the services + auto tracker = ctx->trackServices().build(); + tracker->wait(); + + // Then I can use the useServices method to use the services + int count{0}; // note useService(s) callback are called in the current thread, so no need for atomic + size_t nrCalled = tracker->useServices([&count](CInterface& svc) { + (void)svc; + count++; + }); + EXPECT_EQ(2, nrCalled); + EXPECT_EQ(2, count); + + // And I can use the useServicesWithProperties method to use the services with their properties + count = 0; + nrCalled = tracker->useServicesWithProperties([&count](CInterface& svc, const celix::Properties& props) { + (void)svc; + long svcId = props.getAsLong(CELIX_FRAMEWORK_SERVICE_ID, -1L); + EXPECT_GE(svcId, 0); + count++; + }); + EXPECT_EQ(2, nrCalled); + EXPECT_EQ(2, count); + + // And I can use the useServicesWithOwner method to use the services with their properties and the bundle + count = 0; + nrCalled = tracker->useServicesWithOwner( + [&count](CInterface& svc, const celix::Properties& props, const celix::Bundle& bnd) { + (void)svc; + long svcId = props.getAsLong(CELIX_FRAMEWORK_SERVICE_ID, -1L); + EXPECT_GE(svcId, 0); + EXPECT_GE(bnd.getId(), 0); + count++; + }); + EXPECT_EQ(2, nrCalled); + EXPECT_EQ(2, count); + + // And I can use the useService method to use the highest ranking service + count = 0; + bool called = tracker->useService([&count](CInterface& svc) { + (void)svc; + count++; + }); + EXPECT_TRUE(called); + EXPECT_EQ(1, count); + + // And I can use the useServiceWithProperties method to use the highest ranking service with its properties + count = 0; + called = tracker->useServiceWithProperties([&count](CInterface& svc, const celix::Properties& props) { + (void)svc; + long svcId = props.getAsLong(CELIX_FRAMEWORK_SERVICE_ID, -1L); + EXPECT_GE(svcId, 0); + count++; + }); + EXPECT_TRUE(called); + EXPECT_EQ(1, count); + + // And I can use the useServiceWithOwner method to use the highest ranking service with its properties and the + // bundle + count = 0; + called = tracker->useServiceWithOwner( + [&count](CInterface& svc, const celix::Properties& props, const celix::Bundle& bnd) { + (void)svc; + long svcId = props.getAsLong(CELIX_FRAMEWORK_SERVICE_ID, -1L); + EXPECT_GE(svcId, 0); + EXPECT_GE(bnd.getId(), 0); + count++; + }); + EXPECT_TRUE(called); + EXPECT_EQ(1, count); + + // When registering a new service with a service raking of 100 + auto svc3 = std::make_shared(CInterface{nullptr, nullptr}); + auto svcReg3 = ctx->registerService(svc3).addProperty(celix::SERVICE_RANKING, 100).build(); + svcReg3->wait(); + + // Then the useServices method returns 3 services + count = 0; + nrCalled = tracker->useServices([&count](CInterface& svc) { + (void)svc; + count++; + }); + EXPECT_EQ(3, nrCalled); + EXPECT_EQ(3, count); + + // And the useServiceWithProperties method is called with a service with a service ranking of 100 and returns true + count = 0; + called = tracker->useServiceWithProperties([&count](CInterface& svc, const celix::Properties& props) { + (void)svc; + long ranking = props.getAsLong(celix::SERVICE_RANKING, -1L); + EXPECT_EQ(100, ranking); + count++; + }); + EXPECT_TRUE(called); +} diff --git a/libs/framework/include/celix/Trackers.h b/libs/framework/include/celix/Trackers.h index 60df7c862..1521757e2 100644 --- a/libs/framework/include/celix/Trackers.h +++ b/libs/framework/include/celix/Trackers.h @@ -371,6 +371,98 @@ namespace celix { } return result; } + + /** + * @brief Applies the provided function to each service being tracked. + * + * @tparam F A function or callable object type. The function signature should be equivalent to the following: + * `void func(I& svc)` + * where I is the service type being tracked. + * @param f The function or callable object to apply to each service. + * @return The number of services to which the function was applied. + */ + template + size_t useServices(const F& f) { + return this->template useServicesInternal( + [&f](I& svc, const celix::Properties&, const celix::Bundle&) { f(svc); }); + } + + /** + * @brief Applies the provided function to each service being tracked, along with its properties. + * + * @tparam F A function or callable object type. The function signature should be equivalent to the following: + * `void func(I& svc, const celix::Properties& props)` + * where I is the service type being tracked. + * @param f The function or callable object to apply to each service. + * @return The number of services to which the function was applied. + */ + template + size_t useServicesWithProperties(const F& f) { + return this->template useServicesInternal( + [&f](I& svc, const celix::Properties& props, const celix::Bundle&) { f(svc, props); }); + } + + /** + * @brief Applies the provided function to each service being tracked, along with its properties and owner + * bundle. + * + * @tparam F A function or callable object type. The function signature should be equivalent to the following: + * `void func(I& svc, const celix::Properties& props, const celix::Bundle& bnd)` + * where I is the service type being tracked. + * @param f The function or callable object to apply to each service. + * @return The number of services to which the function was applied. + */ + template + size_t useServicesWithOwner(const F& f) { + return this->template useServicesInternal( + [&f](I& svc, const celix::Properties& props, const celix::Bundle& bnd) { f(svc, props, bnd); }); + } + + /** + * @brief Applies the provided function to the highest ranking service being tracked. + * + * @tparam F A function or callable object type. The function signature should be equivalent to the following: + * `void func(I& svc)` + * where I is the service type being tracked. + * @param f The function or callable object to apply to the highest ranking service. + * @return True if the function was applied to a service, false otherwise. + */ + template + bool useService(const F& f) { + return this->template useServiceInternal( + [&f](I& svc, const celix::Properties&, const celix::Bundle&) { f(svc); }); + } + + /** + * @brief Applies the provided function to the highest ranking service being tracked, along with its properties. + * + * @tparam F A function or callable object type. The function signature should be equivalent to the following: + * `void func(I& svc, const celix::Properties& props)` + * where I is the service type being tracked. + * @param f The function or callable object to apply to the highest ranking service. + * @return True if the function was applied to a service, false otherwise. + */ + template + bool useServiceWithProperties(const F& f) { + return this->template useServiceInternal( + [&f](I& svc, const celix::Properties& props, const celix::Bundle&) { f(svc, props); }); + } + + /** + * @brief Applies the provided function to the highest ranking service being tracked, along with its properties + * and owner bundle. + * + * @tparam F A function or callable object type. The function signature should be equivalent to the following: + * `void func(I& svc, const celix::Properties& props, const celix::Bundle& bnd)` + * where I is the service type being tracked. + * @param f The function or callable object to apply to the highest ranking service. + * @return True if the function was applied to a service, false otherwise. + */ + template + bool useServiceWithOwner(const F& f) { + return this->template useServiceInternal( + [&f](I& svc, const celix::Properties& props, const celix::Bundle& bnd) { f(svc, props, bnd); }); + } protected: struct SvcEntry { SvcEntry(long _svcId, long _svcRanking, std::shared_ptr _svc, @@ -561,6 +653,33 @@ namespace celix { tracker->waitForExpiredSvcEntry(prevEntry); }; } + + template + size_t useServicesInternal(const F& f) { + size_t count = 0; + std::lock_guard lck{mutex}; + for (auto& e : entries) { + I& svc = *e->svc; + const celix::Properties& props = *e->properties; + const celix::Bundle& owner = *e->owner; + f(svc, props, owner); + count++; + } + return count; + } + + template + bool useServiceInternal(const F& f) { + std::lock_guard lck{mutex}; + if (highestRankingServiceEntry) { + I& svc = *highestRankingServiceEntry->svc; + const celix::Properties& props = *highestRankingServiceEntry->properties; + const celix::Bundle& owner = *highestRankingServiceEntry->owner; + f(svc, props, owner); + return true; + } + return false; + } }; /** From 5446bf01f73ab93be95d1e4694667ccdf539c14a Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 25 Feb 2024 15:02:54 +0100 Subject: [PATCH 05/14] gh-87: Move svc tracker state fields to an anon state struct --- libs/framework/src/service_tracker.c | 215 +++++++++---------- libs/framework/src/service_tracker_private.h | 57 ++--- 2 files changed, 139 insertions(+), 133 deletions(-) diff --git a/libs/framework/src/service_tracker.c b/libs/framework/src/service_tracker.c index d3bbbb21d..d24d8198b 100644 --- a/libs/framework/src/service_tracker.c +++ b/libs/framework/src/service_tracker.c @@ -109,7 +109,7 @@ celix_status_t serviceTracker_create(bundle_context_pt context, const char * ser celix_status_t serviceTracker_createWithFilter(bundle_context_pt context, const char * filter, service_tracker_customizer_pt customizer, service_tracker_pt *out) { service_tracker_t* tracker = calloc(1, sizeof(*tracker)); *out = tracker; - tracker->state = CELIX_SERVICE_TRACKER_CLOSED; + tracker->state.lifecycleState = CELIX_SERVICE_TRACKER_CLOSED; tracker->context = context; tracker->filter = celix_utils_strdup(filter); tracker->customizer = *customizer; @@ -118,13 +118,13 @@ celix_status_t serviceTracker_createWithFilter(bundle_context_pt context, const celixThreadMutex_create(&tracker->closeSync.mutex, NULL); celixThreadCondition_init(&tracker->closeSync.cond, NULL); - celixThreadMutex_create(&tracker->mutex, NULL); - celixThreadCondition_init(&tracker->condTracked, NULL); - celixThreadCondition_init(&tracker->condUntracking, NULL); - tracker->trackedServices = celix_arrayList_create(); - tracker->untrackedServiceCount = 0; + celixThreadMutex_create(&tracker->state.mutex, NULL); + celixThreadCondition_init(&tracker->state.condTracked, NULL); + celixThreadCondition_init(&tracker->state.condUntracking, NULL); + tracker->state.trackedServices = celix_arrayList_create(); + tracker->state.untrackedServiceCount = 0; - tracker->currentHighestServiceId = -1; + tracker->state.currentHighestServiceId = -1; tracker->listener.handle = tracker; tracker->listener.serviceChanged = (void *) serviceTracker_serviceChanged; @@ -137,19 +137,19 @@ celix_status_t serviceTracker_destroy(service_tracker_pt tracker) { free(tracker->filter); celixThreadMutex_destroy(&tracker->closeSync.mutex); celixThreadCondition_destroy(&tracker->closeSync.cond); - celixThreadMutex_destroy(&tracker->mutex); - celixThreadCondition_destroy(&tracker->condTracked); - celixThreadCondition_destroy(&tracker->condUntracking); - celix_arrayList_destroy(tracker->trackedServices); + celixThreadMutex_destroy(&tracker->state.mutex); + celixThreadCondition_destroy(&tracker->state.condTracked); + celixThreadCondition_destroy(&tracker->state.condUntracking); + celix_arrayList_destroy(tracker->state.trackedServices); free(tracker); return CELIX_SUCCESS; } celix_status_t serviceTracker_open(service_tracker_pt tracker) { celix_status_t status = CELIX_SUCCESS; - celixThreadMutex_lock(&tracker->mutex); + celixThreadMutex_lock(&tracker->state.mutex); bool needOpening = false; - switch (tracker->state) { + switch (tracker->state.lifecycleState) { case CELIX_SERVICE_TRACKER_OPENING: celix_bundleContext_log(tracker->context, CELIX_LOG_LEVEL_WARNING, "Cannot open opening tracker"); status = CELIX_ILLEGAL_STATE; @@ -158,7 +158,7 @@ celix_status_t serviceTracker_open(service_tracker_pt tracker) { //already open, silently ignore. break; case CELIX_SERVICE_TRACKER_CLOSED: - tracker->state = CELIX_SERVICE_TRACKER_OPENING; + tracker->state.lifecycleState = CELIX_SERVICE_TRACKER_OPENING; needOpening = true; break; case CELIX_SERVICE_TRACKER_CLOSING: @@ -166,13 +166,13 @@ celix_status_t serviceTracker_open(service_tracker_pt tracker) { status = CELIX_ILLEGAL_STATE; break; } - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_unlock(&tracker->state.mutex); if (needOpening) { bundleContext_addServiceListener(tracker->context, &tracker->listener, tracker->filter); - celixThreadMutex_lock(&tracker->mutex); - tracker->state = CELIX_SERVICE_TRACKER_OPEN; - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_lock(&tracker->state.mutex); + tracker->state.lifecycleState = CELIX_SERVICE_TRACKER_OPEN; + celixThreadMutex_unlock(&tracker->state.mutex); } return status; @@ -184,15 +184,15 @@ celix_status_t serviceTracker_close(service_tracker_t* tracker) { celix_status_t status = CELIX_SUCCESS; - celixThreadMutex_lock(&tracker->mutex); + celixThreadMutex_lock(&tracker->state.mutex); bool needClosing = false; - switch (tracker->state) { + switch (tracker->state.lifecycleState) { case CELIX_SERVICE_TRACKER_OPENING: celix_bundleContext_log(tracker->context, CELIX_LOG_LEVEL_WARNING, "Cannot close opening tracker"); status = CELIX_ILLEGAL_STATE; break; case CELIX_SERVICE_TRACKER_OPEN: - tracker->state = CELIX_SERVICE_TRACKER_CLOSING; + tracker->state.lifecycleState = CELIX_SERVICE_TRACKER_CLOSING; needClosing = true; break; case CELIX_SERVICE_TRACKER_CLOSING: @@ -203,7 +203,7 @@ celix_status_t serviceTracker_close(service_tracker_t* tracker) { //silently ignore break; } - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_unlock(&tracker->state.mutex); if (needClosing) { //indicate that the service tracking is closing and wait for the still pending service registration events. @@ -216,37 +216,37 @@ celix_status_t serviceTracker_close(service_tracker_t* tracker) { int nrOfTrackedEntries; do { - celixThreadMutex_lock(&tracker->mutex); + celixThreadMutex_lock(&tracker->state.mutex); celix_tracked_entry_t *tracked = NULL; - nrOfTrackedEntries = celix_arrayList_size(tracker->trackedServices); + nrOfTrackedEntries = celix_arrayList_size(tracker->state.trackedServices); if (nrOfTrackedEntries > 0) { - tracked = celix_arrayList_get(tracker->trackedServices, 0); - celix_arrayList_removeAt(tracker->trackedServices, 0); - tracker->untrackedServiceCount++; + tracked = celix_arrayList_get(tracker->state.trackedServices, 0); + celix_arrayList_removeAt(tracker->state.trackedServices, 0); + tracker->state.untrackedServiceCount++; } - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_unlock(&tracker->state.mutex); if (tracked != NULL) { int currentSize = nrOfTrackedEntries - 1; serviceTracker_untrackTracked(tracker, tracked, currentSize, currentSize == 0); - celixThreadMutex_lock(&tracker->mutex); - tracker->untrackedServiceCount--; - celixThreadCondition_broadcast(&tracker->condUntracking); - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_lock(&tracker->state.mutex); + tracker->state.untrackedServiceCount--; + celixThreadCondition_broadcast(&tracker->state.condUntracking); + celixThreadMutex_unlock(&tracker->state.mutex); } - celixThreadMutex_lock(&tracker->mutex); - nrOfTrackedEntries = celix_arrayList_size(tracker->trackedServices); - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_lock(&tracker->state.mutex); + nrOfTrackedEntries = celix_arrayList_size(tracker->state.trackedServices); + celixThreadMutex_unlock(&tracker->state.mutex); } while (nrOfTrackedEntries > 0); fw_removeServiceListener(tracker->context->framework, tracker->context->bundle, &tracker->listener); - celixThreadMutex_lock(&tracker->mutex); - tracker->state = CELIX_SERVICE_TRACKER_CLOSED; - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_lock(&tracker->state.mutex); + tracker->state.lifecycleState = CELIX_SERVICE_TRACKER_CLOSED; + celixThreadMutex_unlock(&tracker->state.mutex); } return status; @@ -257,12 +257,12 @@ service_reference_pt serviceTracker_getServiceReference(service_tracker_t* track service_reference_pt result = NULL; - celixThreadMutex_lock(&tracker->mutex); - if(celix_arrayList_size(tracker->trackedServices) > 0) { - celix_tracked_entry_t *tracked = celix_arrayList_get(tracker->trackedServices, 0); + celixThreadMutex_lock(&tracker->state.mutex); + if(celix_arrayList_size(tracker->state.trackedServices) > 0) { + celix_tracked_entry_t *tracked = celix_arrayList_get(tracker->state.trackedServices, 0); result = tracked->reference; } - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_unlock(&tracker->state.mutex); return result; } @@ -271,12 +271,12 @@ celix_array_list_t* serviceTracker_getServiceReferences(service_tracker_t* track //TODO deprecated warning -> not locked celix_array_list_t* references = celix_arrayList_create(); - celixThreadMutex_lock(&tracker->mutex); - for (int i = 0; i < celix_arrayList_size(tracker->trackedServices); i++) { - celix_tracked_entry_t *tracked = celix_arrayList_get(tracker->trackedServices, i); + celixThreadMutex_lock(&tracker->state.mutex); + for (int i = 0; i < celix_arrayList_size(tracker->state.trackedServices); i++) { + celix_tracked_entry_t *tracked = celix_arrayList_get(tracker->state.trackedServices, i); celix_arrayList_add(references, tracked->reference); } - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_unlock(&tracker->state.mutex); return references; } @@ -285,12 +285,12 @@ void *serviceTracker_getService(service_tracker_t* tracker) { //TODO deprecated warning -> not locked void *service = NULL; - celixThreadMutex_lock(&tracker->mutex); - if(celix_arrayList_size(tracker->trackedServices) > 0) { - celix_tracked_entry_t* tracked = celix_arrayList_get(tracker->trackedServices, 0); + celixThreadMutex_lock(&tracker->state.mutex); + if(celix_arrayList_size(tracker->state.trackedServices) > 0) { + celix_tracked_entry_t* tracked = celix_arrayList_get(tracker->state.trackedServices, 0); service = tracked->service; } - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_unlock(&tracker->state.mutex); return service; } @@ -299,12 +299,12 @@ celix_array_list_t* serviceTracker_getServices(service_tracker_t* tracker) { //TODO deprecated warning -> not locked, also make locked variant celix_array_list_t* references = celix_arrayList_create(); - celixThreadMutex_lock(&tracker->mutex); - for (int i = 0; i < celix_arrayList_size(tracker->trackedServices); i++) { - celix_tracked_entry_t *tracked = celix_arrayList_get(tracker->trackedServices, i); + celixThreadMutex_lock(&tracker->state.mutex); + for (int i = 0; i < celix_arrayList_size(tracker->state.trackedServices); i++) { + celix_tracked_entry_t *tracked = celix_arrayList_get(tracker->state.trackedServices, i); celix_arrayList_add(references, tracked->service); } - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_unlock(&tracker->state.mutex); return references; } @@ -313,17 +313,17 @@ void *serviceTracker_getServiceByReference(service_tracker_pt tracker, service_r //TODO deprecated warning -> not locked void *service = NULL; - celixThreadMutex_lock(&tracker->mutex); - for (int i = 0; i < celix_arrayList_size(tracker->trackedServices); i++) { + celixThreadMutex_lock(&tracker->state.mutex); + for (int i = 0; i < celix_arrayList_size(tracker->state.trackedServices); i++) { bool equals = false; - celix_tracked_entry_t *tracked = celix_arrayList_get(tracker->trackedServices, i); + celix_tracked_entry_t *tracked = celix_arrayList_get(tracker->state.trackedServices, i); serviceReference_equals(reference, tracked->reference, &equals); if (equals) { service = tracked->service; break; } } - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_unlock(&tracker->state.mutex); return service; } @@ -364,9 +364,9 @@ static void serviceTracker_serviceChanged(void *handle, celix_service_event_t *e } size_t serviceTracker_nrOfTrackedServices(service_tracker_t *tracker) { - celixThreadMutex_lock(&tracker->mutex); - size_t result = (size_t) celix_arrayList_size(tracker->trackedServices); - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_lock(&tracker->state.mutex); + size_t result = (size_t) celix_arrayList_size(tracker->state.trackedServices); + celixThreadMutex_unlock(&tracker->state.mutex); return result; } @@ -377,10 +377,10 @@ static celix_status_t serviceTracker_track(service_tracker_t* tracker, service_r bundleContext_retainServiceReference(tracker->context, reference); - celixThreadMutex_lock(&tracker->mutex); - for (int i = 0; i < celix_arrayList_size(tracker->trackedServices); i++) { + celixThreadMutex_lock(&tracker->state.mutex); + for (int i = 0; i < celix_arrayList_size(tracker->state.trackedServices); i++) { bool equals = false; - celix_tracked_entry_t *visit = (celix_tracked_entry_t*) celix_arrayList_get(tracker->trackedServices, i); + celix_tracked_entry_t *visit = (celix_tracked_entry_t*) celix_arrayList_get(tracker->state.trackedServices, i); serviceReference_equals(reference, visit->reference, &equals); if (equals) { //NOTE it is possible to get two REGISTERED events, second one can be ignored. @@ -388,7 +388,7 @@ static celix_status_t serviceTracker_track(service_tracker_t* tracker, service_r break; } } - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_unlock(&tracker->state.mutex); if (found == NULL) { //NEW entry @@ -409,10 +409,10 @@ static celix_status_t serviceTracker_track(service_tracker_t* tracker, service_r celix_tracked_entry_t *tracked = tracked_create(reference, service, props, bnd); //use count 1 - celixThreadMutex_lock(&tracker->mutex); - celix_arrayList_add(tracker->trackedServices, tracked); - celixThreadCondition_broadcast(&tracker->condTracked); - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_lock(&tracker->state.mutex); + celix_arrayList_add(tracker->state.trackedServices, tracked); + celixThreadCondition_broadcast(&tracker->state.condTracked); + celixThreadMutex_unlock(&tracker->state.mutex); if (tracker->set != NULL || tracker->setWithProperties != NULL || tracker->setWithOwner != NULL) { celix_serviceTracker_useHighestRankingService(tracker, NULL, 0, tracker, NULL, NULL, @@ -442,13 +442,13 @@ static void serviceTracker_checkAndInvokeSetService(void *handle, void *highestS svcId = celix_properties_getAsLong(props, CELIX_FRAMEWORK_SERVICE_ID, -1); } if (svcId >= 0) { - celixThreadMutex_lock(&tracker->mutex); - if (tracker->currentHighestServiceId != svcId) { - tracker->currentHighestServiceId = svcId; + celixThreadMutex_lock(&tracker->state.mutex); + if (tracker->state.currentHighestServiceId != svcId) { + tracker->state.currentHighestServiceId = svcId; update = true; //update } - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_unlock(&tracker->state.mutex); } if (update) { void *h = tracker->callbackHandle; @@ -516,36 +516,36 @@ static celix_status_t serviceTracker_untrack(service_tracker_t* tracker, service celix_status_t status = CELIX_SUCCESS; celix_tracked_entry_t *remove = NULL; - celixThreadMutex_lock(&tracker->mutex); - for (int i = 0; i < celix_arrayList_size(tracker->trackedServices); i++) { + celixThreadMutex_lock(&tracker->state.mutex); + for (int i = 0; i < celix_arrayList_size(tracker->state.trackedServices); i++) { bool equals; - celix_tracked_entry_t *tracked = celix_arrayList_get(tracker->trackedServices, i); + celix_tracked_entry_t *tracked = celix_arrayList_get(tracker->state.trackedServices, i); serviceReference_equals(reference, tracked->reference, &equals); if (equals) { remove = tracked; //remove from trackedServices to prevent getting this service, but don't destroy yet, can be in use - celix_arrayList_removeAt(tracker->trackedServices, i); - tracker->untrackedServiceCount++; + celix_arrayList_removeAt(tracker->state.trackedServices, i); + tracker->state.untrackedServiceCount++; break; } } - int size = celix_arrayList_size(tracker->trackedServices); //updated size - celixThreadMutex_unlock(&tracker->mutex); + int size = celix_arrayList_size(tracker->state.trackedServices); //updated size + celixThreadMutex_unlock(&tracker->state.mutex); //note also syncing on untracking entries, to ensure no untrack is parallel in progress if (remove != NULL) { serviceTracker_untrackTracked(tracker, remove, size, true); - celixThreadMutex_lock(&tracker->mutex); - tracker->untrackedServiceCount--; - celixThreadCondition_broadcast(&tracker->condUntracking); - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_lock(&tracker->state.mutex); + tracker->state.untrackedServiceCount--; + celixThreadCondition_broadcast(&tracker->state.condUntracking); + celixThreadMutex_unlock(&tracker->state.mutex); } else { //ensure no untrack is still happening (to ensure it safe to unregister service) - celixThreadMutex_lock(&tracker->mutex); - while (tracker->untrackedServiceCount > 0) { - celixThreadCondition_wait(&tracker->condUntracking, &tracker->mutex); + celixThreadMutex_lock(&tracker->state.mutex); + while (tracker->state.untrackedServiceCount > 0) { + celixThreadCondition_wait(&tracker->state.condUntracking, &tracker->state.mutex); } - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_unlock(&tracker->state.mutex); } framework_logIfError(tracker->context->framework->logger, status, NULL, "Cannot untrack reference"); @@ -664,7 +664,7 @@ celix_service_tracker_t* celix_serviceTracker_createClosedWithOptions(celix_bund tracker->context = ctx; tracker->serviceName = celix_utils_strdup(serviceName); tracker->filter = filter; - tracker->state = CELIX_SERVICE_TRACKER_CLOSED; + tracker->state.lifecycleState = CELIX_SERVICE_TRACKER_CLOSED; // setting callbacks tracker->callbackHandle = opts->callbackHandle; @@ -681,12 +681,12 @@ celix_service_tracker_t* celix_serviceTracker_createClosedWithOptions(celix_bund celixThreadMutex_create(&tracker->closeSync.mutex, NULL); celixThreadCondition_init(&tracker->closeSync.cond, NULL); - celixThreadMutex_create(&tracker->mutex, NULL); - celixThreadCondition_init(&tracker->condTracked, NULL); - celixThreadCondition_init(&tracker->condUntracking, NULL); - tracker->trackedServices = celix_arrayList_create(); - tracker->untrackedServiceCount = 0; - tracker->currentHighestServiceId = -1; + celixThreadMutex_create(&tracker->state.mutex, NULL); + celixThreadCondition_init(&tracker->state.condTracked, NULL); + celixThreadCondition_init(&tracker->state.condUntracking, NULL); + tracker->state.trackedServices = celix_arrayList_create(); + tracker->state.untrackedServiceCount = 0; + tracker->state.currentHighestServiceId = -1; tracker->listener.handle = tracker; tracker->listener.serviceChanged = (void*)serviceTracker_serviceChanged; @@ -716,8 +716,8 @@ static celix_tracked_entry_t* celix_serviceTracker_findHighestRankingService(ser const char* serviceName) { // precondition tracker->mutex locked celix_tracked_entry_t* highest = NULL; - for (int i = 0; i < celix_arrayList_size(tracker->trackedServices); ++i) { - celix_tracked_entry_t* tracked = celix_arrayList_get(tracker->trackedServices, i); + for (int i = 0; i < celix_arrayList_size(tracker->state.trackedServices); ++i) { + celix_tracked_entry_t* tracked = celix_arrayList_get(tracker->state.trackedServices, i); if (serviceName == NULL || (tracked->serviceName != NULL && celix_utils_stringEquals(tracked->serviceName, serviceName))) { if (highest == NULL) { @@ -742,11 +742,12 @@ bool celix_serviceTracker_useHighestRankingService(service_tracker_t *tracker, void (*useWithProperties)(void *handle, void *svc, const celix_properties_t *props), void (*useWithOwner)(void *handle, void *svc, const celix_properties_t *props, const celix_bundle_t *owner)) { //first lock tracker and get highest ranking tracked entry - celixThreadMutex_lock(&tracker->mutex); + celixThreadMutex_lock(&tracker->state.mutex); struct timespec absTime = celixThreadCondition_getDelayedTime(waitTimeoutInSeconds); celix_tracked_entry_t* highest = celix_serviceTracker_findHighestRankingService(tracker, serviceName); while (highest == NULL && waitTimeoutInSeconds > 0) { - celix_status_t waitStatus = celixThreadCondition_waitUntil(&tracker->condTracked, &tracker->mutex, &absTime); + celix_status_t waitStatus = + celixThreadCondition_waitUntil(&tracker->state.condTracked, &tracker->state.mutex, &absTime); if (waitStatus == ETIMEDOUT) { break; } @@ -757,7 +758,7 @@ bool celix_serviceTracker_useHighestRankingService(service_tracker_t *tracker, tracked_retain(highest); } // unlock tracker so that the tracked entry can be removed from the trackedServices list if unregistered. - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_unlock(&tracker->state.mutex); bool called = false; if (highest) { @@ -787,17 +788,17 @@ size_t celix_serviceTracker_useServices( void (*useWithOwner)(void *handle, void *svc, const celix_properties_t *props, const celix_bundle_t *owner)) { size_t count = 0; //first lock tracker, get tracked entries and increase use count - celixThreadMutex_lock(&tracker->mutex); - int size = celix_arrayList_size(tracker->trackedServices); + celixThreadMutex_lock(&tracker->state.mutex); + int size = celix_arrayList_size(tracker->state.trackedServices); count = (size_t)size; celix_tracked_entry_t *entries[size]; for (int i = 0; i < size; i++) { - celix_tracked_entry_t *tracked = celix_arrayList_get(tracker->trackedServices, i); + celix_tracked_entry_t *tracked = celix_arrayList_get(tracker->state.trackedServices, i); tracked_retain(tracked); entries[i] = tracked; } //unlock tracker so that the tracked entry can be removed from the trackedServices list if unregistered. - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_unlock(&tracker->state.mutex); //then use entries and decrease use count for (int i = 0; i < size; i++) { @@ -819,16 +820,14 @@ size_t celix_serviceTracker_useServices( } size_t celix_serviceTracker_getTrackedServiceCount(celix_service_tracker_t *tracker) { - celix_auto(celix_mutex_lock_guard_t) lck = celixMutexLockGuard_init(&tracker->mutex); - return (size_t) celix_arrayList_size(tracker->trackedServices); + celix_auto(celix_mutex_lock_guard_t) lck = celixMutexLockGuard_init(&tracker->state.mutex); + return (size_t) celix_arrayList_size(tracker->state.trackedServices); } const char* celix_serviceTracker_getTrackedServiceName(celix_service_tracker_t *tracker) { - celix_auto(celix_mutex_lock_guard_t) lck = celixMutexLockGuard_init(&tracker->mutex); return tracker->serviceName; } const char* celix_serviceTracker_getTrackedServiceFilter(celix_service_tracker_t *tracker) { - celix_auto(celix_mutex_lock_guard_t) lck = celixMutexLockGuard_init(&tracker->mutex); return tracker->filter; } diff --git a/libs/framework/src/service_tracker_private.h b/libs/framework/src/service_tracker_private.h index a2ca4fccc..ea3653deb 100644 --- a/libs/framework/src/service_tracker_private.h +++ b/libs/framework/src/service_tracker_private.h @@ -31,45 +31,52 @@ enum celix_service_tracker_state { }; struct celix_serviceTracker { - bundle_context_t *context; + //const after init + bundle_context_t* context; - char* serviceName; - char* filter; + char* filter; + char* serviceName; service_tracker_customizer_t customizer; celix_service_listener_t listener; - void *callbackHandle; + void* callbackHandle; - void (*set)(void *handle, void *svc); //highest ranking - void (*add)(void *handle, void *svc); - void (*remove)(void *handle, void *svc); - void (*modified)(void *handle, void *svc); + void (*set)(void* handle, void* svc); // highest ranking + void (*add)(void* handle, void* svc); + void (*remove)(void* handle, void* svc); + void (*modified)(void* handle, void* svc); - void (*setWithProperties)(void *handle, void *svc, const celix_properties_t* props); //highest ranking - void (*addWithProperties)(void *handle, void *svc, const celix_properties_t* props); - void (*removeWithProperties)(void *handle, void *svc, const celix_properties_t* props); - void (*modifiedWithProperties)(void *handle, void *svc, const celix_properties_t* props); + void (*setWithProperties)(void* handle, void* svc, const celix_properties_t* props); // highest ranking + void (*addWithProperties)(void* handle, void* svc, const celix_properties_t* props); + void (*removeWithProperties)(void* handle, void* svc, const celix_properties_t* props); + void (*modifiedWithProperties)(void* handle, void* svc, const celix_properties_t* props); - void (*setWithOwner)(void *handle, void *svc, const celix_properties_t* props, const bundle_t *owner); //highest ranking - void (*addWithOwner)(void *handle, void *svc, const celix_properties_t* props, const bundle_t *owner); - void (*removeWithOwner)(void *handle, void *svc, const celix_properties_t* props, const bundle_t *owner); - void (*modifiedWithOwner)(void *handle, void *svc, const celix_properties_t* props, const bundle_t *owner); + void (*setWithOwner)(void* handle, + void* svc, + const celix_properties_t* props, + const bundle_t* owner); // highest ranking + void (*addWithOwner)(void* handle, void* svc, const celix_properties_t* props, const bundle_t* owner); + void (*removeWithOwner)(void* handle, void* svc, const celix_properties_t* props, const bundle_t* owner); + void (*modifiedWithOwner)(void* handle, void* svc, const celix_properties_t* props, const bundle_t* owner); + //end const after init struct { - celix_thread_mutex_t mutex; //projects below + celix_thread_mutex_t mutex; // projects below celix_thread_cond_t cond; - bool closing; //if closing is true, no new service registrations are added to the serice tracker + bool closing; // if closing is true, no new service registrations are added to the service tracker size_t activeCalls; // >0 if there is still a serviceChange for a service registration call active } closeSync; - celix_thread_mutex_t mutex; //projects below - celix_thread_cond_t condTracked; - celix_thread_cond_t condUntracking; - celix_array_list_t *trackedServices; - size_t untrackedServiceCount; - enum celix_service_tracker_state state; - long currentHighestServiceId; + struct { + celix_thread_mutex_t mutex; // projects below + celix_thread_cond_t condTracked; + celix_thread_cond_t condUntracking; + celix_array_list_t* trackedServices; + size_t untrackedServiceCount; + enum celix_service_tracker_state lifecycleState; + long currentHighestServiceId; + } state; }; typedef struct celix_tracked_entry { From eb6e69952e7ba7cb47dcb5c46745684484d89077 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 25 Feb 2024 17:08:46 +0100 Subject: [PATCH 06/14] gh-87: Refactor bnd ctx to use a rwlock instead of mutex. Also: - Remove celix_bundleContext_trackService and celix_bundleContext_trackServiceAsync functions. - Update doxygen for useTrackedService* calls. - Remove celix_bundle_destroyServiceTrackerList. function by return an array list with a configured remove callback. - Remove waitTimeoutInSeconds option for celix_tracked_service_use_options_t. --- CHANGES.md | 2 + .../log_admin/gtest/src/LogAdminTestSuite.cc | 6 +- bundles/shell/shell/src/query_command.c | 2 +- .../src/CelixBundleContextBundlesTestSuite.cc | 2 +- .../CelixBundleContextServicesTestSuite.cc | 2 +- libs/framework/include/celix_bundle.h | 12 +- libs/framework/include/celix_bundle_context.h | 297 +++++++++--------- libs/framework/src/bundle.c | 31 +- libs/framework/src/bundle_context.c | 110 +++---- libs/framework/src/bundle_context_private.h | 4 +- 10 files changed, 231 insertions(+), 237 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 39d56c315..8dab49dc3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -63,6 +63,8 @@ limitations under the License. - The signature of `celix_bundleContext_trackServices` has changed. The signature is now simpler to better support the use-case of using a service tracker with the `celix_bundleContext_useTrackedService*` functions. The `celix_bundleContext_trackServicesWithOptions` is still available for more advanced use-cases. +- Function `celix_bundle_destroyServiceTrackerList` is removed. The returned array list from + `celix_bundle_listServiceTrackers` is now configured to destroy the service trackers info entries. ## New Features diff --git a/bundles/logging/log_admin/gtest/src/LogAdminTestSuite.cc b/bundles/logging/log_admin/gtest/src/LogAdminTestSuite.cc index 606b07ecf..918f116e4 100644 --- a/bundles/logging/log_admin/gtest/src/LogAdminTestSuite.cc +++ b/bundles/logging/log_admin/gtest/src/LogAdminTestSuite.cc @@ -84,11 +84,11 @@ TEST_F(LogBundleTestSuite, NrOfLogServices) { EXPECT_EQ(1, control->nrOfLogServices(control->handle, nullptr)); //default the framework log services is available //request "default" log service - long trkId1 = celix_bundleContext_trackService(ctx.get(), CELIX_LOG_SERVICE_NAME); + long trkId1 = celix_bundleContext_trackServices(ctx.get(), CELIX_LOG_SERVICE_NAME); EXPECT_EQ(2, control->nrOfLogServices(control->handle, nullptr)); //request "default" log service -> already created - long trkId2 = celix_bundleContext_trackService(ctx.get(), CELIX_LOG_SERVICE_NAME); + long trkId2 = celix_bundleContext_trackServices(ctx.get(), CELIX_LOG_SERVICE_NAME); EXPECT_EQ(2, control->nrOfLogServices(control->handle, nullptr)); //request a 'logger1' log service @@ -225,7 +225,7 @@ TEST_F(LogBundleTestSuite, SinkLogControl) { TEST_F(LogBundleTestSuite, LogServiceControl) { //request "default" log service - long trkId1 = celix_bundleContext_trackService(ctx.get(), CELIX_LOG_SERVICE_NAME); + long trkId1 = celix_bundleContext_trackServices(ctx.get(), CELIX_LOG_SERVICE_NAME); celix_framework_waitForEmptyEventQueue(fw.get()); EXPECT_EQ(2, control->nrOfLogServices(control->handle, nullptr)); diff --git a/bundles/shell/shell/src/query_command.c b/bundles/shell/shell/src/query_command.c index 13d6fab74..49eb73444 100644 --- a/bundles/shell/shell/src/query_command.c +++ b/bundles/shell/shell/src/query_command.c @@ -118,7 +118,7 @@ static void queryCommand_callback(void *handle, const celix_bundle_t *bnd) { } } } - celix_bundle_destroyServiceTrackerList(trackers); + celix_arrayList_destroy(trackers); } if (printBundleCalled) { diff --git a/libs/framework/gtest/src/CelixBundleContextBundlesTestSuite.cc b/libs/framework/gtest/src/CelixBundleContextBundlesTestSuite.cc index 88d900c7c..01616065b 100644 --- a/libs/framework/gtest/src/CelixBundleContextBundlesTestSuite.cc +++ b/libs/framework/gtest/src/CelixBundleContextBundlesTestSuite.cc @@ -745,7 +745,7 @@ TEST_F(CelixBundleContextBundlesTestSuite, BundleInfoTests) { auto *services = celix_bundle_listRegisteredServices(bnd); data->requestedCount = celix_arrayList_size(trackers); data->provideCount = celix_arrayList_size(services); - celix_bundle_destroyServiceTrackerList(trackers); + celix_arrayList_destroy(trackers); celix_bundle_destroyRegisteredServicesList(services); }; diff --git a/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc b/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc index 3f57f7ae1..bd2894d64 100644 --- a/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc +++ b/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc @@ -1349,7 +1349,7 @@ TEST_F(CelixBundleContextServicesTestSuite, TrackServiceTrackerTest) { EXPECT_TRUE(trackerId >= 0); EXPECT_EQ(0, count); - long tracker2 = celix_bundleContext_trackService(ctx, "example"); + long tracker2 = celix_bundleContext_trackServices(ctx, "example"); EXPECT_TRUE(tracker2 >= 0); EXPECT_EQ(1, count); diff --git a/libs/framework/include/celix_bundle.h b/libs/framework/include/celix_bundle.h index fb3487094..88218cf17 100644 --- a/libs/framework/include/celix_bundle.h +++ b/libs/framework/include/celix_bundle.h @@ -173,7 +173,6 @@ typedef struct celix_bundle_service_tracker_list_entry { size_t nrOfTrackedServices; } celix_bundle_service_tracker_list_entry_t; - /** * Returns a array list of service tracker info entries for this bundle. * @@ -181,15 +180,10 @@ typedef struct celix_bundle_service_tracker_list_entry { * * @param ctx The bundle context * @param bndId The bundle id for which the services should be listed - * @return A celix array list with celix_bundle_service_tracker_list_entry_t*. Caller is owner of the celix array. + * @return A celix array list with celix_bundle_service_tracker_list_entry_t*. Caller is owner of the celix + * array. The returned list should be freed using celix_arrayList_destroy. */ -CELIX_FRAMEWORK_EXPORT celix_array_list_t* celix_bundle_listServiceTrackers(const celix_bundle_t *bnd); - -/** - * Utils function to free memory for the return of a celix_bundle_listServiceTrackers call. - */ -CELIX_FRAMEWORK_EXPORT void celix_bundle_destroyServiceTrackerList(celix_array_list_t* list); - +CELIX_FRAMEWORK_EXPORT celix_array_list_t* celix_bundle_listServiceTrackers(const celix_bundle_t* bnd); #ifdef __cplusplus } diff --git a/libs/framework/include/celix_bundle_context.h b/libs/framework/include/celix_bundle_context.h index e46996e9d..93250ac9f 100644 --- a/libs/framework/include/celix_bundle_context.h +++ b/libs/framework/include/celix_bundle_context.h @@ -391,6 +391,9 @@ CELIX_FRAMEWORK_EXPORT celix_array_list_t* celix_bundleContext_findServicesWithO * @brief Use the service with the provided service id using the provided callback. The Celix framework will ensure that * the targeted service cannot be removed during the callback. * + * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useServiceTracker* functions + * instead. + * * The svc is should only be considered valid during the callback. * If no service is found, the callback will not be invoked and this function will return false immediately. * @@ -399,22 +402,24 @@ CELIX_FRAMEWORK_EXPORT celix_array_list_t* celix_bundleContext_findServicesWithO * * @param ctx The bundle context * @param serviceId the service id. - * @param serviceName the service name of the service. Should match with the registered service name of the provided service id (sanity check) + * @param serviceName the service name of the service. Should match with the registered service name of the provided + * service id (sanity check) * @param callbackHandle The data pointer, which will be used in the callbacks * @param use The callback, which will be called when service is retrieved. * @param bool returns true if a service was found. */ -CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useServiceWithId( - celix_bundle_context_t *ctx, - long serviceId, - const char* serviceName /*sanity check*/, - void *callbackHandle, - void (*use)(void *handle, void* svc) -); +CELIX_FRAMEWORK_DEPRECATED_EXPORT bool celix_bundleContext_useServiceWithId(celix_bundle_context_t* ctx, + long serviceId, + const char* serviceName /*sanity check*/, + void* callbackHandle, + void (*use)(void* handle, void* svc)); /** * @brief Use the highest ranking service with the provided service name using the provided callback. * + * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useServiceTracker* functions + * instead. + * * The Celix framework will ensure that the targeted service cannot be removed during the callback. * * The svc is should only be considered valid during the callback. @@ -429,7 +434,7 @@ CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useServiceWithId( * @param use The callback, which will be called when service is retrieved. * @return True if a service was found. */ -CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useService( +CELIX_FRAMEWORK_DEPRECATED_EXPORT bool celix_bundleContext_useService( celix_bundle_context_t *ctx, const char* serviceName, void *callbackHandle, @@ -439,6 +444,9 @@ CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useService( /** * @brief Use the services with the provided service name using the provided callback. * + * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useServiceTracker* functions + * instead. + * * The Celix framework will ensure that the targeted service cannot be removed during the callback. * * The svc is should only be considered valid during the callback. @@ -453,7 +461,7 @@ CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useService( * @param use The callback, which will be called for every service found. * @return The number of services found and called */ -CELIX_FRAMEWORK_EXPORT size_t celix_bundleContext_useServices( +CELIX_FRAMEWORK_DEPRECATED_EXPORT size_t celix_bundleContext_useServices( celix_bundle_context_t *ctx, const char* serviceName, void *callbackHandle, @@ -540,6 +548,9 @@ typedef struct celix_service_use_options { /** * @brief Use the highest ranking service satisfying the provided service filter options using the provided callback. * + * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useServiceTracker* functions + * instead. + * * The Celix framework will ensure that the targeted service cannot be removed during the callback. * * The svc is should only be considered valid during the callback. @@ -553,7 +564,7 @@ typedef struct celix_service_use_options { * @param opts The required options. Note that the serviceName is required. * @return True if a service was found. */ -CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useServiceWithOptions( +CELIX_FRAMEWORK_DEPRECATED_EXPORT bool celix_bundleContext_useServiceWithOptions( celix_bundle_context_t *ctx, const celix_service_use_options_t *opts); @@ -561,6 +572,9 @@ CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useServiceWithOptions( /** * @brief Use the services with the provided service filter options using the provided callback. * + * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useServiceTracker* functions + * instead. + * * The Celix framework will ensure that the targeted service cannot be removed during the callback. * * The svc is should only be considered valid during the callback. @@ -574,33 +588,10 @@ CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useServiceWithOptions( * @param opts The required options. Note that the serviceName is required. * @return The number of services found and called */ -CELIX_FRAMEWORK_EXPORT size_t celix_bundleContext_useServicesWithOptions( +CELIX_FRAMEWORK_DEPRECATED_EXPORT size_t celix_bundleContext_useServicesWithOptions( celix_bundle_context_t *ctx, const celix_service_use_options_t *opts); -/** - * @brief Track the highest ranking service with the provided serviceName. - * - * The service tracker will be created async on the Celix event loop thread. This means that the function can return - * before the tracker is created. - * - * @param ctx The bundle context. - * @param serviceName The required service name to track. - * If NULL is all service are tracked. - * @return the tracker id (>=0) or < 0 if unsuccessful. - */ -CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServiceAsync(celix_bundle_context_t* ctx, const char* serviceName); - -/** - * @brief Track the highest ranking service with the provided serviceName - * - * Note: If possible, use the celix_bundleContext_trackServiceAsync instead. - * - * @param ctx The bundle context. - * @return the tracker id (>=0) or < 0 if unsuccessful. - */ -CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackService(celix_bundle_context_t* ctx, const char* serviceName); - /** * @brief Track services with the provided serviceName. * @@ -628,7 +619,8 @@ CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServicesAsync(celix_bundle_ CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServices(celix_bundle_context_t* ctx, const char* serviceName); /** - * @brief Service Tracker Options used to fine tune which services to track and the callback to be used for the tracked services. + * @brief Service Tracker Options used to fine tune which services to track and the callback to be used for the tracked + * services. */ typedef struct celix_service_tracking_options { /** @@ -637,78 +629,83 @@ typedef struct celix_service_tracking_options { celix_service_filter_options_t filter CELIX_OPTS_INIT; /** - * @brief The optional callback pointer used in all the provided callback function (set, add, remove, setWithProperties, etc). + * @brief The optional callback pointer used in all the provided callback function (set, add, remove, + * setWithProperties, etc). */ void* callbackHandle CELIX_OPTS_INIT; /** - * @brief The optional set callback will be called when a new highest ranking service is available conform the provided - * service filter options. + * @brief The optional set callback will be called when a new highest ranking service is available conform the + * provided service filter options. * @param handle The callbackHandle pointer as provided in the service tracker options. * @param svc The service pointer of the highest ranking service. */ - void (*set)(void *handle, void *svc) CELIX_OPTS_INIT; + void (*set)(void* handle, void* svc) CELIX_OPTS_INIT; /** - * @brief The optional setWithProperties callback is handled as the set callback, but with the addition that the service properties - * will also be provided to the callback. + * @brief The optional setWithProperties callback is handled as the set callback, but with the addition that the + * service properties will also be provided to the callback. */ - void (*setWithProperties)(void *handle, void *svc, const celix_properties_t *props) CELIX_OPTS_INIT; //highest ranking + void (*setWithProperties)(void* handle, + void* svc, + const celix_properties_t* props) CELIX_OPTS_INIT; // highest ranking /** - * @brief The optional setWithOwner callback is handled as the set callback, but with the addition that the service properties - * and the bundle owning the service will also be provided to the callback. + * @brief The optional setWithOwner callback is handled as the set callback, but with the addition that the service + * properties and the bundle owning the service will also be provided to the callback. */ - void (*setWithOwner)(void *handle, void *svc, const celix_properties_t *props, const celix_bundle_t *svcOwner) CELIX_OPTS_INIT; //highest ranking + void (*setWithOwner)(void* handle, void* svc, const celix_properties_t* props, const celix_bundle_t* svcOwner) + CELIX_OPTS_INIT; /** - * @brief The optional add callback will be called for every current and future service found conform the provided service filter - * options as long as the tracker is active. + * @brief The optional add callback will be called for every current and future service found conform the provided + * service filter options as long as the tracker is active. * @param handle The callbackHandle pointer as provided in the service tracker options. * @param svc The service pointer of a service matching the provided service filter options. */ - void (*add)(void *handle, void *svc) CELIX_OPTS_INIT; + void (*add)(void* handle, void* svc) CELIX_OPTS_INIT; /** - * @brief The optional addWithProperties callback is handled as the add callback, but with the addition that the service properties - * will also be provided to the callback. + * @brief The optional addWithProperties callback is handled as the add callback, but with the addition that the + * service properties will also be provided to the callback. */ - void (*addWithProperties)(void *handle, void *svc, const celix_properties_t *props) CELIX_OPTS_INIT; + void (*addWithProperties)(void* handle, void* svc, const celix_properties_t* props) CELIX_OPTS_INIT; /** - * @brief The optional addWithOwner callback is handled as the add callback, but with the addition that the service properties - * and the bundle owning the service will also be provided to the callback. + * @brief The optional addWithOwner callback is handled as the add callback, but with the addition that the service + * properties and the bundle owning the service will also be provided to the callback. */ - void (*addWithOwner)(void *handle, void *svc, const celix_properties_t *props, const celix_bundle_t *svcOwner) CELIX_OPTS_INIT; + void (*addWithOwner)(void* handle, void* svc, const celix_properties_t* props, const celix_bundle_t* svcOwner) + CELIX_OPTS_INIT; /** - * @brief The optional remove callback will be called for every service conform the provided service filter options that is - * unregistered. When the remove call is finished the removed services should be considered invalid. This means - * that the callback provider should ensure that the removed service is not in use or going to be used after the - * remove callback is finished. + * @brief The optional remove callback will be called for every service conform the provided service filter options + * that is unregistered. When the remove call is finished the removed services should be considered invalid. This + * means that the callback provider should ensure that the removed service is not in use or going to be used after + * the remove callback is finished. * * @param handle The callbackHandle pointer as provided in the service tracker options. * @param svc The service pointer of a service matching the provided service filter options. */ - void (*remove)(void *handle, void *svc) CELIX_OPTS_INIT; + void (*remove)(void* handle, void* svc) CELIX_OPTS_INIT; /** - * @brief The optional removeWithProperties callback is handled as the remove callback, but with the addition that the service properties - * will also be provided to the callback. + * @brief The optional removeWithProperties callback is handled as the remove callback, but with the addition that + * the service properties will also be provided to the callback. */ - void (*removeWithProperties)(void *handle, void *svc, const celix_properties_t *props) CELIX_OPTS_INIT; + void (*removeWithProperties)(void* handle, void* svc, const celix_properties_t* props) CELIX_OPTS_INIT; /** - * @brief The optional removeWithOwner callback is handled as the remove callback, but with the addition that the service properties - * and the bundle owning the service will also be provided to the callback. - */ - void (*removeWithOwner)(void *handle, void *svc, const celix_properties_t *props, const celix_bundle_t *svcOwner) CELIX_OPTS_INIT; - + * @brief The optional removeWithOwner callback is handled as the remove callback, but with the addition that the + * service properties and the bundle owning the service will also be provided to the callback. + */ + void (*removeWithOwner)(void* handle, void* svc, const celix_properties_t* props, const celix_bundle_t* svcOwner) + CELIX_OPTS_INIT; /** * @brief Data for the trackerCreatedCallback. */ - void *trackerCreatedCallbackData CELIX_OPTS_INIT; + void* trackerCreatedCallbackData CELIX_OPTS_INIT; /** * @brief The callback called when the tracker has ben created (and is active) when using a async call. @@ -717,7 +714,7 @@ typedef struct celix_service_tracking_options { * "stop tracker" happens before the "create tracker" event is processed. In this case the asyncCallback * will not be called. */ - void (*trackerCreatedCallback)(void *trackerCreatedCallbackData) CELIX_OPTS_INIT; + void (*trackerCreatedCallback)(void* trackerCreatedCallbackData) CELIX_OPTS_INIT; } celix_service_tracking_options_t; #ifndef __cplusplus @@ -753,7 +750,9 @@ typedef struct celix_service_tracking_options { * @param opts The pointer to the tracker options. * @return the tracker id (>=0) or < 0 if unsuccessful. */ -CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServicesWithOptionsAsync(celix_bundle_context_t *ctx, const celix_service_tracking_options_t *opts); +CELIX_FRAMEWORK_EXPORT long +celix_bundleContext_trackServicesWithOptionsAsync(celix_bundle_context_t* ctx, + const celix_service_tracking_options_t* opts); /** * @brief Tracks services using the provided tracker options. @@ -765,23 +764,25 @@ CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServicesWithOptionsAsync(ce * @param opts The pointer to the tracker options. * @return the tracker id (>=0) or < 0 if unsuccessful. */ -CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServicesWithOptions(celix_bundle_context_t *ctx, const celix_service_tracking_options_t *opts); +CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServicesWithOptions(celix_bundle_context_t* ctx, + const celix_service_tracking_options_t* opts); /** - * @brief Use the highest ranking service, tracked by the provided tracker id, using the provided callback. + * @brief Use the highest-ranking service tracked by the specified tracker id by invoking the provided use callback. * - * If an service is found the use callback will be called on the thread that called this function and when function - * returns the callback is finished. + * The callback is executed on the thread that invokes this function, and the function waits until the use callback + * is completed before returning. * - * An tracker id < 0 will be silently ignored. - * An invalid (non existing) tracker id >= 0 will be logged and the function will return false. + * A tracker id less than 0 is ignored without action. An invalid (non-existent) tracker id of 0 or greater results + * in a logged error, and the function returns false. * * @param[in] ctx The bundle context. - * @param[in] trackerId The tracker id. - * @param[in] callbackHandle The data pointer, which will be used in the callbacks. - * @param[in] use The callback, which will be called when service is retrieved. - * @return True if a service was found and the use callback was called. Returns false if the tracker is not yet active - * (async tracker creation). + * @param[in] trackerId The service tracker id. + * @param[in] callbackHandle An optional pointer to a user-defined context or data structure, passed to the provided use + * callback function. + * @param[in] use The use callback invoked for highest-ranking service tracked. + * @return True if a service was found and the provided use callback was executed; false if the tracker is not yet + * active (asynchronous tracker creation), the are no matching services tracked or if the tracker id is invalid. */ CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useTrackedService(celix_bundle_context_t* ctx, @@ -790,20 +791,21 @@ bool celix_bundleContext_useTrackedService(celix_bundle_context_t* ctx, void (*use)(void* handle, void* svc)); /** - * @brief Use the services, tracked by the provided tracker id, using the provided callback. + * @brief Use the services tracked by the specified tracker id by invoking the provided use callback. * - * If 1 or more services is found the use callback will be called for every service found on the thread that called this - * function and when function returns the callbacks are finished. + * The callback is executed on the thread that invokes this function, and the function waits until the use callback, + * called for all tracked. services, is completed before returning. * - * An tracker id < 0 will be silently ignored. - * An invalid (non existing) tracker id >= 0 will be logged and the function will return 0. + * A tracker id less than 0 is ignored without action. An invalid (non-existent) tracker id of 0 or greater results + * in a logged error, and the function returns 0. * * @param[in] ctx The bundle context. - * @param[in] trackerId The tracker id. - * @param[in] callbackHandle The data pointer, which will be used in the callbacks. - * @param[in] use The callback, which will be called for every service found. - * @return The number of services found and therefore the number of times the use callback was called. Returns 0 if the - * tracker is not yet active (async tracker creation). + * @param[in] trackerId The service tracker id. + * @param[in] callbackHandle An optional pointer to a user-defined context or data structure, passed to the provided use + * callback function. + * @param[in] use The use callback invoked for all tracked services. + * @return The number of services found. Returns 0 if the tracker + * is not yet active (asynchronous tracker creation), 0 services are found or if the tracker id is invalid. */ CELIX_FRAMEWORK_EXPORT size_t celix_bundleContext_useTrackedServices(celix_bundle_context_t* ctx, @@ -812,75 +814,75 @@ size_t celix_bundleContext_useTrackedServices(celix_bundle_context_t* ctx, void (*use)(void* handle, void* svc)); /** - * @brief Use tracked service options used to configure which use callback to use on the tracked services. - * If multiple use callbacks are set, all set callbacks will be called for every service found. + * @brief Options for using services tracked by a service tracker. These options enable specifying callbacks + * to be invoked for each tracked service that matches the selection criteria. If multiple callbacks are provided, + * each will be called for every matching service. */ typedef struct celix_tracked_service_use_options { /** - * @brief An optional timeout (in seconds), if > 0 the use tracked service(s) call will block until the timeout is - * expired or when at least one service is found. Note that it will be ignored when use service on the event loop. - * Default (0) + * @brief An optional pointer to a user-defined context or data structure, passed to all specified callback + * functions (use, useWithProperties, and useWithOwner). * - * Only applicable when using the celix_bundleContext_useTrackedService or - * celix_bundleContext_useTrackedServiceWithOptions (use single service calls). + * Default value: NULL. */ - double waitTimeoutInSeconds CELIX_OPTS_INIT; - - /** - * @brief The optional callback pointer used in all the provided callback function (use, useWithProperties and useWithOwner). - */ - void *callbackHandle CELIX_OPTS_INIT; + void* callbackHandle CELIX_OPTS_INIT; /** - * @brief The optional use callback will be called when for every service tracked. + * @brief An optional callback invoked for each tracked service that matches the selection criteria. + * This callback does not receive the service's properties or information about the service's owning bundle. * - * @param handle The callbackHandle pointer as provided in the service tracker options. - * @param svc The service pointer of the highest ranking service. + * The svc pointer is only valid during the callback. + * + * Default value: NULL. */ - void (*use)(void *handle, void *svc) CELIX_OPTS_INIT; + void (*use)(void* handle, void* svc) CELIX_OPTS_INIT; /** - * @brief The optional useWithProperties callback will be called when for every service tracked and the - * service properties will also be provided to the callback. + * @brief An optional callback invoked for each tracked service that matches the selection criteria, + * providing the service's properties. This enables the callback to utilize additional metadata associated + * with the service. + * + * The svc and props pointers are only valid during the callback. + * + * Default value: NULL. */ - void (*useWithProperties)(void *handle, void *svc, const celix_properties_t *props) CELIX_OPTS_INIT; + void (*useWithProperties)(void* handle, void* svc, const celix_properties_t* props) CELIX_OPTS_INIT; /** - * @brief The optional useWithOwner callback will be called when for every service tracked and the - * service properties and the bundle owning the service will also be provided to the callback. + * @brief An optional callback invoked for each tracked service that matches the selection criteria, + * along with the service's properties and the service's owning bundle. + * + * The svc, props, and svcOwner pointers are only valid during the callback. + * + * Default value: NULL. */ - void (*useWithOwner)(void *handle, void *svc, const celix_properties_t *props, const celix_bundle_t *svcOwner) CELIX_OPTS_INIT; + void (*useWithOwner)(void* handle, void* svc, const celix_properties_t* props, const celix_bundle_t* svcOwner) + CELIX_OPTS_INIT; } celix_tracked_service_use_options_t; #ifndef __cplusplus -/*! - * @brief C Macro to create a empty celix_tracked_service_use_options_t type. +/** + * @brief Macro to initialize a celix_tracked_service_use_options_t structure with default values. */ #define CELIX_EMPTY_TRACKER_SERVICE_USE_OPTIONS \ - { \ - .waitTimeoutInSeconds = 0.0F, .callbackHandle = NULL, .use = NULL, .useWithProperties = NULL, \ - .useWithOwner = NULL \ - } + { .callbackHandle = NULL, .use = NULL, .useWithProperties = NULL, .useWithOwner = NULL } #endif /** - * @brief Use the highest ranking service, tracked by the provided tracker id, using the callbacks in the provided - * options. - * - * If an service is found the use callbacks will be called on the thread that called this function and when function - * returns the callbacks are finished. + * @brief Use the highest-ranking service tracked by the specified tracker id by invoking the callbacks + * specified in the provided options. * - * An tracker id < 0 will be silently ignored. - * An invalid (non existing) tracker id >= 0 will be logged and the function will return false. + * The callbacks are executed on the thread that invokes this function, and the function waits until all callbacks + * are completed before returning. * - * @note the field USE_DIRECT in the provided options has no effect with this function. + * A tracker id less than 0 is ignored without action. An invalid (non-existent) tracker id of 0 or greater results + * in a logged error, and the function returns false. * * @param[in] ctx The bundle context. - * @param[in] trackerId The tracker id. - * @param[in] callbackHandle The data pointer, which will be used in the callbacks. - * @param[in] opts The service use options. - * @return True if a service was found and the use callbacks where called. Returns false if the tracker is not yet - * active (async tracker creation). + * @param[in] trackerId The service tracker id. + * @param[in] opts The service use options containing callbacks and additional configurations. + * @return True if a service was found and the specified callbacks were executed; false if the tracker is not yet + * active (asynchronous tracker creation), the are no matching services tracked or if the tracker id is invalid. */ CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useTrackedServiceWithOptions(celix_bundle_context_t* ctx, @@ -888,23 +890,20 @@ bool celix_bundleContext_useTrackedServiceWithOptions(celix_bundle_context_t* ct const celix_tracked_service_use_options_t* opts); /** - * @brief Use the services, tracked by the provided tracker id, using the callbacks in the provided options. - * - * If 1 or more services is found the use callbacks will be called for every service found on the thread that called - * this function and when function returns the callbacks are finished. + * @brief Use the services tracked by the specified tracker id by invoking the callbacks specified in the + * provided options for each matching service. * - * An tracker id < 0 will be silently ignored. - * An invalid (non existing) tracker id >= 0 will be logged and the function will return 0. + * The callbacks are executed on the thread that invokes this function, and the function waits until all callbacks + * for all found services are completed before returning. * - * @note the field USE_DIRECT in the provided options has no effect with this function. + * A tracker id less than 0 is ignored without action. An invalid (non-existent) tracker id of 0 or greater results + * in a logged error, and the function returns 0. * * @param[in] ctx The bundle context. - * @param[in] trackerId The tracker id. - * @param[in] callbackHandle The data pointer, which will be used in the callbacks. - * @param[in] opts The service use options. - * @return The number of services found and therefore the number of times the callbacks in the provided options where - * called. Returns 0 if the tracker is not yet active - * (async tracker creation). + * @param[in] trackerId The service tracker id. + * @param[in] opts The service use options containing callbacks and additional configurations. + * @return The number of services found and for which the specified callbacks were executed. Returns 0 if the tracker + * is not yet active (asynchronous tracker creation), 0 services are found or if the tracker id is invalid. */ CELIX_FRAMEWORK_EXPORT size_t celix_bundleContext_useTrackedServicesWithOptions(celix_bundle_context_t* ctx, @@ -972,7 +971,7 @@ CELIX_FRAMEWORK_EXPORT void celix_bundleContext_stopTrackerAsync( void (*doneCallback)(void* doneCallbackData)); /** - * @brief Wait, if able, for (async) creation of tracker. + * @brief Wait, if able, for (async) creation of a tracker. * * Will silently ignore trackerId < 0 and log an error if the tracker id is unknown. * If called on the Apache Celix event loop thread, the function will log a warning and return immediately. @@ -998,7 +997,7 @@ CELIX_FRAMEWORK_EXPORT void celix_bundleContext_waitForAsyncStopTracker(celix_bu * * Could be a service tracker, bundle tracker or service tracker tracker. * Only works for the trackers owned by the bundle of the bundle context. - * Note: Please use the celix_bundleContext_registerServiceFactoryAsync instead. + * Note: Please use the celix_bundleContext_stopTrackerAsync instead. * * Will log a error if the provided tracker id is unknown. Will silently ignore trackerId < 0. */ diff --git a/libs/framework/src/bundle.c b/libs/framework/src/bundle.c index 8eaf7112e..97a4f6e29 100644 --- a/libs/framework/src/bundle.c +++ b/libs/framework/src/bundle.c @@ -580,9 +580,18 @@ void celix_bundle_destroyRegisteredServicesList(celix_array_list_t* list) { } } +static void celix_bundle_destroyServiceTrackerListCallback(void *data) { + celix_bundle_service_tracker_list_entry_t *entry = data; + free(entry->filter); + free(entry->serviceName); + free(entry); +} + celix_array_list_t* celix_bundle_listServiceTrackers(const celix_bundle_t *bnd) { - celix_array_list_t* result = celix_arrayList_create(); - celixThreadMutex_lock(&bnd->context->mutex); + celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; + opts.simpleRemovedCallback = celix_bundle_destroyServiceTrackerListCallback; + celix_array_list_t* result = celix_arrayList_createWithOptions(&opts); + celixThreadRwlock_readLock(&bnd->context->lock); CELIX_LONG_HASH_MAP_ITERATE(bnd->context->serviceTrackers, iter) { celix_bundle_context_service_tracker_entry_t *trkEntry = iter.value.ptrValue; if (trkEntry->tracker != NULL) { @@ -597,28 +606,14 @@ celix_array_list_t* celix_bundle_listServiceTrackers(const celix_bundle_t *bnd) } else { framework_logIfError(bnd->framework->logger, CELIX_BUNDLE_EXCEPTION, NULL, "Failed to get service name from tracker. filter is %s", entry->filter); - free(entry->filter); - free(entry); + celix_bundle_destroyServiceTrackerListCallback(entry); } } } - celixThreadMutex_unlock(&bnd->context->mutex); + celixThreadRwlock_unlock(&bnd->context->lock); return result; } - -void celix_bundle_destroyServiceTrackerList(celix_array_list_t* list) { - if (list != NULL) { - for (int i = 0; i < celix_arrayList_size(list); ++i) { - celix_bundle_service_tracker_list_entry_t *entry = celix_arrayList_get(list, i); - free(entry->filter); - free(entry->serviceName); - free(entry); - } - celix_arrayList_destroy(list); - } -} - bundle_archive_t* celix_bundle_getArchive(const celix_bundle_t *bundle) { bundle_archive_t* archive = NULL; bundle_getArchive(bundle, &archive); diff --git a/libs/framework/src/bundle_context.c b/libs/framework/src/bundle_context.c index ec81025a0..def9b586f 100644 --- a/libs/framework/src/bundle_context.c +++ b/libs/framework/src/bundle_context.c @@ -65,7 +65,7 @@ celix_status_t bundleContext_create(framework_pt framework, celix_framework_logg context->bundle = bundle; context->mng = NULL; - celixThreadMutex_create(&context->mutex, NULL); + celixThreadRwlock_create(&context->lock, NULL); context->svcRegistrations = celix_arrayList_create(); context->bundleTrackers = celix_longHashMap_create(); @@ -98,7 +98,7 @@ celix_status_t bundleContext_destroy(bundle_context_pt context) { celix_arrayList_destroy(context->svcRegistrations); celix_longHashMap_destroy(context->stoppingTrackerEventIds); - celixThreadMutex_destroy(&context->mutex); + celixThreadRwlock_destroy(&context->lock); if (context->mng != NULL) { celix_dependencyManager_removeAllComponents(context->mng); @@ -434,9 +434,9 @@ static long celix_bundleContext_registerServiceWithOptionsInternal(bundle_contex } if (svcId >= 0) { - celixThreadMutex_lock(&ctx->mutex); + celixThreadRwlock_writeLock(&ctx->lock); celix_arrayList_addLong(ctx->svcRegistrations, svcId); - celixThreadMutex_unlock(&ctx->mutex); + celixThreadRwlock_unlock(&ctx->lock); } return svcId; } @@ -462,7 +462,7 @@ bool celix_bundleContext_isServiceRegistered(celix_bundle_context_t* ctx, long s static void celix_bundleContext_unregisterServiceInternal(celix_bundle_context_t *ctx, long serviceId, bool async, void *data, void (*done)(void*)) { long found = -1L; if (ctx != NULL && serviceId >= 0) { - celixThreadMutex_lock(&ctx->mutex); + celixThreadRwlock_writeLock(&ctx->lock); int size = celix_arrayList_size(ctx->svcRegistrations); for (int i = 0; i < size; ++i) { long entryId = celix_arrayList_getLong(ctx->svcRegistrations, i); @@ -472,7 +472,7 @@ static void celix_bundleContext_unregisterServiceInternal(celix_bundle_context_t break; } } - celixThreadMutex_unlock(&ctx->mutex); + celixThreadRwlock_unlock(&ctx->lock); if (found >= 0) { if (async) { @@ -519,7 +519,7 @@ void celix_bundleContext_waitForAsyncUnregistration(celix_bundle_context_t* ctx, celix_dependency_manager_t* celix_bundleContext_getDependencyManager(bundle_context_t *ctx) { celix_dependency_manager_t* result = NULL; if (ctx != NULL) { - celixThreadMutex_lock(&ctx->mutex); + celixThreadRwlock_readLock(&ctx->lock); if (ctx->mng == NULL) { ctx->mng = celix_private_dependencyManager_create(ctx); } @@ -527,7 +527,7 @@ celix_dependency_manager_t* celix_bundleContext_getDependencyManager(bundle_cont framework_logIfError(ctx->framework->logger, CELIX_BUNDLE_EXCEPTION, NULL, "Cannot create dependency manager"); } result = ctx->mng; - celixThreadMutex_unlock(&ctx->mutex); + celixThreadRwlock_unlock(&ctx->lock); } return result; } @@ -563,10 +563,10 @@ static celix_status_t bundleContext_bundleChanged(void* listenerSvc, bundle_even void celix_bundleContext_trackBundlesWithOptionsCallback(void *data) { celix_bundle_context_bundle_tracker_entry_t* entry = data; assert(celix_framework_isCurrentThreadTheEventLoop(entry->ctx->framework)); - celixThreadMutex_lock(&entry->ctx->mutex); + celixThreadRwlock_writeLock(&entry->ctx->lock); bool cancelled = entry->cancelled; entry->created = true; - celixThreadMutex_unlock(&entry->ctx->mutex); + celixThreadRwlock_unlock(&entry->ctx->lock); if (cancelled) { fw_log(entry->ctx->framework->logger, CELIX_LOG_LEVEL_DEBUG, "Creation of bundle tracker cancelled. trk id = %li", entry->trackerId); free(entry); @@ -590,11 +590,11 @@ static long celix_bundleContext_trackBundlesWithOptionsInternal( entry->listener.handle = entry; entry->listener.bundleChanged = bundleContext_bundleChanged; - celixThreadMutex_lock(&ctx->mutex); + celixThreadRwlock_writeLock(&ctx->lock); entry->trackerId = ctx->nextTrackerId++; celix_longHashMap_put(ctx->bundleTrackers, entry->trackerId, entry); long trackerId = entry->trackerId; - celixThreadMutex_unlock(&ctx->mutex); + celixThreadRwlock_unlock(&ctx->lock); if (!async) { //note only using the async callback if this is a async call. entry->opts.trackerCreatedCallback = NULL; @@ -677,7 +677,7 @@ static void bundleContext_cleanupBundleTrackers(bundle_context_t* ctx) { celix_array_list_t* danglingTrkIds = NULL; - celixThreadMutex_lock(&ctx->mutex); + celixThreadRwlock_writeLock(&ctx->lock); CELIX_LONG_HASH_MAP_ITERATE(ctx->bundleTrackers, iter) { long trkId = iter.key; fw_log( @@ -691,7 +691,8 @@ static void bundleContext_cleanupBundleTrackers(bundle_context_t* ctx) { } celix_arrayList_addLong(danglingTrkIds, trkId); } - celixThreadMutex_unlock(&ctx->mutex); + celix_longHashMap_clear(ctx->bundleTrackers); + celixThreadRwlock_unlock(&ctx->lock); if (danglingTrkIds != NULL) { for (int i = 0; i < celix_arrayList_size(danglingTrkIds); ++i) { @@ -710,7 +711,7 @@ static void bundleContext_cleanupServiceTrackers(bundle_context_t* ctx) { celix_array_list_t* danglingTrkIds = NULL; - celixThreadMutex_lock(&ctx->mutex); + celixThreadRwlock_writeLock(&ctx->lock); CELIX_LONG_HASH_MAP_ITERATE(ctx->serviceTrackers, iter) { long trkId = iter.key; celix_bundle_context_service_tracker_entry_t* entry = celix_longHashMap_get(ctx->serviceTrackers, trkId); @@ -726,7 +727,8 @@ static void bundleContext_cleanupServiceTrackers(bundle_context_t* ctx) { } celix_arrayList_addLong(danglingTrkIds, trkId); } - celixThreadMutex_unlock(&ctx->mutex); + celix_longHashMap_clear(ctx->serviceTrackers); + celixThreadRwlock_unlock(&ctx->lock); if (danglingTrkIds != NULL) { for (int i = 0; i < celix_arrayList_size(danglingTrkIds); ++i) { @@ -745,7 +747,7 @@ static void bundleContext_cleanupServiceTrackerTrackers(bundle_context_t* ctx) { celix_array_list_t* danglingTrkIds = NULL; - celixThreadMutex_lock(&ctx->mutex); + celixThreadRwlock_writeLock(&ctx->lock); CELIX_LONG_HASH_MAP_ITERATE(ctx->metaTrackers, iter) { long trkId = iter.key; celix_bundle_context_service_tracker_tracker_entry_t* entry = celix_longHashMap_get(ctx->metaTrackers, trkId); @@ -761,7 +763,8 @@ static void bundleContext_cleanupServiceTrackerTrackers(bundle_context_t* ctx) { } celix_arrayList_addLong(danglingTrkIds, trkId); } - celixThreadMutex_unlock(&ctx->mutex); + celix_longHashMap_clear(ctx->metaTrackers); + celixThreadRwlock_unlock(&ctx->lock); if (danglingTrkIds != NULL) { for (int i = 0; i < celix_arrayList_size(danglingTrkIds); ++i) { @@ -780,7 +783,7 @@ static void bundleContext_cleanupServiceRegistration(bundle_context_t* ctx) { celix_array_list_t* danglingSvcIds = NULL; - celixThreadMutex_lock(&ctx->mutex); + celixThreadRwlock_writeLock(&ctx->lock); for (int i = 0; i < celix_arrayList_size(ctx->svcRegistrations); ++i) { long svcId = celix_arrayList_getLong(ctx->svcRegistrations, i); fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Dangling service registration with svcId %li, for bundle %s. Add missing 'celix_bundleContext_unregisterService' calls.", svcId, symbolicName); @@ -789,7 +792,8 @@ static void bundleContext_cleanupServiceRegistration(bundle_context_t* ctx) { } celix_arrayList_addLong(danglingSvcIds, svcId); } - celixThreadMutex_unlock(&ctx->mutex); + celix_arrayList_clear(ctx->svcRegistrations); + celixThreadRwlock_unlock(&ctx->lock); if (danglingSvcIds != NULL) { for (int i = 0; i < celix_arrayList_size(danglingSvcIds); ++i) { @@ -803,18 +807,18 @@ static void bundleContext_cleanupServiceRegistration(bundle_context_t* ctx) { static void celix_bundleContext_removeBundleTracker(void *data) { celix_bundle_context_bundle_tracker_entry_t *tracker = data; fw_removeBundleListener(tracker->ctx->framework, tracker->ctx->bundle, &tracker->listener); - celixThreadMutex_lock(&tracker->ctx->mutex); + celixThreadRwlock_writeLock(&tracker->ctx->lock); celix_longHashMap_remove(tracker->ctx->stoppingTrackerEventIds, tracker->trackerId); - celixThreadMutex_unlock(&tracker->ctx->mutex); + celixThreadRwlock_unlock(&tracker->ctx->lock); free(tracker); } static void celix_bundleContext_removeServiceTracker(void *data) { celix_bundle_context_service_tracker_entry_t *tracker = data; celix_serviceTracker_destroy(tracker->tracker); - celixThreadMutex_lock(&tracker->ctx->mutex); + celixThreadRwlock_writeLock(&tracker->ctx->lock); celix_longHashMap_remove(tracker->ctx->stoppingTrackerEventIds, tracker->trackerId); - celixThreadMutex_unlock(&tracker->ctx->mutex); + celixThreadRwlock_unlock(&tracker->ctx->lock); if (tracker->isFreeFilterNeeded) { free((char*)tracker->opts.filter.serviceName); free((char*)tracker->opts.filter.versionRange); @@ -826,9 +830,9 @@ static void celix_bundleContext_removeServiceTracker(void *data) { static void celix_bundleContext_removeServiceTrackerTracker(void *data) { celix_bundle_context_service_tracker_tracker_entry_t *tracker = data; celix_framework_unregister(tracker->ctx->framework, tracker->ctx->bundle, tracker->serviceId); - celixThreadMutex_lock(&tracker->ctx->mutex); + celixThreadRwlock_writeLock(&tracker->ctx->lock); celix_longHashMap_remove(tracker->ctx->stoppingTrackerEventIds, tracker->trackerId); - celixThreadMutex_unlock(&tracker->ctx->mutex); + celixThreadRwlock_unlock(&tracker->ctx->lock); free(tracker->serviceName); free(tracker); } @@ -844,7 +848,7 @@ static void celix_bundleContext_stopTrackerInternal(bundle_context_t *ctx, long celix_bundle_context_service_tracker_entry_t *serviceTracker = NULL; celix_bundle_context_service_tracker_tracker_entry_t *svcTrackerTracker = NULL; - celixThreadMutex_lock(&ctx->mutex); + celixThreadRwlock_writeLock(&ctx->lock); if (celix_longHashMap_hasKey(ctx->bundleTrackers, trackerId)) { found = true; @@ -873,10 +877,10 @@ static void celix_bundleContext_stopTrackerInternal(bundle_context_t *ctx, long if (found && cancelled) { //nop - celixThreadMutex_unlock(&ctx->mutex); + celixThreadRwlock_unlock(&ctx->lock); } else if (found && !async && celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) { //already on the event loop, stop tracker "traditionally" to keep old behavior - celixThreadMutex_unlock(&ctx->mutex); //note calling remove/stops/unregister out side of locks + celixThreadRwlock_unlock(&ctx->lock); //note calling remove/stops/unregister out side of locks if (bundleTracker != NULL) { fw_removeBundleListener(ctx->framework, ctx->bundle, &bundleTracker->listener); @@ -912,7 +916,7 @@ static void celix_bundleContext_stopTrackerInternal(bundle_context_t *ctx, long } else { fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Unexpected else branch"); } - celixThreadMutex_unlock(&ctx->mutex); + celixThreadRwlock_unlock(&ctx->lock); } else if (found) { /*sync, so waiting for events*/ long eventId = -1L; if (bundleTracker != NULL) { @@ -924,10 +928,10 @@ static void celix_bundleContext_stopTrackerInternal(bundle_context_t *ctx, long } else { fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Unexpected else branch"); } - celixThreadMutex_unlock(&ctx->mutex); + celixThreadRwlock_unlock(&ctx->lock); celix_framework_waitForGenericEvent(ctx->framework, eventId); } else { - celixThreadMutex_unlock(&ctx->mutex); + celixThreadRwlock_unlock(&ctx->lock); fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "No tracker with id %li found'", trackerId); } } @@ -958,7 +962,7 @@ static void celix_bundleContext_waitForTrackerInternal(celix_bundle_context_t* c long svcId = -1; if (waitForStart) { - celixThreadMutex_lock(&ctx->mutex); + celixThreadRwlock_readLock(&ctx->lock); if (celix_longHashMap_hasKey(ctx->bundleTrackers, trackerId)) { found = true; celix_bundle_context_bundle_tracker_entry_t* bundleTracker = celix_longHashMap_get(ctx->bundleTrackers, trackerId); @@ -972,14 +976,14 @@ static void celix_bundleContext_waitForTrackerInternal(celix_bundle_context_t* c celix_bundle_context_service_tracker_tracker_entry_t* svcTrackerTracker = celix_longHashMap_get(ctx->metaTrackers, trackerId); svcId = svcTrackerTracker->serviceId; } - celixThreadMutex_unlock(&ctx->mutex); + celixThreadRwlock_unlock(&ctx->lock); } else { - celixThreadMutex_lock(&ctx->mutex); + celixThreadRwlock_readLock(&ctx->lock); if (celix_longHashMap_hasKey(ctx->stoppingTrackerEventIds, trackerId)) { found = true; eventId = celix_longHashMap_getLong(ctx->stoppingTrackerEventIds, trackerId, -1); } - celixThreadMutex_unlock(&ctx->mutex); + celixThreadRwlock_unlock(&ctx->lock); } if (found) { @@ -1241,11 +1245,11 @@ long celix_bundleContext_trackServicesAsync(celix_bundle_context_t* ctx, const c static void celix_bundleContext_createTrackerOnEventLoop(void *data) { celix_bundle_context_service_tracker_entry_t* entry = data; assert(celix_framework_isCurrentThreadTheEventLoop(entry->ctx->framework)); - celixThreadMutex_lock(&entry->ctx->mutex); + celixThreadRwlock_writeLock(&entry->ctx->lock); bool cancelled = entry->cancelled; if (cancelled) { fw_log(entry->ctx->framework->logger, CELIX_LOG_LEVEL_DEBUG, "Creating of service tracker was cancelled. trk id = %li, svc name tracked = %s", entry->trackerId, entry->opts.filter.serviceName); - celixThreadMutex_unlock(&entry->ctx->mutex); + celixThreadRwlock_unlock(&entry->ctx->lock); return; } celix_service_tracker_t *tracker = celix_serviceTracker_createClosedWithOptions(entry->ctx, &entry->opts); @@ -1254,7 +1258,7 @@ static void celix_bundleContext_createTrackerOnEventLoop(void *data) { } else { entry->tracker = tracker; } - celixThreadMutex_unlock(&entry->ctx->mutex); + celixThreadRwlock_unlock(&entry->ctx->lock); if (tracker) { serviceTracker_open(tracker); } @@ -1262,9 +1266,9 @@ static void celix_bundleContext_createTrackerOnEventLoop(void *data) { static void celix_bundleContext_doneCreatingTrackerOnEventLoop(void *data) { celix_bundle_context_service_tracker_entry_t* entry = data; - celixThreadMutex_lock(&entry->ctx->mutex); + celixThreadRwlock_readLock(&entry->ctx->lock); bool cancelled = entry->cancelled; - celixThreadMutex_unlock(&entry->ctx->mutex); + celixThreadRwlock_unlock(&entry->ctx->lock); if (cancelled) { //tracker creation cancelled -> entry already removed from map, but memory needs to be freed. if (entry->isFreeFilterNeeded) { @@ -1303,11 +1307,11 @@ static long celix_bundleContext_trackServicesWithOptionsInternal(celix_bundle_co entry->opts = *opts; entry->isFreeFilterNeeded = false; entry->createEventId = -1; - celixThreadMutex_lock(&ctx->mutex); + celixThreadRwlock_writeLock(&ctx->lock); entry->trackerId = ctx->nextTrackerId++; trackerId = entry->trackerId; celix_longHashMap_put(ctx->serviceTrackers, trackerId, entry); - celixThreadMutex_unlock(&ctx->mutex); + celixThreadRwlock_unlock(&ctx->lock); } return trackerId; } else { @@ -1329,11 +1333,11 @@ static long celix_bundleContext_trackServicesWithOptionsInternal(celix_bundle_co entry->opts.filter.filter = celix_utils_strdup(opts->filter.filter); } - celixThreadMutex_lock(&ctx->mutex); + celixThreadRwlock_writeLock(&ctx->lock); entry->trackerId = ctx->nextTrackerId++; long trackerId = entry->trackerId; celix_longHashMap_put(ctx->serviceTrackers, entry->trackerId, entry); - celixThreadMutex_unlock(&ctx->mutex); + celixThreadRwlock_unlock(&ctx->lock); long id = celix_framework_fireGenericEvent(ctx->framework, createEventId, @@ -1416,7 +1420,7 @@ static size_t celix_bundleContext_useTrackedServiceWithOptionsInternal(celix_bun long trackerId, const celix_tracked_service_use_options_t* opts, bool singleUse) { - celix_auto(celix_mutex_lock_guard_t) lck = celixMutexLockGuard_init(&ctx->mutex); + celix_auto(celix_rwlock_rlock_guard_t) lck = celixRwlockRlockGuard_init(&ctx->lock); celix_service_tracker_t* trk = celix_bundleContext_findServiceTracker(ctx, trackerId); if (!trk) { return 0; @@ -1425,7 +1429,7 @@ static size_t celix_bundleContext_useTrackedServiceWithOptionsInternal(celix_bun if (singleUse) { bool called = celix_serviceTracker_useHighestRankingService(trk, NULL, - opts->waitTimeoutInSeconds, + 0, opts->callbackHandle, opts->use, opts->useWithProperties, @@ -1460,7 +1464,7 @@ void celix_bundleContext_getTrackerInfo(celix_bundle_context_t *ctx, long tracke *trackedServiceFilter = NULL; } - celix_auto(celix_mutex_lock_guard_t) lck = celixMutexLockGuard_init(&ctx->mutex); + celix_auto(celix_rwlock_rlock_guard_t) lck = celixRwlockRlockGuard_init(&ctx->lock); celix_service_tracker_t* trk = celix_bundleContext_findServiceTracker(ctx, trackerId); if (!trk) { return; @@ -1496,7 +1500,7 @@ const char* celix_bundleContext_getTrackedServiceFilter(celix_bundle_context_t* } bool celix_bundleContext_isValidTrackerId(celix_bundle_context_t* ctx, long trackerId) { - celix_auto(celix_mutex_lock_guard_t) lck = celixMutexLockGuard_init(&ctx->mutex); + celix_auto(celix_rwlock_rlock_guard_t) lck = celixRwlockRlockGuard_init(&ctx->lock); return celix_longHashMap_hasKey(ctx->serviceTrackers, trackerId); } @@ -1590,9 +1594,9 @@ long celix_bundleContext_trackServiceTrackersInternal( } celix_bundle_context_service_tracker_tracker_entry_t *entry = calloc(1, sizeof(*entry)); - celixThreadMutex_lock(&ctx->mutex); + celixThreadRwlock_writeLock(&ctx->lock); entry->trackerId = ctx->nextTrackerId++; - celixThreadMutex_unlock(&ctx->mutex); + celixThreadRwlock_unlock(&ctx->lock); entry->ctx = ctx; entry->callbackHandle = callbackHandle; entry->add = trackerAdd; @@ -1615,10 +1619,10 @@ long celix_bundleContext_trackServiceTrackersInternal( } if (entry->serviceId >= 0) { - celixThreadMutex_lock(&ctx->mutex); + celixThreadRwlock_writeLock(&ctx->lock); celix_longHashMap_put(ctx->metaTrackers, entry->trackerId, entry); long trkId = entry->trackerId; - celixThreadMutex_unlock(&ctx->mutex); + celixThreadRwlock_unlock(&ctx->lock); return trkId; } else { celix_framework_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, __FUNCTION__, __BASE_FILE__, __LINE__, "Error registering service listener hook for service tracker tracker\n"); diff --git a/libs/framework/src/bundle_context_private.h b/libs/framework/src/bundle_context_private.h index 1eacc48ba..5fe106016 100644 --- a/libs/framework/src/bundle_context_private.h +++ b/libs/framework/src/bundle_context_private.h @@ -81,8 +81,8 @@ struct celix_bundle_context { celix_framework_t* framework; celix_bundle_t* bundle; - celix_thread_mutex_t - mutex; // protects fields below (NOTE/FIXME also used by bundle.c for listing service tracker usage) + celix_thread_rwlock_t + lock; // protects fields below celix_array_list_t* svcRegistrations; // serviceIds celix_dependency_manager_t* mng; long nextTrackerId; From 8f2f552671997f896b9da415081b8256754baeb7 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 25 Feb 2024 17:25:35 +0100 Subject: [PATCH 07/14] gh-87: Revert clear tracker maps in bnd ctx cleanup. --- libs/framework/src/bundle_context.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/libs/framework/src/bundle_context.c b/libs/framework/src/bundle_context.c index def9b586f..f53a51a22 100644 --- a/libs/framework/src/bundle_context.c +++ b/libs/framework/src/bundle_context.c @@ -691,7 +691,6 @@ static void bundleContext_cleanupBundleTrackers(bundle_context_t* ctx) { } celix_arrayList_addLong(danglingTrkIds, trkId); } - celix_longHashMap_clear(ctx->bundleTrackers); celixThreadRwlock_unlock(&ctx->lock); if (danglingTrkIds != NULL) { @@ -727,7 +726,6 @@ static void bundleContext_cleanupServiceTrackers(bundle_context_t* ctx) { } celix_arrayList_addLong(danglingTrkIds, trkId); } - celix_longHashMap_clear(ctx->serviceTrackers); celixThreadRwlock_unlock(&ctx->lock); if (danglingTrkIds != NULL) { @@ -763,7 +761,6 @@ static void bundleContext_cleanupServiceTrackerTrackers(bundle_context_t* ctx) { } celix_arrayList_addLong(danglingTrkIds, trkId); } - celix_longHashMap_clear(ctx->metaTrackers); celixThreadRwlock_unlock(&ctx->lock); if (danglingTrkIds != NULL) { From 102cd33327cffd356740008b84e26b5f6159bd08 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 25 Feb 2024 19:45:48 +0100 Subject: [PATCH 08/14] gh-87: Replace trackService usage in rsa test with trackServiceWithOptions --- .../gtest/src/rsa_client_server_tests.cc | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/bundles/remote_services/remote_service_admin_dfi/gtest/src/rsa_client_server_tests.cc b/bundles/remote_services/remote_service_admin_dfi/gtest/src/rsa_client_server_tests.cc index 7fb210585..7e903e1e9 100644 --- a/bundles/remote_services/remote_service_admin_dfi/gtest/src/rsa_client_server_tests.cc +++ b/bundles/remote_services/remote_service_admin_dfi/gtest/src/rsa_client_server_tests.cc @@ -446,18 +446,25 @@ class RsaDfiDynamicIpServerTestSuite : public ::testing::Test { void TestRemoteCalculator(void (*testBody)(tst_service_t* testSvc), const char* serverIp = "127.0.0.1") { remote_service_admin_service_t* serverRsaSvc{nullptr}; remote_service_admin_service_t* clientRsaSvc{nullptr}; - serverRsaTrkId = celix_bundleContext_trackService(serverCtx.get(), CELIX_RSA_REMOTE_SERVICE_ADMIN, - &serverRsaSvc, [](void* handle, void* svc){ - auto rsaSvc = static_cast(handle); - *rsaSvc = static_cast(svc); - }); + + celix_service_tracking_options_t trackintOpts{}; + trackintOpts.filter.serviceName = CELIX_RSA_REMOTE_SERVICE_ADMIN; + trackintOpts.callbackHandle = &serverRsaSvc; + trackintOpts.set = [](void* handle, void* svc) { + auto rsaSvc = static_cast(handle); + *rsaSvc = static_cast(svc); + }; + serverRsaTrkId = celix_bundleContext_trackServicesWithOptions(serverCtx.get(), &trackintOpts); EXPECT_GE(serverRsaTrkId, 0); - clientRsaTrkId = celix_bundleContext_trackService(clientCtx.get(), CELIX_RSA_REMOTE_SERVICE_ADMIN, - &clientRsaSvc, [](void* handle, void* svc){ - auto rsaSvc = static_cast(handle); - *rsaSvc = static_cast(svc); - }); + + trackintOpts.callbackHandle = &clientRsaSvc; + trackintOpts.set = [](void* handle, void* svc){ + auto rsaSvc = static_cast(handle); + *rsaSvc = static_cast(svc); + }; + clientRsaTrkId = celix_bundleContext_trackServicesWithOptions(clientCtx.get(), &trackintOpts); EXPECT_GE(clientRsaTrkId, 0); + long calcId = celix_bundleContext_findService(serverCtx.get(), CALCULATOR_SERVICE); ASSERT_TRUE(calcId >= 0L); ASSERT_TRUE(clientRsaSvc != nullptr); From 6af4f405b5b7c8861e2289374b970984d9eea226 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Mon, 26 Feb 2024 20:56:58 +0800 Subject: [PATCH 09/14] Some minor documentation corrections. --- libs/framework/include/celix_bundle_context.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/framework/include/celix_bundle_context.h b/libs/framework/include/celix_bundle_context.h index 93250ac9f..fb9dc94b7 100644 --- a/libs/framework/include/celix_bundle_context.h +++ b/libs/framework/include/celix_bundle_context.h @@ -391,7 +391,7 @@ CELIX_FRAMEWORK_EXPORT celix_array_list_t* celix_bundleContext_findServicesWithO * @brief Use the service with the provided service id using the provided callback. The Celix framework will ensure that * the targeted service cannot be removed during the callback. * - * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useServiceTracker* functions + * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useTrackedService* functions * instead. * * The svc is should only be considered valid during the callback. @@ -417,7 +417,7 @@ CELIX_FRAMEWORK_DEPRECATED_EXPORT bool celix_bundleContext_useServiceWithId(celi /** * @brief Use the highest ranking service with the provided service name using the provided callback. * - * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useServiceTracker* functions + * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useTrackedService* functions * instead. * * The Celix framework will ensure that the targeted service cannot be removed during the callback. @@ -444,7 +444,7 @@ CELIX_FRAMEWORK_DEPRECATED_EXPORT bool celix_bundleContext_useService( /** * @brief Use the services with the provided service name using the provided callback. * - * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useServiceTracker* functions + * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useTrackedService* functions * instead. * * The Celix framework will ensure that the targeted service cannot be removed during the callback. @@ -548,7 +548,7 @@ typedef struct celix_service_use_options { /** * @brief Use the highest ranking service satisfying the provided service filter options using the provided callback. * - * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useServiceTracker* functions + * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useTrackedService* functions * instead. * * The Celix framework will ensure that the targeted service cannot be removed during the callback. @@ -572,7 +572,7 @@ CELIX_FRAMEWORK_DEPRECATED_EXPORT bool celix_bundleContext_useServiceWithOptions /** * @brief Use the services with the provided service filter options using the provided callback. * - * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useServiceTracker* functions + * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useTrackedService* functions * instead. * * The Celix framework will ensure that the targeted service cannot be removed during the callback. From 67b1240f9825ba71e18dae06152ea213f7b64ab8 Mon Sep 17 00:00:00 2001 From: PengZheng Date: Mon, 26 Feb 2024 21:10:04 +0800 Subject: [PATCH 10/14] gh-87: Remove unused APIs. --- libs/framework/src/bundle_context.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/libs/framework/src/bundle_context.c b/libs/framework/src/bundle_context.c index f53a51a22..252a014c5 100644 --- a/libs/framework/src/bundle_context.c +++ b/libs/framework/src/bundle_context.c @@ -1215,18 +1215,6 @@ size_t celix_bundleContext_useServicesWithOptions( return data.count; } -long celix_bundleContext_trackService(bundle_context_t* ctx, const char* serviceName) { - celix_service_tracking_options_t opts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS; - opts.filter.serviceName = serviceName; - return celix_bundleContext_trackServicesWithOptionsInternal(ctx, &opts, false); -} - -long celix_bundleContext_trackServiceAsync(bundle_context_t* ctx, const char* serviceName) { - celix_service_tracking_options_t opts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS; - opts.filter.serviceName = serviceName; - return celix_bundleContext_trackServicesWithOptionsInternal(ctx, &opts, true); -} - long celix_bundleContext_trackServices(bundle_context_t* ctx, const char* serviceName) { celix_service_tracking_options_t opts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS; opts.filter.serviceName = serviceName; From c44da3980c3d0d4a181fbeebb08a85712e82ba4f Mon Sep 17 00:00:00 2001 From: PengZheng Date: Mon, 26 Feb 2024 21:34:45 +0800 Subject: [PATCH 11/14] gh-87: Fix crash caused by wait on event loop. --- .../CelixBundleContextServicesTestSuite.cc | 34 +++++++++++++++++++ libs/framework/src/bundle_context.c | 1 + 2 files changed, 35 insertions(+) diff --git a/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc b/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc index bd2894d64..6f990cc3e 100644 --- a/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc +++ b/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc @@ -1664,6 +1664,40 @@ TEST_F(CelixBundleContextServicesTestSuite, StopSvcTrackerBeforeAsyncTrackerIsCr EXPECT_EQ(0, cbData.count.load()); //note create tracker canceled -> no callback } +TEST_F(CelixBundleContextServicesTestSuite, WaitForTrackerOnLoop) { + struct callback_data { + std::atomic count{}; + celix_bundle_context_t* ctx{nullptr}; + }; + callback_data cbData{}; + cbData.ctx = ctx; + + celix_framework_fireGenericEvent( + fw, + -1, + celix_bundle_getId(celix_framework_getFrameworkBundle(fw)), + "create tracker async", + (void*)&cbData, + [](void *data) { + auto cbd = static_cast(data); + + celix_service_tracking_options_t opts{}; + opts.filter.serviceName = "test-service"; + opts.trackerCreatedCallbackData = data; + opts.trackerCreatedCallback = [](void *data) { + auto* cbd = static_cast(data); + cbd->count.fetch_add(1); + }; + long trkId = celix_bundleContext_trackServicesWithOptions(cbd->ctx, &opts); + celix_bundleContext_waitForAsyncTracker(cbd->ctx, trkId); + celix_bundleContext_stopTracker(cbd->ctx, trkId); + }, + nullptr, + nullptr); + + celix_bundleContext_waitForEvents(ctx); +} + TEST_F(CelixBundleContextServicesTestSuite, StopBundleTrackerBeforeAsyncTrackerIsCreatedTest) { struct callback_data { std::atomic count{}; diff --git a/libs/framework/src/bundle_context.c b/libs/framework/src/bundle_context.c index 252a014c5..612e940e8 100644 --- a/libs/framework/src/bundle_context.c +++ b/libs/framework/src/bundle_context.c @@ -952,6 +952,7 @@ static void celix_bundleContext_waitForTrackerInternal(celix_bundle_context_t* c CELIX_LOG_LEVEL_WARNING, "Cannot wait for tracker on the event loop thread. This can cause a deadlock. " "Ignoring call."); + return; } bool found = false; From 13fb823650a153192a87ce12a01a172e2d431e0d Mon Sep 17 00:00:00 2001 From: PengZheng Date: Mon, 26 Feb 2024 21:38:18 +0800 Subject: [PATCH 12/14] gh-87: Add test case to wait for non-existing tracker. --- .../gtest/src/CelixBundleContextServicesTestSuite.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc b/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc index 6f990cc3e..8218ecf05 100644 --- a/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc +++ b/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc @@ -1698,6 +1698,10 @@ TEST_F(CelixBundleContextServicesTestSuite, WaitForTrackerOnLoop) { celix_bundleContext_waitForEvents(ctx); } +TEST_F(CelixBundleContextServicesTestSuite, WaitForNonexistingTracker) { + celix_bundleContext_waitForAsyncTracker(ctx, 111L /* non-existing tracker */); +} + TEST_F(CelixBundleContextServicesTestSuite, StopBundleTrackerBeforeAsyncTrackerIsCreatedTest) { struct callback_data { std::atomic count{}; From b2d197ebcdf332088bffeee3f82aa2895985bade Mon Sep 17 00:00:00 2001 From: PengZheng Date: Tue, 27 Feb 2024 12:17:05 +0800 Subject: [PATCH 13/14] gh-87: Support lock stealing from a lock guard. Fix race condition in `celix_bundleContext_getDependencyManager` and avoid write lock when possible. --- libs/framework/src/bundle_context.c | 28 ++++++++++++++++------------ libs/utils/include/celix_threads.h | 12 +++++++++--- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/libs/framework/src/bundle_context.c b/libs/framework/src/bundle_context.c index 612e940e8..db20c1a4d 100644 --- a/libs/framework/src/bundle_context.c +++ b/libs/framework/src/bundle_context.c @@ -517,19 +517,23 @@ void celix_bundleContext_waitForAsyncUnregistration(celix_bundle_context_t* ctx, } celix_dependency_manager_t* celix_bundleContext_getDependencyManager(bundle_context_t *ctx) { - celix_dependency_manager_t* result = NULL; - if (ctx != NULL) { - celixThreadRwlock_readLock(&ctx->lock); - if (ctx->mng == NULL) { - ctx->mng = celix_private_dependencyManager_create(ctx); - } - if (ctx->mng == NULL) { - framework_logIfError(ctx->framework->logger, CELIX_BUNDLE_EXCEPTION, NULL, "Cannot create dependency manager"); - } - result = ctx->mng; - celixThreadRwlock_unlock(&ctx->lock); + if (ctx == NULL) { + return NULL; } - return result; + celix_auto(celix_rwlock_rlock_guard_t) rlockGuard = celixRwlockRlockGuard_init(&ctx->lock); + if (ctx->mng) { + return ctx->mng; + } + celixThreadRwlock_unlock(celix_steal_ptr(rlockGuard.lock)); + + celix_auto(celix_rwlock_wlock_guard_t) wlockGuard = celixRwlockWlockGuard_init(&ctx->lock); + if (ctx->mng == NULL) { + ctx->mng = celix_private_dependencyManager_create(ctx); + } + if (ctx->mng == NULL) { + framework_logIfError(ctx->framework->logger, CELIX_BUNDLE_EXCEPTION, NULL, "Cannot create dependency manager"); + } + return ctx->mng; } static celix_status_t bundleContext_bundleChanged(void* listenerSvc, bundle_event_t* event) { diff --git a/libs/utils/include/celix_threads.h b/libs/utils/include/celix_threads.h index 064601d78..19c585f3b 100644 --- a/libs/utils/include/celix_threads.h +++ b/libs/utils/include/celix_threads.h @@ -139,7 +139,9 @@ static CELIX_UNUSED inline celix_mutex_lock_guard_t celixMutexLockGuard_init(cel * @param guard A celix_mutex_lock_guard_t. */ static CELIX_UNUSED inline void celixMutexLockGuard_deinit(celix_mutex_lock_guard_t* guard) { - celixThreadMutex_unlock(guard->mutex); + if (guard->mutex) { + celixThreadMutex_unlock(guard->mutex); + } } CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_mutex_lock_guard_t, celixMutexLockGuard_deinit) @@ -211,7 +213,9 @@ static CELIX_UNUSED inline celix_rwlock_wlock_guard_t celixRwlockWlockGuard_init * @param guard A celix_rwlock_wlock_guard_t. */ static CELIX_UNUSED inline void celixRwlockWlockGuard_deinit(celix_rwlock_wlock_guard_t* guard) { - celixThreadRwlock_unlock(guard->lock); + if (guard->lock) { + celixThreadRwlock_unlock(guard->lock); + } } CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_rwlock_wlock_guard_t, celixRwlockWlockGuard_deinit) @@ -256,7 +260,9 @@ static CELIX_UNUSED inline celix_rwlock_rlock_guard_t celixRwlockRlockGuard_init * @param guard A celix_rwlock_rlock_guard_t. */ static CELIX_UNUSED inline void celixRwlockRlockGuard_deinit(celix_rwlock_rlock_guard_t* guard) { - celixThreadRwlock_unlock(guard->lock); + if (guard->lock) { + celixThreadRwlock_unlock(guard->lock); + } } CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_rwlock_rlock_guard_t, celixRwlockRlockGuard_deinit) From 8ca876da16b672bd7d88cc823672c3ad1d05b65e Mon Sep 17 00:00:00 2001 From: PengZheng Date: Tue, 27 Feb 2024 12:58:01 +0800 Subject: [PATCH 14/14] gh-87: Avoid write lock in bundleContext_cleanupXXX when possible and fix possible nullptr dereference. --- libs/framework/src/bundle_context.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/libs/framework/src/bundle_context.c b/libs/framework/src/bundle_context.c index db20c1a4d..b8600ef96 100644 --- a/libs/framework/src/bundle_context.c +++ b/libs/framework/src/bundle_context.c @@ -681,7 +681,7 @@ static void bundleContext_cleanupBundleTrackers(bundle_context_t* ctx) { celix_array_list_t* danglingTrkIds = NULL; - celixThreadRwlock_writeLock(&ctx->lock); + celixThreadRwlock_readLock(&ctx->lock); CELIX_LONG_HASH_MAP_ITERATE(ctx->bundleTrackers, iter) { long trkId = iter.key; fw_log( @@ -714,7 +714,7 @@ static void bundleContext_cleanupServiceTrackers(bundle_context_t* ctx) { celix_array_list_t* danglingTrkIds = NULL; - celixThreadRwlock_writeLock(&ctx->lock); + celixThreadRwlock_readLock(&ctx->lock); CELIX_LONG_HASH_MAP_ITERATE(ctx->serviceTrackers, iter) { long trkId = iter.key; celix_bundle_context_service_tracker_entry_t* entry = celix_longHashMap_get(ctx->serviceTrackers, trkId); @@ -724,7 +724,7 @@ static void bundleContext_cleanupServiceTrackers(bundle_context_t* ctx) { "'celix_bundleContext_stopTracker' calls.", trkId, symbolicName, - entry->tracker->filter); + entry->tracker ? entry->tracker->filter : "unknown"); if (danglingTrkIds == NULL) { danglingTrkIds = celix_arrayList_create(); } @@ -749,7 +749,7 @@ static void bundleContext_cleanupServiceTrackerTrackers(bundle_context_t* ctx) { celix_array_list_t* danglingTrkIds = NULL; - celixThreadRwlock_writeLock(&ctx->lock); + celixThreadRwlock_readLock(&ctx->lock); CELIX_LONG_HASH_MAP_ITERATE(ctx->metaTrackers, iter) { long trkId = iter.key; celix_bundle_context_service_tracker_tracker_entry_t* entry = celix_longHashMap_get(ctx->metaTrackers, trkId); @@ -759,7 +759,7 @@ static void bundleContext_cleanupServiceTrackerTrackers(bundle_context_t* ctx) { "Add missing 'celix_bundleContext_stopTracker' calls.", trkId, symbolicName, - entry->serviceName); + entry->serviceName ? entry->serviceName : "all"); if (danglingTrkIds == NULL) { danglingTrkIds = celix_arrayList_create(); } @@ -784,16 +784,17 @@ static void bundleContext_cleanupServiceRegistration(bundle_context_t* ctx) { celix_array_list_t* danglingSvcIds = NULL; - celixThreadRwlock_writeLock(&ctx->lock); + celixThreadRwlock_readLock(&ctx->lock); for (int i = 0; i < celix_arrayList_size(ctx->svcRegistrations); ++i) { long svcId = celix_arrayList_getLong(ctx->svcRegistrations, i); - fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Dangling service registration with svcId %li, for bundle %s. Add missing 'celix_bundleContext_unregisterService' calls.", svcId, symbolicName); + fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, + "Dangling service registration with svcId %li, for bundle %s. " + "Add missing 'celix_bundleContext_unregisterService' calls.", svcId, symbolicName); if (danglingSvcIds == NULL) { danglingSvcIds = celix_arrayList_create(); } celix_arrayList_addLong(danglingSvcIds, svcId); } - celix_arrayList_clear(ctx->svcRegistrations); celixThreadRwlock_unlock(&ctx->lock); if (danglingSvcIds != NULL) {