From 7b37d720a06b756b3a07576de8708bc400afd460 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 11 Feb 2024 15:28:06 +0100 Subject: [PATCH] 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;