diff --git a/CHANGES.md b/CHANGES.md index 1ca2af688..3221802a1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -61,6 +61,11 @@ limitations under the License. - the celix_arrayList_add function no longer accepts a NULL value. - version.h and version_range.h are removed and no longer supported. Use celix_version.h and celix_version_range.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. +- 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 fe11a78f5..4e94ff11f 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_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, nullptr, nullptr); + long trkId2 = celix_bundleContext_trackServices(ctx.get(), CELIX_LOG_SERVICE_NAME); EXPECT_EQ(2, control->nrOfLogServices(control->handle, nullptr)); //request a 'logger1' log service @@ -221,7 +221,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_trackServices(ctx.get(), CELIX_LOG_SERVICE_NAME); celix_framework_waitForEmptyEventQueue(fw.get()); EXPECT_EQ(2, control->nrOfLogServices(control->handle, nullptr)); 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); 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/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/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..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); }; @@ -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 d17b3892b..8218ecf05 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" @@ -52,6 +51,8 @@ class CelixBundleContextServicesTestSuite : public ::testing::Test { fw = celix_frameworkFactory_createFramework(properties); ctx = framework_getContext(fw); + + celix_err_resetErrors(); } ~CelixBundleContextServicesTestSuite() override { @@ -63,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; }; @@ -104,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; }; @@ -144,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); @@ -153,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; }; @@ -196,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); @@ -205,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; }; @@ -256,7 +257,7 @@ TEST_F(CelixBundleContextServicesTestSuite, RegisterServiceTest) { }; const char *calcName = "calc"; - calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -272,7 +273,7 @@ TEST_F(CelixBundleContextServicesTestSuite, TegisterServiceAsyncTest) { }; const char *calcName = "calc"; - calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -307,7 +308,7 @@ TEST_F(CelixBundleContextServicesTestSuite, UseServicesWithoutNameTest) { }; const char *calcName = "calc"; - struct calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -360,7 +361,7 @@ TEST_F(CelixBundleContextServicesTestSuite, TegisterMultipleAndUseServicesTest) }; const char *calcName = "calc"; - struct calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -441,7 +442,7 @@ TEST_F(CelixBundleContextServicesTestSuite, UseServiceInUseCallbackTest) { }; const char *calcName = "calc"; - struct calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -484,7 +485,7 @@ TEST_F(CelixBundleContextServicesTestSuite, RegisterAndUseServiceTest) { }; const char *calcName = "calc"; - struct calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -507,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; }); @@ -555,7 +556,7 @@ TEST_F(CelixBundleContextServicesTestSuite, RegisterAndUseWithForcedRaceConditio }; const char *calcName = "calc"; - struct calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -576,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); @@ -586,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; }; @@ -642,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); @@ -674,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); @@ -692,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); @@ -740,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); @@ -856,7 +872,7 @@ TEST_F(CelixBundleContextServicesTestSuite, ServiceTrackerWithRaceConditionTest) }; const char *calcName = "calc"; - struct calc svc; + calc svc{}; svc.calc = [](int n) -> int { return n * 42; }; @@ -875,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; @@ -883,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(); @@ -904,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); @@ -1177,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); @@ -1328,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_trackServices(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); @@ -1378,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)); @@ -1643,6 +1664,44 @@ 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, WaitForNonexistingTracker) { + celix_bundleContext_waitForAsyncTracker(ctx, 111L /* non-existing tracker */); +} + TEST_F(CelixBundleContextServicesTestSuite, StopBundleTrackerBeforeAsyncTrackerIsCreatedTest) { struct callback_data { std::atomic count{}; @@ -1741,3 +1800,212 @@ 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"); + 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"); + 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); + 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); +} + +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/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; + } }; /** 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 d582fabd6..fb9dc94b7 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" { @@ -393,51 +388,210 @@ 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 Track the highest ranking service with the provided serviceName. + * @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 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. + * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useTrackedService* functions + * instead. * - * 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. + * 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. * - * @param ctx The bundle context. - * @param serviceName The required service name to track. - * If NULL is all service are tracked. + * 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 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. + * @param use The callback, which will be called when service is retrieved. + * @param bool returns true if a service was found. */ -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_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_useTrackedService* 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. + * 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_DEPRECATED_EXPORT bool celix_bundleContext_useService( + celix_bundle_context_t *ctx, + const char* serviceName, + void *callbackHandle, + void (*use)(void *handle, void *svc) ); /** - * @brief Track the highest ranking service with the provided serviceName. + * @brief Use the services with the provided service name using the provided callback. * - * 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. + * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useTrackedService* functions + * 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. + * 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 long celix_bundleContext_trackService( - celix_bundle_context_t *ctx, - const char* serviceName, - void* callbackHandle, - void (*set)(void* handle, void* svc) +CELIX_FRAMEWORK_DEPRECATED_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. + * + * @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. + * + * 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_DEPRECATED_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. + * + * @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. + * + * 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_DEPRECATED_EXPORT size_t celix_bundleContext_useServicesWithOptions( + celix_bundle_context_t *ctx, + const celix_service_use_options_t *opts); + /** * @brief Track services with the provided serviceName. * @@ -447,42 +601,26 @@ 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. + * @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 { /** @@ -491,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. @@ -571,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 @@ -607,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. @@ -615,236 +760,290 @@ 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. */ -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 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 specified tracker id by invoking the provided use 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. + * The callback is executed on the thread that invokes this function, and the function waits until the use callback + * is completed before returning. * - * 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). + * 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. * - * 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 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 void celix_bundleContext_stopTrackerAsync( - celix_bundle_context_t *ctx, - long trackerId, - void *doneCallbackData, - void (*doneCallback)(void* doneCallbackData)); +CELIX_FRAMEWORK_EXPORT +bool celix_bundleContext_useTrackedService(celix_bundle_context_t* ctx, + long trackerId, + void* callbackHandle, + void (*use)(void* handle, void* svc)); /** - * @brief Wait for (async) creation of tracker + * @brief Use the services tracked by the specified tracker id by invoking the provided use callback. + * + * 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. + * + * 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 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 void celix_bundleContext_waitForAsyncTracker(celix_bundle_context_t *ctx, long trackerId); +CELIX_FRAMEWORK_EXPORT +size_t celix_bundleContext_useTrackedServices(celix_bundle_context_t* ctx, + long trackerId, + void* callbackHandle, + void (*use)(void* handle, void* svc)); /** - * @brief Wait for (async) stopping of tracking. + * @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. */ -CELIX_FRAMEWORK_EXPORT void celix_bundleContext_waitForAsyncStopTracker(celix_bundle_context_t *ctx, long trackerId); +typedef struct celix_tracked_service_use_options { + /** + * @brief An optional pointer to a user-defined context or data structure, passed to all specified callback + * functions (use, useWithProperties, and useWithOwner). + * + * Default value: NULL. + */ + void* callbackHandle CELIX_OPTS_INIT; + /** + * @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. + * + * The svc pointer is only valid during the callback. + * + * Default value: NULL. + */ + void (*use)(void* handle, void* svc) CELIX_OPTS_INIT; + + /** + * @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; + + /** + * @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; +} celix_tracked_service_use_options_t; + +#ifndef __cplusplus /** - * @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. + * @brief Macro to initialize a celix_tracked_service_use_options_t structure with default values. */ -CELIX_FRAMEWORK_EXPORT void celix_bundleContext_stopTracker(celix_bundle_context_t *ctx, long trackerId); +#define CELIX_EMPTY_TRACKER_SERVICE_USE_OPTIONS \ + { .callbackHandle = NULL, .use = NULL, .useWithProperties = NULL, .useWithOwner = NULL } +#endif /** - * @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. + * @brief Use the highest-ranking service tracked by the specified tracker id by invoking the callbacks + * specified in the provided options. * - * 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. + * The callbacks are executed on the thread that invokes this function, and the function waits until all callbacks + * are completed before returning. * - * This function will block until the callback is finished. As result it is possible to provide callback data from the - * stack. + * 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 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. + * @param[in] ctx The bundle context. + * @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_useServiceWithId( - celix_bundle_context_t *ctx, - long serviceId, - const char* serviceName /*sanity check*/, - void *callbackHandle, - void (*use)(void *handle, void* svc) -); +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 highest ranking service with the provided service name using the provided callback. + * @brief Use the services tracked by the specified tracker id by invoking the callbacks specified in the + * provided options for each matching service. * - * 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. + * 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. * - * This function will block until the callback is finished. As result it is possible to provide callback data from the - * stack. + * 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 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. + * @param[in] ctx The bundle context. + * @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 bool celix_bundleContext_useService( - celix_bundle_context_t *ctx, - const char* serviceName, - void *callbackHandle, - void (*use)(void *handle, void *svc) -); +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 name 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. - * - * 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. + * @return The number of tracked services or 0 if the tracker id is unknown or < 0. */ -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_getTrackedServiceCount(celix_bundle_context_t *ctx, long trackerId); /** - * @brief Service Use Options used to fine tune which services to use and which callbacks to use. + * @brief Get the service name of the tracked services for the provided tracker id. + * + * Silently ignore tracker ids < 0 and invalid tracker ids. + * + * @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. */ -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; +CELIX_FRAMEWORK_EXPORT +const char* celix_bundleContext_getTrackedServiceName(celix_bundle_context_t *ctx, long trackerId); - /** - * @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) - */ - double waitTimeoutInSeconds CELIX_OPTS_INIT; - - /** - * @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 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. +/** + * @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. */ -#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 +CELIX_FRAMEWORK_EXPORT +const char* celix_bundleContext_getTrackedServiceFilter(celix_bundle_context_t* ctx, long trackerId); /** - * @brief Use the highest ranking service satisfying the provided service filter options using the provided callback. + * @brief Stop the tracker with the provided track id. * - * The Celix framework will ensure that the targeted service cannot be removed during the callback. + * 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 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. + * 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. * - * This function will block until the callback is finished. As result it is possible to provide callback data from the - * stack. + * 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). * - * @param ctx The bundle context. - * @param opts The required options. Note that the serviceName is required. - * @return True if a service was found. + * Will log a error if the provided tracker id is unknown. Will silently ignore trackerId < 0. */ -CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useServiceWithOptions( +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, 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. + * + * @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 Use the services with the provided service filter options using the provided callback. + * @brief Wait, if able, for (async) stopping of tracking. * - * The Celix framework will ensure that the targeted service cannot be removed during the callback. + * 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. * - * 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. + */ +CELIX_FRAMEWORK_EXPORT void celix_bundleContext_waitForAsyncStopTracker(celix_bundle_context_t *ctx, long trackerId); + +/** + * @brief Stop the tracker with the provided track id. * - * This function will block until the callback is finished. As result it is possible to provide callback data from the - * stack. + * 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_stopTrackerAsync instead. * - * @param ctx The bundle context. - * @param opts The required options. Note that the serviceName is required. - * @return The number of services found and called + * Will log a error if the provided tracker id is unknown. Will silently ignore trackerId < 0. */ -CELIX_FRAMEWORK_EXPORT size_t celix_bundleContext_useServicesWithOptions( - celix_bundle_context_t *ctx, - const celix_service_use_options_t *opts); +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. + */ +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/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; diff --git a/libs/framework/include_deprecated/service_tracker.h b/libs/framework/include_deprecated/service_tracker.h index 5f75c8abf..bc9df3941 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,22 @@ 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..97a4f6e29 100644 --- a/libs/framework/src/bundle.c +++ b/libs/framework/src/bundle.c @@ -580,12 +580,20 @@ 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); - 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_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) { celix_bundle_service_tracker_list_entry_t *entry = calloc(1, sizeof(*entry)); entry->filter = celix_utils_strdup(trkEntry->tracker->filter); @@ -598,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 e1075e71b..ee8906485 100644 --- a/libs/framework/src/bundle_context.c +++ b/libs/framework/src/bundle_context.c @@ -65,13 +65,13 @@ 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 = 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,22 +84,21 @@ 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); + celixThreadRwlock_destroy(&context->lock); if (context->mng != NULL) { celix_dependencyManager_removeAllComponents(context->mng); @@ -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) { @@ -435,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; } @@ -463,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); @@ -473,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) { @@ -518,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) { - celixThreadMutex_lock(&ctx->mutex); - 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; - celixThreadMutex_unlock(&ctx->mutex); + 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) { @@ -564,10 +567,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); @@ -591,11 +594,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++; - hashMap_put(ctx->bundleTrackers, (void*)(entry->trackerId), entry); + 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; @@ -670,25 +673,29 @@ 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) { + celixThreadRwlock_readLock(&ctx->lock); + 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); } - celixThreadMutex_unlock(&ctx->mutex); + celixThreadRwlock_unlock(&ctx->lock); if (danglingTrkIds != NULL) { for (int i = 0; i < celix_arrayList_size(danglingTrkIds); ++i) { @@ -699,26 +706,31 @@ 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); + 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); + 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 ? entry->tracker->filter : "unknown"); if (danglingTrkIds == NULL) { danglingTrkIds = celix_arrayList_create(); } celix_arrayList_addLong(danglingTrkIds, trkId); } - celixThreadMutex_unlock(&ctx->mutex); + celixThreadRwlock_unlock(&ctx->lock); if (danglingTrkIds != NULL) { for (int i = 0; i < celix_arrayList_size(danglingTrkIds); ++i) { @@ -729,26 +741,31 @@ 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); + 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); + 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 ? entry->serviceName : "all"); if (danglingTrkIds == NULL) { danglingTrkIds = celix_arrayList_create(); } celix_arrayList_addLong(danglingTrkIds, trkId); } - celixThreadMutex_unlock(&ctx->mutex); + celixThreadRwlock_unlock(&ctx->lock); if (danglingTrkIds != NULL) { for (int i = 0; i < celix_arrayList_size(danglingTrkIds); ++i) { @@ -767,16 +784,18 @@ static void bundleContext_cleanupServiceRegistration(bundle_context_t* ctx) { celix_array_list_t* danglingSvcIds = NULL; - celixThreadMutex_lock(&ctx->mutex); + 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); } - celixThreadMutex_unlock(&ctx->mutex); + celixThreadRwlock_unlock(&ctx->lock); if (danglingSvcIds != NULL) { for (int i = 0; i < celix_arrayList_size(danglingSvcIds); ++i) { @@ -790,18 +809,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); - hashMap_remove(tracker->ctx->stoppingTrackerEventIds, (void*)tracker->trackerId); - celixThreadMutex_unlock(&tracker->ctx->mutex); + celixThreadRwlock_writeLock(&tracker->ctx->lock); + celix_longHashMap_remove(tracker->ctx->stoppingTrackerEventIds, tracker->trackerId); + 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); - hashMap_remove(tracker->ctx->stoppingTrackerEventIds, (void*)tracker->trackerId); - celixThreadMutex_unlock(&tracker->ctx->mutex); + celixThreadRwlock_writeLock(&tracker->ctx->lock); + celix_longHashMap_remove(tracker->ctx->stoppingTrackerEventIds, tracker->trackerId); + celixThreadRwlock_unlock(&tracker->ctx->lock); if (tracker->isFreeFilterNeeded) { free((char*)tracker->opts.filter.serviceName); free((char*)tracker->opts.filter.versionRange); @@ -813,9 +832,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); - hashMap_remove(tracker->ctx->stoppingTrackerEventIds, (void*)tracker->trackerId); - celixThreadMutex_unlock(&tracker->ctx->mutex); + celixThreadRwlock_writeLock(&tracker->ctx->lock); + celix_longHashMap_remove(tracker->ctx->stoppingTrackerEventIds, tracker->trackerId); + celixThreadRwlock_unlock(&tracker->ctx->lock); free(tracker->serviceName); free(tracker); } @@ -831,36 +850,39 @@ 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 (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) } 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); @@ -885,7 +907,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); @@ -896,7 +918,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) { @@ -908,10 +930,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); } } @@ -930,34 +952,41 @@ 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."); + return; + } bool found = false; long eventId = -1; long svcId = -1; if (waitForStart) { - celixThreadMutex_lock(&ctx->mutex); - if (hashMap_containsKey(ctx->bundleTrackers, (void *) trackerId)) { + celixThreadRwlock_readLock(&ctx->lock); + 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); + celixThreadRwlock_unlock(&ctx->lock); } else { - celixThreadMutex_lock(&ctx->mutex); - if (hashMap_containsKey(ctx->stoppingTrackerEventIds, (void*)trackerId)) { + celixThreadRwlock_readLock(&ctx->lock); + 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); + celixThreadRwlock_unlock(&ctx->lock); } if (found) { @@ -966,6 +995,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); } } @@ -1187,69 +1221,26 @@ 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_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.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_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.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)) { - 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)) { - 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)); - 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); @@ -1258,7 +1249,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); } @@ -1266,9 +1257,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) { @@ -1307,17 +1298,18 @@ 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; - hashMap_put(ctx->serviceTrackers, (void *) trackerId, entry); - celixThreadMutex_unlock(&ctx->mutex); + celix_longHashMap_put(ctx->serviceTrackers, trackerId, entry); + celixThreadRwlock_unlock(&ctx->lock); } 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; @@ -1331,13 +1323,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); + + celixThreadRwlock_writeLock(&ctx->lock); entry->trackerId = ctx->nextTrackerId++; long trackerId = entry->trackerId; - hashMap_put(ctx->serviceTrackers, (void *)entry->trackerId, entry); - celixThreadMutex_unlock(&ctx->mutex); + celix_longHashMap_put(ctx->serviceTrackers, entry->trackerId, entry); + celixThreadRwlock_unlock(&ctx->lock); + + 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); - 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); if (!async) { celix_framework_waitForGenericEvent(ctx->framework, id); @@ -1355,6 +1356,145 @@ 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; + } + + 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; +} + +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_rwlock_rlock_guard_t) lck = celixRwlockRlockGuard_init(&ctx->lock); + celix_service_tracker_t* trk = celix_bundleContext_findServiceTracker(ctx, trackerId); + if (!trk) { + return 0; + } + + if (singleUse) { + bool called = celix_serviceTracker_useHighestRankingService(trk, + NULL, + 0, + 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_rwlock_rlock_guard_t) lck = celixRwlockRlockGuard_init(&ctx->lock); + 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_rwlock_rlock_guard_t) lck = celixRwlockRlockGuard_init(&ctx->lock); + 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; @@ -1445,9 +1585,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; @@ -1470,10 +1610,10 @@ long celix_bundleContext_trackServiceTrackersInternal( } if (entry->serviceId >= 0) { - celixThreadMutex_lock(&ctx->mutex); - hashMap_put(ctx->metaTrackers, (void*)entry->trackerId, entry); + 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 38482d1d6..5fe106016 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" { @@ -46,47 +48,50 @@ 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; 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_rwlock_t + lock; // protects fields below + 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..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; @@ -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))) { + 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) { 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; } @@ -743,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; } @@ -758,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) { @@ -788,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++) { @@ -817,4 +817,17 @@ 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->state.mutex); + return (size_t) celix_arrayList_size(tracker->state.trackedServices); +} + +const char* celix_serviceTracker_getTrackedServiceName(celix_service_tracker_t *tracker) { + return tracker->serviceName; +} + +const char* celix_serviceTracker_getTrackedServiceFilter(celix_service_tracker_t *tracker) { + 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 { 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)