This is an automated email from the ASF dual-hosted git repository. pnoltes pushed a commit to branch feature/87-refactor-use-services in repository https://gitbox.apache.org/repos/asf/celix.git
commit 03c0a2ac78dee60fe487e056f1ab106942c27714 Author: Pepijn Noltes <pnol...@apache.org> AuthorDate: Sat Mar 9 14:39:55 2024 +0100 gh-87: Refactor ctx useService* impl to reuse service tracker. Note that also means that useService* functions and methods are no longer use-able in the Celix event thread. --- CHANGES.md | 3 + bundles/shell/shell/gtest/CMakeLists.txt | 6 +- bundles/shell/shell/gtest/src/ShellTestSuite.cc | 54 +++++--- bundles/shell/shell/src/help_command.c | 40 +++--- .../src/CelixBundleContextServicesTestSuite.cc | 32 +---- libs/framework/include/celix/BundleContext.h | 12 +- libs/framework/include/celix_bundle_context.h | 15 ++- libs/framework/src/bundle_context.c | 148 ++++++--------------- 8 files changed, 139 insertions(+), 171 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3221802a..7fee14e7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -66,6 +66,9 @@ limitations under the License. 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. +- It is no longer possible to use the `celix_bundleContext_useService*` functions or `celix::BundleContxt::useService*` + methods on the Celix event thread. The calls will now immediately return and log an error if called on the + Celix event thread. ## New Features diff --git a/bundles/shell/shell/gtest/CMakeLists.txt b/bundles/shell/shell/gtest/CMakeLists.txt index 91a50aef..75d18ea8 100644 --- a/bundles/shell/shell/gtest/CMakeLists.txt +++ b/bundles/shell/shell/gtest/CMakeLists.txt @@ -19,8 +19,10 @@ add_executable(test_shell src/ShellTestSuite.cc ) +add_celix_bundle(celix_shell_empty_resource_test_bundle NO_ACTIVATOR VERSION 0.0.0) + target_link_libraries(test_shell PRIVATE Celix::framework Celix::shell_api GTest::gtest GTest::gtest_main) -celix_target_bundle_set_definition(test_shell NAME TEST_BUNDLES Celix::shell) +celix_target_bundle_set_definition(test_shell NAME TEST_BUNDLES Celix::shell celix_shell_empty_resource_test_bundle) target_compile_options(test_shell PRIVATE -Wno-deprecated-declarations) add_test(NAME test_shell COMMAND test_shell) @@ -31,7 +33,7 @@ if (CELIX_CXX14) add_executable(test_cxx_shell src/ShellTestSuite.cc) target_link_libraries(test_cxx_shell PRIVATE Celix::framework Celix::shell_api GTest::gtest GTest::gtest_main) - celix_target_bundle_set_definition(test_cxx_shell NAME TEST_BUNDLES Celix::ShellCxx) + celix_target_bundle_set_definition(test_cxx_shell NAME TEST_BUNDLES Celix::ShellCxx celix_shell_empty_resource_test_bundle) target_compile_definitions(test_cxx_shell PRIVATE -DCXX_SHELL) add_test(NAME test_cxx_shell COMMAND test_cxx_shell) diff --git a/bundles/shell/shell/gtest/src/ShellTestSuite.cc b/bundles/shell/shell/gtest/src/ShellTestSuite.cc index 318a8379..b7b5f791 100644 --- a/bundles/shell/shell/gtest/src/ShellTestSuite.cc +++ b/bundles/shell/shell/gtest/src/ShellTestSuite.cc @@ -21,19 +21,20 @@ #include <gtest/gtest.h> #include <thread> -#include "celix_shell_command.h" -#include "celix_framework_factory.h" #include "celix_bundle_context.h" -#include "celix_shell.h" -#include "celix_framework_utils.h" #include "celix_constants.h" +#include "celix_framework_factory.h" +#include "celix_framework_utils.h" +#include "celix_shell.h" +#include "celix_shell_command.h" +#include "celix_stdlib_cleanup.h" class ShellTestSuite : public ::testing::Test { public: ShellTestSuite() : ctx{createFrameworkContext()} { auto* fw = celix_bundleContext_getFramework(ctx.get()); size_t nr = celix_framework_utils_installBundleSet(fw, TEST_BUNDLES, true); - EXPECT_EQ(nr, 1); //shell bundle + EXPECT_EQ(nr, 2); //shell and celix_shell_empty_resource_test_bundle bundle } static std::shared_ptr<celix_bundle_context_t> createFrameworkContext() { @@ -54,12 +55,25 @@ public: }}; } + long getBundleIdForResourceBundle() const { + celix_autoptr(celix_array_list_t) bundles = celix_bundleContext_listBundles(ctx.get()); + for (auto i = 0 ; i < celix_arrayList_size(bundles); ++i) { + auto bndId = celix_arrayList_getLong(bundles, i); + celix_autofree char* name = celix_bundleContext_getBundleSymbolicName(ctx.get(), bndId); + if (strstr(name, "resource") != nullptr) { + return bndId; + } + + } + return -1; + } + std::shared_ptr<celix_bundle_context_t> ctx; }; TEST_F(ShellTestSuite, shellBundleInstalledTest) { auto *bndIds = celix_bundleContext_listBundles(ctx.get()); - EXPECT_EQ(1, celix_arrayList_size(bndIds)); + EXPECT_EQ(2, celix_arrayList_size(bndIds)); celix_arrayList_destroy(bndIds); } @@ -71,8 +85,6 @@ static void callCommand(std::shared_ptr<celix_bundle_context_t>& ctx, const char celix_bundle_context_t* context{}; long tracker{-1}; }; - // Note that using celix_bundleContext_useServiceWithOptions to call command quit/stop may result in deadlock. - // For more on this, see https://github.com/apache/celix/issues/629 struct callback_data data{}; data.cmdLine = cmdLine; data.cmdShouldSucceed = cmdShouldSucceed; @@ -104,9 +116,10 @@ TEST_F(ShellTestSuite, testAllCommandsAreCallable) { callCommand(ctx, "non-existing", false); callCommand(ctx, "install a-bundle-loc.zip", true); callCommand(ctx, "help", true); - callCommand(ctx, "help lb", false); //note need namespace + callCommand(ctx, "help lb", true); callCommand(ctx, "help celix::lb", true); callCommand(ctx, "help non-existing-command", false); + callCommand(ctx, "help celix::non-existing-command-with-namespace", false); callCommand(ctx, "lb", true); callCommand(ctx, "lb -l", true); callCommand(ctx, "query", true); @@ -144,7 +157,8 @@ TEST_F(ShellTestSuite, stopFrameworkTest) { TEST_F(ShellTestSuite, stopSelfTest) { auto* list = celix_bundleContext_listBundles(ctx.get()); - EXPECT_EQ(1, celix_arrayList_size(list)); + ASSERT_GE(celix_arrayList_size(list), 1); + auto nrOfBundles = celix_arrayList_size(list); long shellBundleId = celix_arrayList_getLong(list, 0); celix_arrayList_destroy(list); @@ -156,24 +170,33 @@ TEST_F(ShellTestSuite, stopSelfTest) { std::this_thread::sleep_for(std::chrono::milliseconds{100}); list = celix_bundleContext_listBundles(ctx.get()); - EXPECT_EQ(0, celix_arrayList_size(list)); + EXPECT_EQ(nrOfBundles-1, celix_arrayList_size(list)); celix_arrayList_destroy(list); } TEST_F(ShellTestSuite, queryTest) { + struct data { + long resourceBundleId; + }; + data data{}; + data.resourceBundleId = getBundleIdForResourceBundle(); + celix_service_use_options_t opts{}; opts.filter.serviceName = CELIX_SHELL_COMMAND_SERVICE_NAME; opts.filter.filter = "(command.name=celix::query)"; opts.waitTimeoutInSeconds = 1.0; - opts.use = [](void */*handle*/, void *svc) { + opts.callbackHandle = (void*)&data; + opts.use = [](void* handle, void *svc) { + auto *d = static_cast<struct data*>(handle); auto *command = static_cast<celix_shell_command_t*>(svc); - EXPECT_TRUE(command != nullptr); + ASSERT_TRUE(command != nullptr); + ASSERT_TRUE(d != nullptr); { char *buf = nullptr; size_t len; FILE *sout = open_memstream(&buf, &len); - command->executeCommand(command->handle, (char *) "query", sout, sout); + command->executeCommand(command->handle, "query", sout, sout); fclose(sout); char* found = strstr(buf, "Provided services found 1"); //note could be 11, 12, etc EXPECT_TRUE(found != nullptr); @@ -183,7 +206,8 @@ TEST_F(ShellTestSuite, queryTest) { char *buf = nullptr; size_t len; FILE *sout = open_memstream(&buf, &len); - command->executeCommand(command->handle, (char *) "query 0", sout, sout); //note query framework bundle -> no results + auto cmd = std::string{"query "} + std::to_string(d->resourceBundleId); + command->executeCommand(command->handle, cmd.c_str(), sout, sout); //note query test resource bundle -> no results fclose(sout); char* found = strstr(buf, "No results"); //note could be 11, 12, etc EXPECT_TRUE(found != nullptr); diff --git a/bundles/shell/shell/src/help_command.c b/bundles/shell/shell/src/help_command.c index 092a32f4..746de5c0 100644 --- a/bundles/shell/shell/src/help_command.c +++ b/bundles/shell/shell/src/help_command.c @@ -22,8 +22,9 @@ #include <stdint.h> #include "celix_array_list.h" -#include "celix_utils.h" #include "celix_shell.h" +#include "celix_stdlib_cleanup.h" +#include "celix_utils.h" #include "std_commands.h" struct print_handle { @@ -51,11 +52,9 @@ static void printHelp(void *handle, void *svc) { sub = strtok_r(NULL, CELIX_SHELL_COMMAND_SEPARATOR, &save_ptr); if (sub == NULL) { - unsigned int i; celix_array_list_t *commands = NULL; - shell->getCommands(shell->handle, &commands); - for (i = 0; i < celix_arrayList_size(commands); i++) { + for (int i = 0; i < celix_arrayList_size(commands); i++) { char *name = celix_arrayList_get(commands, i); fprintf(out, "%s\n", name); free(name); @@ -71,17 +70,19 @@ static void printHelp(void *handle, void *svc) { shell->getCommands(shell->handle, &commands); bool cmdFound = false; for (i = 0; i < celix_arrayList_size(commands); i++) { - char *name = celix_arrayList_get(commands, i); - if (strcmp(sub, name) == 0) { + char *fqn = celix_arrayList_get(commands, i); + bool hasNamespace = strstr(fqn, "::") != NULL; + char* localName = hasNamespace ? strstr(fqn, "::") + 2 : fqn; + if (strcmp(sub, fqn) == 0 || (hasNamespace && strcmp(sub, localName) == 0)) { cmdFound = true; char *usage_str = NULL; char *desc_str = NULL; - sub_status_desc = shell->getCommandDescription(shell->handle, name, &desc_str); - sub_status_usage = shell->getCommandUsage(shell->handle, name, &usage_str); + sub_status_desc = shell->getCommandDescription(shell->handle, fqn, &desc_str); + sub_status_usage = shell->getCommandUsage(shell->handle, fqn, &usage_str); if (sub_status_usage == CELIX_SUCCESS && sub_status_desc == CELIX_SUCCESS) { - fprintf(out, "Command : %s\n", name); + fprintf(out, "Command : %s\n", fqn); fprintf(out, "Usage : %s\n", usage_str == NULL ? "" : usage_str); fprintf(out, "Description : %s\n", desc_str == NULL ? "" : desc_str); } else { @@ -91,7 +92,7 @@ static void printHelp(void *handle, void *svc) { free(usage_str); free(desc_str); } - free(name); + free(fqn); } celix_arrayList_destroy(commands); @@ -103,16 +104,23 @@ static void printHelp(void *handle, void *svc) { } } -bool helpCommand_execute(void *handle, const char *const_cmdLine, FILE *out, FILE *err) { - celix_bundle_context_t *ctx = handle; - struct print_handle printHandle; - char *cmdLine = celix_utils_strdup(const_cmdLine); +bool helpCommand_execute(void* handle, const char* const_cmdLine, FILE* out, FILE* err) { + celix_bundle_context_t* ctx = handle; + celix_autofree char* cmdLine = celix_utils_strdup(const_cmdLine); + + long trkId = celix_bundleContext_trackServices(ctx, CELIX_SHELL_SERVICE_NAME); + if (trkId < 0) { + fprintf(err, "Error tracking shell services\n"); + return false; + } + struct print_handle printHandle; printHandle.cmdLine = cmdLine; printHandle.out = out; printHandle.err = err; - bool called = celix_bundleContext_useService(ctx, CELIX_SHELL_SERVICE_NAME, &printHandle, printHelp); + bool called = celix_bundleContext_useTrackedService(ctx, trkId, &printHandle, printHelp); + + celix_bundleContext_stopTracker(ctx, trkId); - free(cmdLine); return called & printHandle.callSucceeded; } diff --git a/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc b/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc index 12ffcd94..a164520f 100644 --- a/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc +++ b/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc @@ -974,16 +974,14 @@ TEST_F(CelixBundleContextServicesTestSuite, ServiceTrackerWithRaceConditionTest) celix_bundleContext_stopTracker(ctx, trackerId); }; -TEST_F(CelixBundleContextServicesTestSuite, UseServiceDoesNotBlockInEventLoop) { - void *svc1 = (void*)0x100; - - auto set = [](void *handle, void */*svc*/) { - celix_bundle_context_t *ctx = static_cast<celix_bundle_context_t *>(handle); +TEST_F(CelixBundleContextServicesTestSuite, UseServiceDoesNotWorkInEventLoop) { + auto callback = [](void* data) { + auto* ctx = static_cast<celix_bundle_context_t*>(data); celix_service_use_options_t use_opts{}; use_opts.filter.serviceName = "NotAvailable"; use_opts.waitTimeoutInSeconds = 3600; // unacceptable long blocking use_opts.callbackHandle = nullptr; - use_opts.use = [](void *handle, void *svc) { + use_opts.use = [](void* handle, void* svc) { FAIL() << "We shouldn't get here: (" << handle << "," << svc << ")"; }; @@ -991,25 +989,9 @@ TEST_F(CelixBundleContextServicesTestSuite, UseServiceDoesNotBlockInEventLoop) { ASSERT_FALSE(called); }; - long svcId1 = celix_bundleContext_registerService(ctx, svc1, "NA", nullptr); - ASSERT_GE(svcId1, 0); - - celix_service_tracking_options_t opts{}; - opts.callbackHandle = (void*)ctx; - opts.filter.serviceName = "NA"; - opts.set = set; - long trackerId = celix_bundleContext_trackServicesWithOptionsAsync(ctx, &opts); - ASSERT_TRUE(trackerId >= 0); - - void *svc2 = (void*)0x200; //no ranking - long svcId2 = celix_bundleContext_registerServiceAsync(ctx, svc2, "NotAvailable", nullptr); - ASSERT_GE(svcId2, 0); - celix_bundleContext_waitForAsyncTracker(ctx, trackerId); - celix_bundleContext_waitForAsyncRegistration(ctx, svcId2); - - celix_bundleContext_unregisterService(ctx, svcId2); - celix_bundleContext_stopTracker(ctx, trackerId); - celix_bundleContext_unregisterService(ctx, svcId1); + long eventId = celix_framework_fireGenericEvent( + fw, 0, CELIX_FRAMEWORK_BUNDLE_ID, "test event", ctx, callback, nullptr, nullptr); + celix_framework_waitForGenericEvent(fw, eventId); } TEST_F(CelixBundleContextServicesTestSuite, ServicesTrackerSetTest) { diff --git a/libs/framework/include/celix/BundleContext.h b/libs/framework/include/celix/BundleContext.h index d98c0c5e..1bb89fae 100644 --- a/libs/framework/include/celix/BundleContext.h +++ b/libs/framework/include/celix/BundleContext.h @@ -33,7 +33,7 @@ #include "celix/Bundle.h" #include "celix/Framework.h" -#include "celix/dm/DependencyManager.h" //TODO, TBD include or forward declaration? +#include "celix/dm/DependencyManager.h" namespace celix { @@ -106,6 +106,10 @@ namespace celix { /** * @brief Use a service registered in the Celix framework using a fluent builder API. * + * @deprecated celix_bundleContext_useService are deprecated and should be considered test utils functions. In + * operational code use celix_bundleContext_trackService* combined with celix_bundleContext_useTrackedService* + * functions instead. + * * The service use can be fine tuned using the returned UseServiceBuilder API. * * With this API a Celix service can be used by providing use functions. @@ -130,6 +134,7 @@ namespace celix { * @return A UseServiceBuilder object. */ template<typename I> + [[deprecated]] UseServiceBuilder<I> useService(const std::string& name = {}) { return UseServiceBuilder<I>{cCtx, celix::typeName<I>(name), true}; } @@ -137,6 +142,10 @@ namespace celix { /** * @brief Use services registered in the Celix framework using a fluent builder API. * + * @deprecated celix_bundleContext_useService are deprecated and should be considered test utils functions. In + * operational code use celix_bundleContext_trackService* combined with celix_bundleContext_useTrackedService* + * functions instead. + * * The service use can be fine tuned using the returned UseServiceBuilder API. * * With this API Celix services can be used by providing use functions. @@ -158,6 +167,7 @@ namespace celix { * @return A UseServiceBuilder object. */ template<typename I> + [[deprecated]] UseServiceBuilder<I> useServices(const std::string& name = {}) { return UseServiceBuilder<I>{cCtx, celix::typeName<I>(name), false}; } diff --git a/libs/framework/include/celix_bundle_context.h b/libs/framework/include/celix_bundle_context.h index fb9dc94b..56ec0e5c 100644 --- a/libs/framework/include/celix_bundle_context.h +++ b/libs/framework/include/celix_bundle_context.h @@ -391,7 +391,8 @@ CELIX_FRAMEWORK_EXPORT celix_array_list_t* celix_bundleContext_findServicesWithO * @brief Use the service with the provided service id using the provided callback. The Celix framework will ensure that * the targeted service cannot be removed during the callback. * - * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useTrackedService* functions + * @deprecated celix_bundleContext_useService are deprecated and should be considered test utils functions. In + * operational code use celix_bundleContext_trackService* combined with celix_bundleContext_useTrackedService* functions * instead. * * The svc is should only be considered valid during the callback. @@ -417,7 +418,8 @@ CELIX_FRAMEWORK_DEPRECATED_EXPORT bool celix_bundleContext_useServiceWithId(celi /** * @brief Use the highest ranking service with the provided service name using the provided callback. * - * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useTrackedService* functions + * @deprecated celix_bundleContext_useService are deprecated and should be considered test utils functions. In + * operational code use celix_bundleContext_trackService* combined with celix_bundleContext_useTrackedService* functions * instead. * * The Celix framework will ensure that the targeted service cannot be removed during the callback. @@ -444,7 +446,8 @@ CELIX_FRAMEWORK_DEPRECATED_EXPORT bool celix_bundleContext_useService( /** * @brief Use the services with the provided service name using the provided callback. * - * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useTrackedService* functions + * @deprecated celix_bundleContext_useService are deprecated and should be considered test utils functions. In + * operational code use celix_bundleContext_trackService* combined with celix_bundleContext_useTrackedService* functions * instead. * * The Celix framework will ensure that the targeted service cannot be removed during the callback. @@ -548,7 +551,8 @@ typedef struct celix_service_use_options { /** * @brief Use the highest ranking service satisfying the provided service filter options using the provided callback. * - * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useTrackedService* functions + * @deprecated celix_bundleContext_useService are deprecated and should be considered test utils functions. In + * operational code use celix_bundleContext_trackService* combined with celix_bundleContext_useTrackedService* functions * instead. * * The Celix framework will ensure that the targeted service cannot be removed during the callback. @@ -572,7 +576,8 @@ CELIX_FRAMEWORK_DEPRECATED_EXPORT bool celix_bundleContext_useServiceWithOptions /** * @brief Use the services with the provided service filter options using the provided callback. * - * @deprecated Use celix_bundleContext_trackService* combined with celix_bundleContext_useTrackedService* functions + * @deprecated celix_bundleContext_useService are deprecated and should be considered test utils functions. In + * operational code 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. diff --git a/libs/framework/src/bundle_context.c b/libs/framework/src/bundle_context.c index 99624cae..149fe611 100644 --- a/libs/framework/src/bundle_context.c +++ b/libs/framework/src/bundle_context.c @@ -1118,130 +1118,64 @@ size_t celix_bundleContext_useServices( return celix_bundleContext_useServicesWithOptions(ctx, &opts); } -typedef struct celix_bundle_context_use_service_data { - celix_bundle_context_t* ctx; - const celix_service_use_options_t* opts; - - bool called; //for use service - size_t count; //for use services - celix_service_tracker_t * svcTracker; -} celix_bundle_context_use_service_data_t; - -static void celix_bundleContext_useServiceWithOptions_1_CreateServiceTracker(void *data) { - celix_bundle_context_use_service_data_t* d = data; - assert(celix_framework_isCurrentThreadTheEventLoop(d->ctx->framework)); - - celix_service_tracking_options_t trkOpts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS; - trkOpts.filter = d->opts->filter; - - d->called = false; - d->count = 0; - d->svcTracker = celix_serviceTracker_createWithOptions(d->ctx, &trkOpts); -} - -static void celix_bundleContext_useServiceWithOptions_2_UseServiceTracker(void *data) { - celix_bundle_context_use_service_data_t* d = data; - assert(celix_framework_isCurrentThreadTheEventLoop(d->ctx->framework)); - - d->called = celix_serviceTracker_useHighestRankingService(d->svcTracker, d->opts->filter.serviceName, 0, d->opts->callbackHandle, d->opts->use, d->opts->useWithProperties, d->opts->useWithOwner); -} - -bool celix_bundleContext_useServiceWithOptions( - celix_bundle_context_t *ctx, - const celix_service_use_options_t *opts) { +static size_t celix_bundleContext_useServicesInternal(celix_bundle_context_t *ctx, + const celix_service_use_options_t *opts, bool singular) { if (opts == NULL || opts->filter.serviceName == NULL) { - return false; + return 0; } - celix_bundle_context_use_service_data_t data = {0}; - data.ctx = ctx; - data.opts = opts; - bool called = false; - if (celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) { - // Ignore timeout: blocking the event loop prevents any progress to be made - celix_bundleContext_useServiceWithOptions_1_CreateServiceTracker(&data); - celix_bundleContext_useServiceWithOptions_2_UseServiceTracker(&data); - celix_serviceTracker_destroy(data.svcTracker); - return data.called; + fw_log(ctx->framework->logger, + CELIX_LOG_LEVEL_ERROR, + "Cannot use services on the event loop thread. Ignoring call."); + return 0; } - long eventId = celix_framework_fireGenericEvent(ctx->framework, -1, celix_bundle_getId(ctx->bundle), "create service tracker for celix_bundleContext_useServiceWithOptions", &data, celix_bundleContext_useServiceWithOptions_1_CreateServiceTracker, NULL, NULL); - celix_framework_waitForGenericEvent(ctx->framework, eventId); - + celix_service_tracking_options_t trackingOpts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS; + memcpy(&trackingOpts.filter, &opts->filter, sizeof(opts->filter)); + long trkId = celix_bundleContext_trackServicesWithOptions(ctx, &trackingOpts); + if (trkId < 0) { + return 0; + } - if(opts->flags & CELIX_SERVICE_USE_DIRECT) { - if(opts->flags & CELIX_SERVICE_USE_SOD) { - // check CelixBundleContextServicesTestSuite.UseServiceOnDemandDirectlyWithAsyncRegisterTest to see what is "service on demand". - celix_framework_waitUntilNoPendingRegistration(ctx->framework); + //Note because useService* is primary an API used in tests, waiting for events to that service-on-demand is + //working and useService* calls work after an async service (un)registration. + celix_framework_waitForEmptyEventQueue(ctx->framework); + + celix_tracked_service_use_options_t useOpts = CELIX_EMPTY_TRACKER_SERVICE_USE_OPTIONS; + useOpts.callbackHandle = opts->callbackHandle; + useOpts.use = opts->use; + useOpts.useWithProperties = opts->useWithProperties; + useOpts.useWithOwner = opts->useWithOwner; + + size_t count; + if (singular) { + struct timespec start = celix_gettime(CLOCK_MONOTONIC); + bool called = celix_bundleContext_useTrackedServiceWithOptions(ctx, trkId, &useOpts); + if (!called && opts->waitTimeoutInSeconds > 0) { + while (!called && celix_elapsedtime(CLOCK_MONOTONIC, start) < opts->waitTimeoutInSeconds) { + usleep(1000); + called = celix_bundleContext_useTrackedServiceWithOptions(ctx, trkId, &useOpts); + } } - called = celix_serviceTracker_useHighestRankingService(data.svcTracker, NULL, opts->waitTimeoutInSeconds, opts->callbackHandle, opts->use, opts->useWithProperties, opts->useWithOwner); + count = called ? 1 : 0; } else { - struct timespec startTime = celix_gettime(CLOCK_MONOTONIC); - bool useServiceIsDone = false; - do { - eventId = celix_framework_fireGenericEvent(ctx->framework, -1, celix_bundle_getId(ctx->bundle), "use service tracker for celix_bundleContext_useServiceWithOptions", &data, celix_bundleContext_useServiceWithOptions_2_UseServiceTracker, NULL, NULL); - celix_framework_waitForGenericEvent(ctx->framework, eventId); - - bool timeoutNotUsed = opts->waitTimeoutInSeconds == 0; - bool timeoutExpired = celix_elapsedtime(CLOCK_MONOTONIC, startTime) > opts->waitTimeoutInSeconds; - - called = data.called; - - useServiceIsDone = timeoutNotUsed || timeoutExpired || called; - if (!useServiceIsDone) { - usleep(10); - } - } while (!useServiceIsDone); + count = celix_bundleContext_useTrackedServicesWithOptions(ctx, trkId, &useOpts); } - - eventId = celix_framework_fireGenericEvent(ctx->framework, -1, celix_bundle_getId(ctx->bundle), "close service tracker for celix_bundleContext_useServiceWithOptions", data.svcTracker, (void *)celix_serviceTracker_destroy, NULL, NULL); - celix_framework_waitForGenericEvent(ctx->framework, eventId); - - return called; + celix_bundleContext_stopTracker(ctx, trkId); + return count; } -static void celix_bundleContext_useServicesWithOptions_2_UseServiceTracker(void *data) { - celix_bundle_context_use_service_data_t* d = data; - d->count = celix_serviceTracker_useServices(d->svcTracker, d->opts->filter.serviceName, d->opts->callbackHandle, d->opts->use, d->opts->useWithProperties, d->opts->useWithOwner); +bool celix_bundleContext_useServiceWithOptions( + celix_bundle_context_t *ctx, + const celix_service_use_options_t *opts) { + return celix_bundleContext_useServicesInternal(ctx, opts, true) > 0; } size_t celix_bundleContext_useServicesWithOptions( celix_bundle_context_t *ctx, const celix_service_use_options_t *opts) { - if (opts == NULL || opts->filter.serviceName == NULL) { - return 0; - } - - celix_bundle_context_use_service_data_t data = {0}; - data.ctx = ctx; - data.opts = opts; - - if (celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) { - celix_bundleContext_useServiceWithOptions_1_CreateServiceTracker(&data); - celix_bundleContext_useServicesWithOptions_2_UseServiceTracker(&data); - celix_serviceTracker_destroy(data.svcTracker); - return data.count; - } - - long eventId = celix_framework_fireGenericEvent(ctx->framework, -1, celix_bundle_getId(ctx->bundle), "create service tracker for celix_bundleContext_useServicesWithOptions", &data, celix_bundleContext_useServiceWithOptions_1_CreateServiceTracker, NULL, NULL); - celix_framework_waitForGenericEvent(ctx->framework, eventId); - - if (opts->flags & CELIX_SERVICE_USE_DIRECT) { - if(opts->flags & CELIX_SERVICE_USE_SOD) { - // check CelixBundleContextServicesTestSuite.UseServicesOnDemandDirectlyWithAsyncRegisterTest to see what is "service on demand". - celix_framework_waitUntilNoPendingRegistration(ctx->framework); - } - celix_bundleContext_useServicesWithOptions_2_UseServiceTracker(&data); - } else { - eventId = celix_framework_fireGenericEvent(ctx->framework, -1, celix_bundle_getId(ctx->bundle), "use service tracker for celix_bundleContext_useServicesWithOptions", &data, celix_bundleContext_useServicesWithOptions_2_UseServiceTracker, NULL, NULL); - celix_framework_waitForGenericEvent(ctx->framework, eventId); - } - - eventId = celix_framework_fireGenericEvent(ctx->framework, -1, celix_bundle_getId(ctx->bundle), "use service tracker for celix_bundleContext_useServicesWithOptions", data.svcTracker, (void *)celix_serviceTracker_destroy, NULL, NULL); - celix_framework_waitForGenericEvent(ctx->framework, eventId); - - return data.count; + return celix_bundleContext_useServicesInternal(ctx, opts, false); } long celix_bundleContext_trackServices(bundle_context_t* ctx, const char* serviceName) {