Merge pull request #286 from apache/feature/async_svc_registration
Feature/async svc registration
diff --git a/bundles/http_admin/http_admin/src/http_admin.c b/bundles/http_admin/http_admin/src/http_admin.c
index ca7c93d..20b2287 100755
--- a/bundles/http_admin/http_admin/src/http_admin.c
+++ b/bundles/http_admin/http_admin/src/http_admin.c
@@ -122,17 +122,17 @@
//Destroy alias map by removing symbolic links first.
unsigned int size = celix_arrayList_size(admin->aliasList);
- for (unsigned int i = (size - 1); i < size; i--) {
+ for (int i = 0; i < size; ++i) {
http_alias_t *alias = celix_arrayList_get(admin->aliasList, i);
//Delete alias in cache directory
- if (remove(alias->alias_path) < 0)
+ if (remove(alias->alias_path) < 0) {
fprintf(stdout, "remove of %s failed\n", alias->alias_path);
+ }
free(alias->url);
free(alias->alias_path);
free(alias);
- celix_arrayList_removeAt(admin->aliasList, i);
}
celix_arrayList_destroy(admin->aliasList);
@@ -520,6 +520,7 @@
http_alias_t *alias = arrayList_get(admin->aliasList, i);
if(alias->bundle_id == bundle_id) {
remove(alias->alias_path); //Delete alias in cache directory
+ free(alias->url);
free(alias->alias_path);
free(alias);
celix_arrayList_removeAt(admin->aliasList, i);
diff --git a/bundles/logging/log_admin/gtest/src/LogAdminTestSuite.cc b/bundles/logging/log_admin/gtest/src/LogAdminTestSuite.cc
index 56a9ce5..b385a19 100644
--- a/bundles/logging/log_admin/gtest/src/LogAdminTestSuite.cc
+++ b/bundles/logging/log_admin/gtest/src/LogAdminTestSuite.cc
@@ -34,6 +34,7 @@
auto* properties = celix_properties_create();
celix_properties_set(properties, "org.osgi.framework.storage", ".cacheLogBundleTestSuite");
+
auto* fwPtr = celix_frameworkFactory_createFramework(properties);
auto* ctxPtr = celix_framework_getFrameworkContext(fwPtr);
fw = std::shared_ptr<celix_framework_t>{fwPtr, [](celix_framework_t* f) {celix_frameworkFactory_destroyFramework(f);}};
@@ -120,6 +121,10 @@
EXPECT_EQ(0, control->nrOfSinks(control->handle, nullptr));
celix_log_sink_t logSink;
+ logSink.handle = nullptr;
+ logSink.sinkLog = [](void */*handle*/, celix_log_level_e /*level*/, long /*logServiceId*/, const char* /*logServiceName*/, const char* /*file*/, const char* /*function*/, int /*line*/, const char */*format*/, va_list /*formatArgs*/) {
+ //nop
+ };
celix_service_registration_options_t opts{};
opts.serviceName = CELIX_LOG_SINK_NAME;
opts.serviceVersion = CELIX_LOG_SINK_VERSION;
@@ -147,6 +152,11 @@
TEST_F(LogBundleTestSuite, SinkLogControl) {
celix_log_sink_t logSink;
+ logSink.handle = nullptr;
+ logSink.sinkLog = [](void */*handle*/, celix_log_level_e /*level*/, long /*logServiceId*/, const char* /*logServiceName*/, const char* /*file*/, const char* /*function*/, int /*line*/, const char */*format*/, va_list /*formatArgs*/) {
+ //nop
+ };
+
celix_service_registration_options_t opts{};
opts.serviceName = CELIX_LOG_SINK_NAME;
opts.serviceVersion = CELIX_LOG_SINK_VERSION;
@@ -214,6 +224,7 @@
TEST_F(LogBundleTestSuite, LogServiceControl) {
//request "default" log service
long trkId1 = celix_bundleContext_trackService(ctx.get(), CELIX_LOG_SERVICE_NAME, NULL, NULL);
+ celix_framework_waitForEmptyEventQueue(fw.get());
EXPECT_EQ(2, control->nrOfLogServices(control->handle, nullptr));
//request a 'logger1' log service
@@ -221,10 +232,12 @@
opts.filter.serviceName = CELIX_LOG_SERVICE_NAME;
opts.filter.filter = "(name=test::group::Log1)";
long trkId2 = celix_bundleContext_trackServicesWithOptions(ctx.get(), &opts);
+ celix_framework_waitForEmptyEventQueue(fw.get());
EXPECT_EQ(3, control->nrOfLogServices(control->handle, nullptr));
opts.filter.filter = "(name=test::group::Log2)";
long trkId3 = celix_bundleContext_trackServicesWithOptions(ctx.get(), &opts);
+ celix_framework_waitForEmptyEventQueue(fw.get());
EXPECT_EQ(4, control->nrOfLogServices(control->handle, nullptr));
EXPECT_EQ(2, control->nrOfLogServices(control->handle, "test::group"));
@@ -267,7 +280,9 @@
EXPECT_GE(level, CELIX_LOG_LEVEL_TRACE);
EXPECT_GE(logServiceId, 0);
- EXPECT_STREQ("test::Log1", logServiceName);
+ if (level == CELIX_LOG_LEVEL_FATAL) {
+ EXPECT_STREQ("test::Log1", logServiceName);
+ }
vfprintf(stdout, format, formatArgs);
@@ -276,6 +291,11 @@
TEST_F(LogBundleTestSuite, LogServiceAndSink) {
celix_log_sink_t logSink;
+ logSink.handle = nullptr;
+ logSink.sinkLog = [](void */*handle*/, celix_log_level_e /*level*/, long /*logServiceId*/, const char* /*logServiceName*/, const char* /*file*/, const char* /*function*/, int /*line*/, const char */*format*/, va_list /*formatArgs*/) {
+ //nop
+ };
+
std::atomic<size_t> count{0};
logSink.handle = (void*)&count;
logSink.sinkLog = logSinkFunction;
@@ -307,31 +327,32 @@
};
trkId = celix_bundleContext_trackServicesWithOptions(ctx.get(), &opts);
}
+ celix_framework_waitForEmptyEventQueue(fw.get());
ASSERT_TRUE(logSvc.load() != nullptr);
- EXPECT_EQ(0, count.load());
+ auto initial = count.load();
celix_log_service_t *ls = logSvc.load();
ls->info(ls->handle, "test %i %i %i", 1, 2, 3); //active log level
- EXPECT_EQ(1, count.load());
+ EXPECT_EQ(initial +1, count.load());
ls->debug(ls->handle, "test %i %i %i", 1, 2, 3); //note not a active log level
- EXPECT_EQ(1, count.load());
+ EXPECT_EQ(initial +1, count.load());
control->setActiveLogLevels(control->handle, "test::Log1", CELIX_LOG_LEVEL_DEBUG);
ls->debug(ls->handle, "test %i %i %i", 1, 2, 3); //active log level
- EXPECT_EQ(2, count.load());
+ EXPECT_EQ(initial +2, count.load());
control->setActiveLogLevels(control->handle, "test::Log1", CELIX_LOG_LEVEL_DISABLED);
ls->debug(ls->handle, "test %i %i %i", 1, 2, 3); //log service disable
- EXPECT_EQ(2, count.load());
+ EXPECT_EQ(initial +2, count.load());
control->setActiveLogLevels(control->handle, "test::Log1", CELIX_LOG_LEVEL_TRACE);
control->setSinkEnabled(control->handle, "test::Sink1", false);
ls->debug(ls->handle, "test %i %i %i", 1, 2, 3); //active log level and enabled log service, but sink disabled.
- EXPECT_EQ(2, count.load());
+ EXPECT_EQ(initial +2, count.load());
control->setSinkEnabled(control->handle, "test::Sink1", true);
ls->debug(ls->handle, "test %i %i %i", 1, 2, 3); //all enabled and active again
- EXPECT_EQ(3, count.load());
+ EXPECT_EQ(initial +3, count.load());
ls->trace(ls->handle, "test %i %i %i", 1, 2, 3); //+1
ls->debug(ls->handle, "test %i %i %i", 1, 2, 3); //+1
@@ -341,18 +362,23 @@
ls->fatal(ls->handle, "test %i %i %i", 1, 2, 3); //+1
ls->log(ls->handle, CELIX_LOG_LEVEL_ERROR, "error"); //+1
ls->logDetails(ls->handle, CELIX_LOG_LEVEL_ERROR, __FILE__, __FUNCTION__, __LINE__, "error"); //+1
- EXPECT_EQ(11, count.load());
+ EXPECT_EQ(initial +11, count.load());
celix_bundleContext_unregisterService(ctx.get(), svcId); //no log sink anymore
ls->fatal(ls->handle, "test %i %i %i", 1, 2, 3); //+0 (no log to sink, fallback to stdout)
- EXPECT_EQ(11, count.load());
+ EXPECT_EQ(initial +11, count.load());
celix_bundleContext_stopTracker(ctx.get(), trkId);
}
TEST_F(LogBundleTestSuite, LogAdminCmd) {
celix_log_sink_t logSink;
+ logSink.handle = nullptr;
+ logSink.sinkLog = [](void */*handle*/, celix_log_level_e /*level*/, long /*logServiceId*/, const char* /*logServiceName*/, const char* /*file*/, const char* /*function*/, int /*line*/, const char */*format*/, va_list /*formatArgs*/) {
+ //nop
+ };
+
long svcId;
{
auto *svcProps = celix_properties_create();
diff --git a/bundles/logging/log_admin/src/celix_log_admin.c b/bundles/logging/log_admin/src/celix_log_admin.c
index 26849c2..b048cb7 100644
--- a/bundles/logging/log_admin/src/celix_log_admin.c
+++ b/bundles/logging/log_admin/src/celix_log_admin.c
@@ -195,6 +195,24 @@
newEntry->logSvc.vlog = celix_logAdmin_vlog;
newEntry->logSvc.vlogDetails = celix_logAdmin_vlogDetails;
hashMap_put(admin->loggers, (void*)newEntry->name, newEntry);
+ celixThreadRwlock_unlock(&admin->lock);
+
+ {
+ //register new log service async
+ celix_properties_t* props = celix_properties_create();
+ celix_properties_set(props, CELIX_LOG_SERVICE_PROPERTY_NAME, newEntry->name);
+ if (celix_utils_stringEquals(newEntry->name, CELIX_LOG_ADMIN_DEFAULT_LOG_NAME) == 0) {
+ //ensure that the default log service is found when no name filter is used.
+ celix_properties_setLong(props, OSGI_FRAMEWORK_SERVICE_RANKING, 100);
+ }
+
+ celix_service_registration_options_t opts = CELIX_EMPTY_SERVICE_REGISTRATION_OPTIONS;
+ opts.serviceName = CELIX_LOG_SERVICE_NAME;
+ opts.serviceVersion = CELIX_LOG_SERVICE_VERSION;
+ opts.properties = props;
+ opts.svc = &newEntry->logSvc;
+ newEntry->logSvcId = celix_bundleContext_registerServiceWithOptionsAsync(admin->ctx, &opts);
+ }
if (celix_utils_stringEquals(newEntry->name, CELIX_LOG_ADMIN_FRAMEWORK_LOG_NAME)) {
celix_framework_t* fw = celix_bundleContext_getFramework(admin->ctx);
@@ -202,24 +220,7 @@
}
} else {
found->count += 1;
- }
- celixThreadRwlock_unlock(&admin->lock);
-
- if (newEntry != NULL) {
- //register new instance
- celix_properties_t* props = celix_properties_create();
- celix_properties_set(props, CELIX_LOG_SERVICE_PROPERTY_NAME, newEntry->name);
- if (celix_utils_stringEquals(newEntry->name, CELIX_LOG_ADMIN_DEFAULT_LOG_NAME) == 0) {
- //ensure that the default log service is found when no name filter is used.
- celix_properties_setLong(props, OSGI_FRAMEWORK_SERVICE_RANKING, 100);
- }
-
- celix_service_registration_options_t opts = CELIX_EMPTY_SERVICE_REGISTRATION_OPTIONS;
- opts.serviceName = CELIX_LOG_SERVICE_NAME;
- opts.serviceVersion = CELIX_LOG_SERVICE_VERSION;
- opts.properties = props;
- opts.svc = &newEntry->logSvc;
- newEntry->logSvcId = celix_bundleContext_registerServiceWithOptions(admin->ctx, &opts);
+ celixThreadRwlock_unlock(&admin->lock);
}
}
@@ -232,31 +233,28 @@
celix_logAdmin_addLogSvcForName(admin, name);
}
-static void celix_logAdmin_remLogSvcForName(celix_log_admin_t* admin, const char* name) {
- celix_log_service_entry_t* remEntry = NULL;
+static void celix_logAdmin_freeLogEntry(void *data) {
+ celix_log_service_entry_t* entry = data;
+ if (celix_utils_stringEquals(entry->name, CELIX_LOG_ADMIN_FRAMEWORK_LOG_NAME)) {
+ celix_framework_t* fw = celix_bundleContext_getFramework(entry->admin->ctx);
+ celix_framework_setLogCallback(fw, NULL, NULL);
+ }
+ free(entry->name);
+ free(entry);
+}
+static void celix_logAdmin_remLogSvcForName(celix_log_admin_t* admin, const char* name) {
celixThreadRwlock_writeLock(&admin->lock);
celix_log_service_entry_t* found = hashMap_get(admin->loggers, name);
if (found != NULL) {
found->count -= 1;
if (found->count == 0) {
//remove
- remEntry = found;
hashMap_remove(admin->loggers, name);
+ celix_bundleContext_unregisterServiceAsync(admin->ctx, found->logSvcId, found, celix_logAdmin_freeLogEntry);
}
}
celixThreadRwlock_unlock(&admin->lock);
-
- if (remEntry != NULL) {
- if (celix_utils_stringEquals(remEntry->name, CELIX_LOG_ADMIN_FRAMEWORK_LOG_NAME)) {
- celix_framework_t* fw = celix_bundleContext_getFramework(admin->ctx);
- celix_framework_setLogCallback(fw, NULL, NULL);
- }
-
- celix_bundleContext_unregisterService(admin->ctx, remEntry->logSvcId);
- free(remEntry->name);
- free(remEntry);
- }
}
@@ -311,7 +309,7 @@
celixThreadRwlock_writeLock(&admin->lock);
celix_log_sink_entry_t* entry = hashMap_get(admin->sinks, sinkName);
- if (entry->svcId != svcId) {
+ if (entry != NULL && entry->svcId != svcId) {
//no match (note there can be invalid log sinks with the same name, but different svc ids.
entry = NULL;
}
@@ -591,10 +589,10 @@
opts.callbackHandle = admin;
opts.addWithProperties = celix_logAdmin_addSink;
opts.removeWithProperties = celix_logAdmin_remSink;
- admin->logWriterTrackerId = celix_bundleContext_trackServicesWithOptions(ctx, &opts);
+ admin->logWriterTrackerId = celix_bundleContext_trackServicesWithOptionsAsync(ctx, &opts);
}
- admin->logServiceMetaTrackerId = celix_bundleContext_trackServiceTrackers(ctx, CELIX_LOG_SERVICE_NAME, admin, celix_logAdmin_trackerAdd, celix_logAdmin_trackerRem);
+ admin->logServiceMetaTrackerId = celix_bundleContext_trackServiceTrackersAsync(ctx, CELIX_LOG_SERVICE_NAME, admin, celix_logAdmin_trackerAdd, celix_logAdmin_trackerRem, NULL, NULL);
{
admin->controlSvc.handle = admin;
@@ -612,7 +610,7 @@
opts.serviceName = CELIX_LOG_CONTROL_NAME;
opts.serviceVersion = CELIX_LOG_CONTROL_VERSION;
opts.svc = &admin->controlSvc;
- admin->controlSvcId = celix_bundleContext_registerServiceWithOptions(ctx, &opts);
+ admin->controlSvcId = celix_bundleContext_registerServiceWithOptionsAsync(ctx, &opts);
}
{
@@ -629,12 +627,11 @@
opts.serviceVersion = CELIX_SHELL_COMMAND_SERVICE_VERSION;
opts.properties = props;
opts.svc = &admin->cmdSvc;
- admin->cmdSvcId = celix_bundleContext_registerServiceWithOptions(ctx, &opts);
+ admin->cmdSvcId = celix_bundleContext_registerServiceWithOptionsAsync(ctx, &opts);
}
//add log service for the framework
celix_logAdmin_addLogSvcForName(admin, CELIX_LOG_ADMIN_FRAMEWORK_LOG_NAME);
-
return admin;
}
@@ -642,10 +639,11 @@
if (admin != NULL) {
celix_logAdmin_remLogSvcForName(admin, CELIX_LOG_ADMIN_FRAMEWORK_LOG_NAME);
- celix_bundleContext_unregisterService(admin->ctx, admin->cmdSvcId);
- celix_bundleContext_unregisterService(admin->ctx, admin->controlSvcId);
- celix_bundleContext_stopTracker(admin->ctx, admin->logServiceMetaTrackerId);
- celix_bundleContext_stopTracker(admin->ctx, admin->logWriterTrackerId);
+ celix_bundleContext_unregisterServiceAsync(admin->ctx, admin->cmdSvcId, NULL, NULL);
+ celix_bundleContext_unregisterServiceAsync(admin->ctx, admin->controlSvcId, NULL, NULL);
+ celix_bundleContext_stopTrackerAsync(admin->ctx, admin->logServiceMetaTrackerId, NULL, NULL);
+ celix_bundleContext_stopTrackerAsync(admin->ctx, admin->logWriterTrackerId, NULL, NULL);
+ celix_bundleContext_waitForEvents(admin->ctx);
assert(hashMap_size(admin->loggers) == 0); //note stopping service tracker tracker should triggered all needed remove events
hashMap_destroy(admin->loggers, false, false);
diff --git a/bundles/pubsub/pubsub_admin_zmq/v2/src/pubsub_zmq_admin.c b/bundles/pubsub/pubsub_admin_zmq/v2/src/pubsub_zmq_admin.c
index 1434f8d..f6dc84b 100644
--- a/bundles/pubsub/pubsub_admin_zmq/v2/src/pubsub_zmq_admin.c
+++ b/bundles/pubsub/pubsub_admin_zmq/v2/src/pubsub_zmq_admin.c
@@ -459,6 +459,12 @@
return status;
}
+static void pubsub_zmqAdmin_getSerType(void *handle, void *svc __attribute__((unused)), const celix_properties_t* props) {
+ const char** out = handle;
+ *out = celix_properties_get(props, PUBSUB_MESSAGE_SERIALIZATION_SERVICE_SERIALIZATION_TYPE_PROPERTY, NULL);
+}
+
+
celix_status_t pubsub_zmqAdmin_setupTopicSender(void *handle, const char *scope, const char *topic, const celix_properties_t *topicProperties, long serializerSvcId __attribute__((unused)), long protocolSvcId, celix_properties_t **outPublisherEndpoint) {
pubsub_zmq_admin_t *psa = handle;
celix_status_t status = CELIX_SUCCESS;
@@ -475,7 +481,17 @@
staticBindUrl = celix_properties_get(topicProperties, PUBSUB_ZMQ_STATIC_BIND_URL, NULL);
}
char *key = pubsubEndpoint_createScopeTopicKey(scope, topic);
- const char *serType = celix_properties_get(topicProperties, PUBSUB_SERIALIZER_TYPE_KEY, NULL);
+
+ //get serializer type
+ const char *serType = NULL;
+ celix_service_use_options_t opts = CELIX_EMPTY_SERVICE_USE_OPTIONS;
+ opts.callbackHandle = &serType;
+ opts.useWithProperties = pubsub_zmqAdmin_getSerType;
+ opts.filter.serviceName = PUBSUB_MESSAGE_SERIALIZATION_SERVICE_NAME;
+ char filter[32];
+ snprintf(filter, 32, "(%s=%li)", OSGI_FRAMEWORK_SERVICE_ID, serializerSvcId);
+ opts.filter.filter = filter;
+ celix_bundleContext_useServiceWithOptions(psa->ctx, &opts);
celixThreadMutex_lock(&psa->protocols.mutex);
celixThreadMutex_lock(&psa->topicSenders.mutex);
@@ -570,7 +586,17 @@
celix_properties_t *newEndpoint = NULL;
char *key = pubsubEndpoint_createScopeTopicKey(scope, topic);
- const char *serType = celix_properties_get(topicProperties, PUBSUB_SERIALIZER_TYPE_KEY, NULL);
+
+ //get serializer type
+ const char *serType = NULL;
+ celix_service_use_options_t opts = CELIX_EMPTY_SERVICE_USE_OPTIONS;
+ opts.callbackHandle = &serType;
+ opts.useWithProperties = pubsub_zmqAdmin_getSerType;
+ opts.filter.serviceName = PUBSUB_MESSAGE_SERIALIZATION_SERVICE_NAME;
+ char filter[32];
+ snprintf(filter, 32, "(%s=%li)", OSGI_FRAMEWORK_SERVICE_ID, serializerSvcId);
+ opts.filter.filter = filter;
+ celix_bundleContext_useServiceWithOptions(psa->ctx, &opts);
celixThreadMutex_lock(&psa->protocols.mutex);
celixThreadMutex_lock(&psa->topicReceivers.mutex);
diff --git a/bundles/pubsub/pubsub_spi/src/pubsub_endpoint.c b/bundles/pubsub/pubsub_spi/src/pubsub_endpoint.c
index a7e9386..9d1ffe9 100644
--- a/bundles/pubsub/pubsub_spi/src/pubsub_endpoint.c
+++ b/bundles/pubsub/pubsub_spi/src/pubsub_endpoint.c
@@ -170,10 +170,8 @@
data.topic = topic;
celix_bundleContext_useBundle(ctx, bundleId, &data, retrieveTopicProperties);
- if (data.props != NULL) {
- pubsubEndpoint_setFields(ep, fwUUID, scope, topic, PUBSUB_PUBLISHER_ENDPOINT_TYPE, NULL, NULL, NULL, data.props);
- celix_properties_destroy(data.props); //safe to delete, properties are copied in pubsubEndpoint_setFields
- }
+ pubsubEndpoint_setFields(ep, fwUUID, scope, topic, PUBSUB_PUBLISHER_ENDPOINT_TYPE, NULL, NULL, NULL, data.props);
+ celix_properties_destroy(data.props); //safe to delete, properties are copied in pubsubEndpoint_setFields
if (!pubsubEndpoint_isValid(ep, false, false)) {
celix_properties_destroy(ep);
diff --git a/bundles/pubsub/pubsub_spi/src/pubsub_endpoint_match.c b/bundles/pubsub/pubsub_spi/src/pubsub_endpoint_match.c
index ef99ff5..92ebe69 100644
--- a/bundles/pubsub/pubsub_spi/src/pubsub_endpoint_match.c
+++ b/bundles/pubsub/pubsub_spi/src/pubsub_endpoint_match.c
@@ -56,38 +56,21 @@
return score;
}
-struct psa_serializer_selection_data {
- const char *requested_serializer;
- long matchingSvcId;
-};
-
-void psa_serializer_selection_callback(void *handle, void *svc __attribute__((unused)), const celix_properties_t *props) {
- struct psa_serializer_selection_data *data = handle;
- const char *serType = celix_properties_get(props, PUBSUB_SERIALIZER_TYPE_KEY, NULL);
- if (serType == NULL) {
- fprintf(stderr, "Warning found serializer without mandatory serializer type key (%s)\n", PUBSUB_SERIALIZER_TYPE_KEY);
- } else {
- if (strncmp(data->requested_serializer, serType, 1024 * 1024) == 0) {
- data->matchingSvcId = celix_properties_getAsLong(props, OSGI_FRAMEWORK_SERVICE_ID, -1L);
- }
- }
-}
-
static long getPSASerializer(celix_bundle_context_t *ctx, const char *requested_serializer) {
- long svcId;
+ long svcId = -1L;
if (requested_serializer != NULL) {
- struct psa_serializer_selection_data data;
- data.requested_serializer = requested_serializer;
- data.matchingSvcId = -1L;
+ char filter[512];
+ snprintf(filter, 512, "(%s=%s)", PUBSUB_SERIALIZER_TYPE_KEY, requested_serializer);
- celix_service_use_options_t opts = CELIX_EMPTY_SERVICE_USE_OPTIONS;
- opts.filter.serviceName = PUBSUB_SERIALIZER_SERVICE_NAME;
- opts.filter.ignoreServiceLanguage = true;
- opts.callbackHandle = &data;
- opts.useWithProperties = psa_serializer_selection_callback;
- celix_bundleContext_useServicesWithOptions(ctx, &opts);
- svcId = data.matchingSvcId;
+ celix_service_filter_options_t opts = CELIX_EMPTY_SERVICE_FILTER_OPTIONS;
+ opts.serviceName = PUBSUB_SERIALIZER_SERVICE_NAME;
+ opts.filter = filter;
+
+ svcId = celix_bundleContext_findServiceWithOptions(ctx, &opts);
+ if (svcId == -1) {
+ fprintf(stderr, "Warning cannot find serializer with requested serializer type '%s'\n", requested_serializer);
+ }
} else {
celix_service_filter_options_t opts = CELIX_EMPTY_SERVICE_FILTER_OPTIONS;
opts.serviceName = PUBSUB_SERIALIZER_SERVICE_NAME;
diff --git a/bundles/pubsub/pubsub_utils/src/pubsub_matching.c b/bundles/pubsub/pubsub_utils/src/pubsub_matching.c
index 48ca2ce..66193f9 100644
--- a/bundles/pubsub/pubsub_utils/src/pubsub_matching.c
+++ b/bundles/pubsub/pubsub_utils/src/pubsub_matching.c
@@ -33,51 +33,20 @@
celix_properties_t *outEndpoint;
} ps_utils_retrieve_topic_properties_data_t;
-void ps_utils_serializer_selection_callback(void *handle, void *svc __attribute__((unused)), const celix_properties_t *props) {
- struct ps_utils_serializer_selection_data *data = handle;
- const char *serType = celix_properties_get(props, PUBSUB_MESSAGE_SERIALIZATION_SERVICE_SERIALIZATION_TYPE_PROPERTY, NULL);
- long foundRanking = celix_properties_getAsLong(props, OSGI_FRAMEWORK_SERVICE_RANKING, -1);
- if (serType == NULL) {
- fprintf(stderr, "Warning found serializer without mandatory serializer type key (%s)\n", PUBSUB_MESSAGE_SERIALIZATION_SERVICE_SERIALIZATION_TYPE_PROPERTY);
- } else {
- if (strncmp(data->requested_serializer, serType, 1024 * 1024) == 0 && foundRanking > data->matchingRanking) {
- data->matchingRanking = foundRanking;
- data->matchingSvcId = celix_properties_getAsLong(props, OSGI_FRAMEWORK_SERVICE_ID, -1L);
- }
- }
-}
-
-
-
-void ps_protocol_selection_callback(void *handle, void *svc __attribute__((unused)), const celix_properties_t *props) {
- struct ps_utils_protocol_selection_data *data = handle;
- const char *serType = celix_properties_get(props, PUBSUB_PROTOCOL_TYPE_KEY, NULL);
- if (serType == NULL) {
- fprintf(stderr, "Warning found protocol without mandatory protocol type key (%s)\n", PUBSUB_PROTOCOL_TYPE_KEY);
- } else {
- if (strncmp(data->requested_protocol, serType, 1024 * 1024) == 0) {
- data->matchingSvcId = celix_properties_getAsLong(props, OSGI_FRAMEWORK_SERVICE_ID, -1L);
- }
- }
-}
-
-
static long getPSSerializer(celix_bundle_context_t *ctx, const char *requested_serializer) {
- long svcId;
+ long svcId = -1L;
if (requested_serializer != NULL) {
- struct ps_utils_serializer_selection_data data;
- data.requested_serializer = requested_serializer;
- data.matchingSvcId = -1L;
- data.matchingRanking = -2L;
-
- celix_service_use_options_t opts = CELIX_EMPTY_SERVICE_USE_OPTIONS;
- opts.filter.serviceName = PUBSUB_MESSAGE_SERIALIZATION_SERVICE_NAME;
- opts.filter.ignoreServiceLanguage = true;
- opts.callbackHandle = &data;
- opts.useWithProperties = ps_utils_serializer_selection_callback;
- celix_bundleContext_useServicesWithOptions(ctx, &opts);
- svcId = data.matchingSvcId;
+ char filter[512];
+ int written = snprintf(filter, 512, "(%s=%s)", PUBSUB_MESSAGE_SERIALIZATION_SERVICE_SERIALIZATION_TYPE_PROPERTY, requested_serializer);
+ if (written > 512) {
+ fprintf(stderr, "Cannot create serializer filter. need more than 512 char array\n");
+ } else {
+ celix_service_filter_options_t opts = CELIX_EMPTY_SERVICE_FILTER_OPTIONS;
+ opts.serviceName = PUBSUB_MESSAGE_SERIALIZATION_SERVICE_NAME;
+ opts.filter = filter;
+ svcId = celix_bundleContext_findServiceWithOptions(ctx, &opts);
+ }
} else {
celix_service_filter_options_t opts = CELIX_EMPTY_SERVICE_FILTER_OPTIONS;
opts.serviceName = PUBSUB_MESSAGE_SERIALIZATION_SERVICE_NAME;
@@ -118,20 +87,19 @@
}
static long getPSProtocol(celix_bundle_context_t *ctx, const char *requested_protocol) {
- long svcId;
+ long svcId = -1L;
if (requested_protocol != NULL) {
- struct ps_utils_protocol_selection_data data;
- data.requested_protocol = requested_protocol;
- data.matchingSvcId = -1L;
-
- celix_service_use_options_t opts = CELIX_EMPTY_SERVICE_USE_OPTIONS;
- opts.filter.serviceName = PUBSUB_PROTOCOL_SERVICE_NAME;
- opts.filter.ignoreServiceLanguage = true;
- opts.callbackHandle = &data;
- opts.useWithProperties = ps_protocol_selection_callback;
- celix_bundleContext_useServicesWithOptions(ctx, &opts);
- svcId = data.matchingSvcId;
+ char filter[512];
+ int written = snprintf(filter, 512, "(%s=%s)", PUBSUB_PROTOCOL_TYPE_KEY, requested_protocol);
+ if (written > 512) {
+ fprintf(stderr, "Cannot create protocol filter. need more than 512 char array\n");
+ } else {
+ celix_service_filter_options_t opts = CELIX_EMPTY_SERVICE_FILTER_OPTIONS;
+ opts.serviceName = PUBSUB_PROTOCOL_SERVICE_NAME;
+ opts.filter = filter;
+ svcId = celix_bundleContext_findServiceWithOptions(ctx, &opts);
+ }
} else {
celix_service_filter_options_t opts = CELIX_EMPTY_SERVICE_FILTER_OPTIONS;
opts.serviceName = PUBSUB_PROTOCOL_SERVICE_NAME;
diff --git a/bundles/remote_services/remote_service_admin_dfi/gtest/src/tst_activator.c b/bundles/remote_services/remote_service_admin_dfi/gtest/src/tst_activator.c
index e3a8000..0f1cbb9 100644
--- a/bundles/remote_services/remote_service_admin_dfi/gtest/src/tst_activator.c
+++ b/bundles/remote_services/remote_service_admin_dfi/gtest/src/tst_activator.c
@@ -315,6 +315,7 @@
act->testSvc.testCreateRemoteServiceInRemoteCall = testCreateRemoteServiceInRemoteCall;
act->testSvc.testCreateDestroyComponentWithRemoteService = bndTestCreateDestroyComponentWithRemoteService;
+ act->testSvc.testCreateRemoteServiceInRemoteCall = testCreateRemoteServiceInRemoteCall;
//create mutex
pthread_mutex_init(&act->mutex, NULL);
diff --git a/bundles/remote_services/topology_manager/tms_tst/disc_mock/disc_mock_activator.c b/bundles/remote_services/topology_manager/tms_tst/disc_mock/disc_mock_activator.c
index c34a67d..40e26f3 100644
--- a/bundles/remote_services/topology_manager/tms_tst/disc_mock/disc_mock_activator.c
+++ b/bundles/remote_services/topology_manager/tms_tst/disc_mock/disc_mock_activator.c
@@ -112,6 +112,7 @@
celix_status_t status;
struct disc_mock_activator * act = userData;
+ serviceRegistration_unregister(act->endpointListenerService);
status = serviceRegistration_unregister(act->reg);
return status;
diff --git a/bundles/shell/shell/src/help_command.c b/bundles/shell/shell/src/help_command.c
index fa124bb..a2ad219 100644
--- a/bundles/shell/shell/src/help_command.c
+++ b/bundles/shell/shell/src/help_command.c
@@ -57,6 +57,7 @@
for (i = 0; i < arrayList_size(commands); i++) {
char *name = arrayList_get(commands, i);
fprintf(out, "%s\n", name);
+ free(name);
}
fprintf(out, "\nUse 'help <command-name>' for more information.\n");
celix_arrayList_destroy(commands);
diff --git a/bundles/shell/shell/src/shell.c b/bundles/shell/shell/src/shell.c
index d3fb27d..d4f063b 100644
--- a/bundles/shell/shell/src/shell.c
+++ b/bundles/shell/shell/src/shell.c
@@ -33,11 +33,8 @@
shell->ctx = ctx;
shell->logHelper = celix_logHelper_create(ctx, "celix_shell");
-
- celix_thread_mutexattr_t attr;
- celixThreadMutexAttr_create(&attr);
- celixThreadMutexAttr_settype(&attr, CELIX_THREAD_MUTEX_RECURSIVE); //NOTE recursive, because command can also use the shell service
- celixThreadMutex_create(&shell->mutex, &attr);
+
+ celixThreadRwlock_create(&shell->lock, NULL);
shell->commandServices = hashMap_create(utils_stringHash, NULL, utils_stringEquals, NULL);
shell->legacyCommandServices = hashMap_create(utils_stringHash, NULL, utils_stringEquals, NULL);
@@ -46,9 +43,11 @@
void shell_destroy(shell_t *shell) {
if (shell != NULL) {
- celixThreadMutex_destroy(&shell->mutex);
+ celixThreadRwlock_writeLock(&shell->lock);
hashMap_destroy(shell->commandServices, false, false);
hashMap_destroy(shell->legacyCommandServices, false, false);
+ celixThreadRwlock_unlock(&shell->lock);
+ celixThreadRwlock_destroy(&shell->lock);
celix_logHelper_destroy(shell->logHelper);
free(shell);
}
@@ -64,7 +63,7 @@
status = CELIX_BUNDLE_EXCEPTION;
} else {
long svcId = celix_properties_getAsLong(props, OSGI_FRAMEWORK_SERVICE_ID, -1L);
- celixThreadMutex_lock(&shell->mutex);
+ celixThreadRwlock_writeLock(&shell->lock);
if (hashMap_containsKey(shell->commandServices, name)) {
celix_logHelper_log(shell->logHelper, CELIX_LOG_LEVEL_WARNING, "Command with name %s already registered!", name);
} else {
@@ -79,7 +78,7 @@
entry->localName = localName;
hashMap_put(shell->commandServices, (void*)name, entry);
}
- celixThreadMutex_unlock(&shell->mutex);
+ celixThreadRwlock_unlock(&shell->lock);
}
return status;
@@ -94,7 +93,7 @@
status = CELIX_BUNDLE_EXCEPTION;
} else {
long svcId = celix_properties_getAsLong(props, OSGI_FRAMEWORK_SERVICE_ID, -1L);
- celixThreadMutex_lock(&shell->mutex);
+ celixThreadRwlock_writeLock(&shell->lock);
if (hashMap_containsKey(shell->commandServices, name)) {
celix_shell_command_entry_t *entry = hashMap_get(shell->commandServices, name);
if (entry->svcId == svcId) {
@@ -108,7 +107,7 @@
} else {
celix_logHelper_log(shell->logHelper, CELIX_LOG_LEVEL_WARNING, "Cannot find shell command with name %s!", name);
}
- celixThreadMutex_unlock(&shell->mutex);
+ celixThreadRwlock_unlock(&shell->lock);
}
return status;
@@ -124,7 +123,7 @@
status = CELIX_BUNDLE_EXCEPTION;
} else {
long svcId = celix_properties_getAsLong(props, OSGI_FRAMEWORK_SERVICE_ID, -1L);
- celixThreadMutex_lock(&shell->mutex);
+ celixThreadRwlock_writeLock(&shell->lock);
if (hashMap_containsKey(shell->legacyCommandServices, name)) {
celix_logHelper_log(shell->logHelper, CELIX_LOG_LEVEL_WARNING, "Command with name %s already registered!", name);
} else {
@@ -134,7 +133,7 @@
entry->props = props;
hashMap_put(shell->legacyCommandServices, (void*)name, entry);
}
- celixThreadMutex_unlock(&shell->mutex);
+ celixThreadRwlock_unlock(&shell->lock);
}
return status;
@@ -151,7 +150,7 @@
status = CELIX_BUNDLE_EXCEPTION;
} else {
long svcId = celix_properties_getAsLong(props, OSGI_FRAMEWORK_SERVICE_ID, -1L);
- celixThreadMutex_lock(&shell->mutex);
+ celixThreadRwlock_writeLock(&shell->lock);
if (hashMap_containsKey(shell->legacyCommandServices, name)) {
celix_legacy_command_entry_t *entry = hashMap_get(shell->legacyCommandServices, name);
if (entry->svcId == svcId) {
@@ -163,7 +162,7 @@
} else {
celix_logHelper_log(shell->logHelper, CELIX_LOG_LEVEL_WARNING, "Cannot find shell command with name %s!", name);
}
- celixThreadMutex_unlock(&shell->mutex);
+ celixThreadRwlock_unlock(&shell->lock);
}
return status;
@@ -174,7 +173,7 @@
celix_status_t status = CELIX_SUCCESS;
celix_array_list_t *result = celix_arrayList_create();
- celixThreadMutex_lock(&shell->mutex);
+ celixThreadRwlock_readLock(&shell->lock);
hash_map_iterator_t iter = hashMapIterator_construct(shell->commandServices);
while (hashMapIterator_hasNext(&iter)) {
const char *name = hashMapIterator_nextKey(&iter);
@@ -186,7 +185,7 @@
const char *name = hashMapIterator_nextKey(&iter);
celix_arrayList_add(result, strndup(name, 1024*1024*10));
}
- celixThreadMutex_unlock(&shell->mutex);
+ celixThreadRwlock_unlock(&shell->lock);
*outCommands = result;
@@ -197,7 +196,7 @@
celix_status_t shell_getCommandUsage(shell_t *shell, const char *commandName, char **outUsage) {
celix_status_t status = CELIX_SUCCESS;
- celixThreadMutex_lock(&shell->mutex);
+ celixThreadRwlock_readLock(&shell->lock);
celix_shell_command_entry_t *entry = hashMap_get(shell->commandServices, commandName);
celix_legacy_command_entry_t *legacyEntry = hashMap_get(shell->legacyCommandServices, commandName);
if (entry != NULL) {
@@ -209,7 +208,7 @@
} else {
*outUsage = NULL;
}
- celixThreadMutex_unlock(&shell->mutex);
+ celixThreadRwlock_unlock(&shell->lock);
return status;
}
@@ -217,7 +216,7 @@
celix_status_t shell_getCommandDescription(shell_t *shell, const char *commandName, char **outDescription) {
celix_status_t status = CELIX_SUCCESS;
- celixThreadMutex_lock(&shell->mutex);
+ celixThreadRwlock_readLock(&shell->lock);
celix_shell_command_entry_t *entry = hashMap_get(shell->commandServices, commandName);
celix_legacy_command_entry_t *legacyEntry = hashMap_get(shell->legacyCommandServices, commandName);
if (entry != NULL) {
@@ -229,7 +228,7 @@
} else {
*outDescription = NULL;
}
- celixThreadMutex_unlock(&shell->mutex);
+ celixThreadRwlock_unlock(&shell->lock);
return status;
}
@@ -270,7 +269,7 @@
char *commandName = (pos != strlen(commandLine)) ? strndup(commandLine, pos) : strdup(commandLine);
- celixThreadMutex_lock(&shell->mutex);
+ celixThreadRwlock_readLock(&shell->lock);
celix_shell_command_entry_t *entry = shell_findEntry(shell, commandName, err);
celix_legacy_command_entry_t *legacyEntry = hashMap_get(shell->legacyCommandServices, commandName);
if (entry != NULL) {
@@ -283,7 +282,7 @@
fprintf(err, "No command '%s'. Provided command line: %s\n", commandName, commandLine);
status = CELIX_BUNDLE_EXCEPTION;
}
- celixThreadMutex_unlock(&shell->mutex);
+ celixThreadRwlock_unlock(&shell->lock);
free(commandName);
return status;
diff --git a/bundles/shell/shell/src/shell_private.h b/bundles/shell/shell/src/shell_private.h
index ad602bc..4afa972 100644
--- a/bundles/shell/shell/src/shell_private.h
+++ b/bundles/shell/shell/src/shell_private.h
@@ -63,7 +63,7 @@
struct shell {
celix_bundle_context_t *ctx;
celix_log_helper_t *logHelper;
- celix_thread_mutex_t mutex; //protects below
+ celix_thread_rwlock_t lock; //protects below
hash_map_t *commandServices; //key = char* (fully qualified command name), value = celix_shell_command_entry_t*
hash_map_t *legacyCommandServices; //key = char* (command name), value = celix_legacy_command_entry_t*
};
diff --git a/bundles/shell/shell/test/CMakeLists.txt b/bundles/shell/shell/test/CMakeLists.txt
index 4ae6f29..ebf4d1e 100644
--- a/bundles/shell/shell/test/CMakeLists.txt
+++ b/bundles/shell/shell/test/CMakeLists.txt
@@ -18,12 +18,10 @@
find_package(CppUTest REQUIRED)
add_executable(test_shell
- src/run_tests.cpp
- src/shell_tests.cpp
+ src/ShellTestSuite.cpp
)
-target_link_libraries(test_shell PRIVATE Celix::framework CURL::libcurl ${CppUTest_LIBRARY} Celix::shell_api)
-target_include_directories(test_shell SYSTEM PRIVATE ${CppUTest_INCLUDE_DIRS})
+target_link_libraries(test_shell PRIVATE Celix::framework CURL::libcurl Celix::shell_api GTest::gtest GTest::gtest_main)
add_dependencies(test_shell shell_bundle)
target_compile_definitions(test_shell PRIVATE -DSHELL_BUNDLE_LOCATION=\"$<TARGET_PROPERTY:shell,BUNDLE_FILE>\")
target_compile_options(test_shell PRIVATE -Wno-deprecated-declarations)
diff --git a/bundles/shell/shell/test/src/shell_tests.cpp b/bundles/shell/shell/test/src/ShellTestSuite.cpp
similarity index 60%
rename from bundles/shell/shell/test/src/shell_tests.cpp
rename to bundles/shell/shell/test/src/ShellTestSuite.cpp
index c0e09f4..bdf4fa6 100644
--- a/bundles/shell/shell/test/src/shell_tests.cpp
+++ b/bundles/shell/shell/test/src/ShellTestSuite.cpp
@@ -17,55 +17,45 @@
* under the License.
*/
+#include <gtest/gtest.h>
#include "celix_shell_command.h"
#include "celix_api.h"
#include "celix_shell.h"
-#include <CppUTest/TestHarness.h>
-#include <CppUTest/CommandLineTestRunner.h>
+class ShellTestSuite : public ::testing::Test {
+public:
+ static constexpr const char * const SHELL_BUNDLE_LOC = "" SHELL_BUNDLE_LOCATION "";
-#ifdef SHELL_BUNDLE_LOCATION
-const char * const SHELL_BUNDLE = SHELL_BUNDLE_LOCATION;
-#endif
+ ShellTestSuite() : ctx{createFrameworkContext()} {}
-#ifdef __APPLE__
-#include "memstream/open_memstream.h"
-#else
-#include <stdio.h>
-#endif
+ static std::shared_ptr<celix_bundle_context_t> createFrameworkContext() {
+ auto properties = properties_create();
+ properties_set(properties, "LOGHELPER_ENABLE_STDOUT_FALLBACK", "true");
+ properties_set(properties, "org.osgi.framework.storage", ".cacheShellTestSuite");
+ auto* cFw = celix_frameworkFactory_createFramework(properties);
+ auto cCtx = framework_getContext(cFw);
-TEST_GROUP(CelixShellTests) {
- framework_t* fw = nullptr;
- bundle_context_t *ctx = nullptr;
- celix_properties_t *properties = nullptr;
+ long shellBundleId = celix_bundleContext_installBundle(cCtx, SHELL_BUNDLE_LOCATION, true);
+ EXPECT_GE(shellBundleId, 0);
- void setup() {
- properties = celix_properties_create();
- celix_properties_set(properties, "LOGHELPER_ENABLE_STDOUT_FALLBACK", "true");
- celix_properties_set(properties, "org.osgi.framework.storage.clean", "onFirstInit");
- celix_properties_set(properties, "org.osgi.framework.storage", ".cacheBundleContextTestFramework");
-
- fw = celix_frameworkFactory_createFramework(properties);
- ctx = framework_getContext(fw);
-
- long bndId = celix_bundleContext_installBundle(ctx, SHELL_BUNDLE, true);
- CHECK_TRUE(bndId >= 0);
+ return std::shared_ptr<celix_bundle_context_t>{cCtx, [](celix_bundle_context_t* context) {
+ auto *fw = celix_bundleContext_getFramework(context);
+ celix_frameworkFactory_destroyFramework(fw);
+ }};
}
-
- void teardown() {
- celix_frameworkFactory_destroyFramework(fw);
- }
+
+ std::shared_ptr<celix_bundle_context_t> ctx;
};
-TEST(CelixShellTests, shellBundleInstalledTest) {
- auto *bndIds = celix_bundleContext_listBundles(ctx);
- CHECK_EQUAL(1, celix_arrayList_size(bndIds));
+TEST_F(ShellTestSuite, shellBundleInstalledTest) {
+ auto *bndIds = celix_bundleContext_listBundles(ctx.get());
+ EXPECT_EQ(1, celix_arrayList_size(bndIds));
celix_arrayList_destroy(bndIds);
}
-static void callCommand(celix_bundle_context_t *ctx, const char *cmdLine, bool cmdShouldSucceed) {
+static void callCommand(std::shared_ptr<celix_bundle_context_t>& ctx, const char *cmdLine, bool cmdShouldSucceed) {
celix_service_use_options_t opts{};
struct callback_data {
@@ -82,21 +72,22 @@
opts.use = [](void *handle, void *svc) {
auto *shell = static_cast<celix_shell_t *>(svc);
auto *d = static_cast<struct callback_data*>(handle);
- CHECK_TRUE(shell != nullptr);
+ EXPECT_TRUE(shell != nullptr);
celix_status_t status = shell->executeCommand(shell->handle, d->cmdLine, stdout, stderr);
if (d->cmdShouldSucceed) {
- CHECK_EQUAL_TEXT(CELIX_SUCCESS, status, d->cmdLine);
+ EXPECT_EQ(CELIX_SUCCESS, status);
} else {
- CHECK_TRUE_TEXT(status != CELIX_SUCCESS, d->cmdLine);
+ EXPECT_TRUE(status != CELIX_SUCCESS);
}
};
- bool called = celix_bundleContext_useServiceWithOptions(ctx, &opts);
- CHECK_TRUE(called);
+ bool called = celix_bundleContext_useServiceWithOptions(ctx.get(), &opts);
+ EXPECT_TRUE(called);
}
-TEST(CelixShellTests, testAllCommandsAreCallable) {
+TEST_F(ShellTestSuite, testAllCommandsAreCallable) {
callCommand(ctx, "non-existing", false);
callCommand(ctx, "install a-bundle-loc.zip", false);
+ callCommand(ctx, "help", true);
callCommand(ctx, "help lb", false); //note need namespace
callCommand(ctx, "help celix::lb", true);
callCommand(ctx, "help non-existing-command", false);
@@ -110,22 +101,22 @@
callCommand(ctx, "update 15", false);
}
-TEST(CelixShellTests, quitTest) {
+TEST_F(ShellTestSuite, quitTest) {
callCommand(ctx, "quit", true);
}
-TEST(CelixShellTests, stopFrameworkTest) {
+TEST_F(ShellTestSuite, stopFrameworkTest) {
callCommand(ctx, "stop 0", true);
}
-TEST(CelixShellTests, queryTest) {
+TEST_F(ShellTestSuite, queryTest) {
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) {
auto *command = static_cast<celix_shell_command_t*>(svc);
- CHECK_TRUE(command != nullptr);
+ EXPECT_TRUE(command != nullptr);
{
char *buf = nullptr;
@@ -133,8 +124,8 @@
FILE *sout = open_memstream(&buf, &len);
command->executeCommand(command->handle, (char *) "query", sout, sout);
fclose(sout);
- STRCMP_CONTAINS("Provided services found 1", buf); //note could be 11, 12, etc
- //STRCMP_CONTAINS("Requested services found 1", buf); //note very explicit, could be improved
+ char* found = strstr(buf, "Provided services found 1"); //note could be 11, 12, etc
+ EXPECT_TRUE(found != nullptr);
free(buf);
}
{
@@ -143,53 +134,54 @@
FILE *sout = open_memstream(&buf, &len);
command->executeCommand(command->handle, (char *) "query 0", sout, sout); //note query framework bundle -> no results
fclose(sout);
- STRCMP_CONTAINS("No results", buf); //note could be 11, 12, etc
+ char* found = strstr(buf, "No results"); //note could be 11, 12, etc
+ EXPECT_TRUE(found != nullptr);
free(buf);
}
};
- bool called = celix_bundleContext_useServiceWithOptions(ctx, &opts);
- CHECK_TRUE(called);
+ bool called = celix_bundleContext_useServiceWithOptions(ctx.get(), &opts);
+ EXPECT_TRUE(called);
}
-TEST(CelixShellTests, localNameClashTest) {
+TEST_F(ShellTestSuite, localNameClashTest) {
callCommand(ctx, "lb", true);
celix_shell_command_t cmdService;
cmdService.handle = nullptr;
cmdService.executeCommand = [](void *, const char* cmdLine, FILE *, FILE *) -> bool {
- CHECK_TRUE(cmdLine != NULL);
+ EXPECT_TRUE(cmdLine != nullptr);
return true;
};
celix_properties_t *props = celix_properties_create();
celix_properties_set(props, CELIX_SHELL_COMMAND_NAME, "3rdparty::lb");
- long svcId = celix_bundleContext_registerService(ctx, &cmdService, CELIX_SHELL_COMMAND_SERVICE_NAME, props);
+ long svcId = celix_bundleContext_registerService(ctx.get(), &cmdService, CELIX_SHELL_COMMAND_SERVICE_NAME, props);
//two lb commands, need namespace
callCommand(ctx, "lb", false);
callCommand(ctx, "celix::lb", true);
callCommand(ctx, "3rdparty::lb", true);
- celix_bundleContext_unregisterService(ctx, svcId);
+ celix_bundleContext_unregisterService(ctx.get(), svcId);
}
#ifdef CELIX_INSTALL_DEPRECATED_API
#include "command.h"
-TEST(CelixShellTests, legacyCommandTest) {
+TEST_F(ShellTestSuite, legacyCommandTest) {
command_service_t cmdService;
cmdService.handle = nullptr;
cmdService.executeCommand = [](void *, char* cmdLine, FILE *, FILE *) -> celix_status_t {
- CHECK_TRUE(cmdLine != NULL);
+ EXPECT_TRUE(cmdLine != nullptr);
return CELIX_SUCCESS;
};
celix_properties_t *props = celix_properties_create();
celix_properties_set(props, OSGI_SHELL_COMMAND_NAME, "testCommand");
- long svcId = celix_bundleContext_registerService(ctx, &cmdService, OSGI_SHELL_COMMAND_SERVICE_NAME, props);
+ long svcId = celix_bundleContext_registerService(ctx.get(), &cmdService, OSGI_SHELL_COMMAND_SERVICE_NAME, props);
callCommand(ctx, "testCommand", true);
- celix_bundleContext_unregisterService(ctx, svcId);
+ celix_bundleContext_unregisterService(ctx.get(), svcId);
}
#endif
\ No newline at end of file
diff --git a/bundles/shell/shell/test/src/run_tests.cpp b/bundles/shell/shell/test/src/run_tests.cpp
deleted file mode 100644
index efaee82..0000000
--- a/bundles/shell/shell/test/src/run_tests.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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.
- */
-
-#include <CppUTest/TestHarness.h>
-#include "CppUTest/CommandLineTestRunner.h"
-
-int main(int argc, char** argv) {
- MemoryLeakWarningPlugin::turnOffNewDeleteOverloads();
- return RUN_ALL_TESTS(argc, argv);
-}
\ No newline at end of file
diff --git a/libs/framework/CMakeLists.txt b/libs/framework/CMakeLists.txt
index 950de4e..5d4ea4a 100644
--- a/libs/framework/CMakeLists.txt
+++ b/libs/framework/CMakeLists.txt
@@ -45,10 +45,6 @@
target_link_libraries(framework PUBLIC Celix::utils Celix::dfi ${CELIX_OPTIONAL_EXTRA_LIBS})
target_link_libraries(framework PUBLIC UUID::lib CURL::libcurl ZLIB::ZLIB)
-#Note option to ensure celix uses separate shutdown thread for closing service trackers.
-#This can prevent deadlocks, but those deadlock are bugs which need to be solved instead of this approach.
-#target_compile_definitions(framework PRIVATE -DCELIX_SERVICE_TRACKER_USE_SHUTDOWN_THREAD)
-
install(TARGETS framework EXPORT celix DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT framework)
install(DIRECTORY include/ DESTINATION include/celix COMPONENT framework)
diff --git a/libs/framework/gtest/CMakeLists.txt b/libs/framework/gtest/CMakeLists.txt
index cfd605c..89f9eb0 100644
--- a/libs/framework/gtest/CMakeLists.txt
+++ b/libs/framework/gtest/CMakeLists.txt
@@ -32,7 +32,6 @@
add_dependencies(unresolveable_bundle sublib)
add_executable(test_framework
- src/run_tests.cpp
src/single_framework_test.cpp
src/multiple_frameworks_test.cpp
src/bundle_context_bundles_tests.cpp
@@ -40,7 +39,7 @@
src/DependencyManagerTestSuite.cc
)
-target_link_libraries(test_framework Celix::framework CURL::libcurl GTest::gtest)
+target_link_libraries(test_framework Celix::framework CURL::libcurl GTest::gtest GTest::gtest_main)
add_dependencies(test_framework simple_test_bundle1_bundle simple_test_bundle2_bundle simple_test_bundle3_bundle simple_test_bundle4_bundle simple_test_bundle5_bundle bundle_with_exception_bundle unresolveable_bundle_bundle)
target_include_directories(test_framework PRIVATE ../src)
diff --git a/libs/framework/gtest/src/bundle_context_bundles_tests.cpp b/libs/framework/gtest/src/bundle_context_bundles_tests.cpp
index 5a9253d..90f1d00 100644
--- a/libs/framework/gtest/src/bundle_context_bundles_tests.cpp
+++ b/libs/framework/gtest/src/bundle_context_bundles_tests.cpp
@@ -378,6 +378,90 @@
celix_bundleContext_stopTracker(ctx, trackerId);
};
+
+TEST_F(CelixBundleContextBundlesTests, trackBundlesTestAsync) {
+ struct data {
+ std::atomic<int> installedCount{0};
+ std::atomic<int> startedCount{0};
+ std::atomic<int> stoppedCount{0};
+ };
+ struct data data;
+
+ auto installed = [](void *handle, const bundle_t *bnd) {
+ auto *d = static_cast<struct data*>(handle);
+ EXPECT_TRUE(bnd != nullptr);
+ d->installedCount.fetch_add(1);
+ };
+
+ auto started = [](void *handle, const bundle_t *bnd) {
+ auto *d = static_cast<struct data*>(handle);
+ EXPECT_TRUE(bnd != nullptr);
+ d->startedCount.fetch_add(1);
+ };
+
+ auto stopped = [](void *handle, const bundle_t *bnd) {
+ auto *d = static_cast<struct data*>(handle);
+ if (bnd == nullptr) {
+ celix_logUtils_logToStdout("test", CELIX_LOG_LEVEL_ERROR, "bnd should not be null");
+ }
+ EXPECT_TRUE(bnd != nullptr);
+ d->stoppedCount.fetch_add(1);
+ };
+
+ celix_bundle_tracking_options_t opts{};
+ opts.callbackHandle = static_cast<void*>(&data);
+ opts.onInstalled = installed;
+ opts.onStarted = started;
+ opts.onStopped = stopped;
+
+ long bundleId1 = celix_bundleContext_installBundle(ctx, TEST_BND1_LOC, true);
+ celix_framework_waitForEmptyEventQueue(fw);
+ EXPECT_TRUE(bundleId1 >= 0);
+
+ /*
+ * NOTE for bundles already installed (TEST_BND1) the callbacks are called on the
+ * thread of celix_bundleContext_trackBundlesWithOptions.
+ * For Bundles installed after the celix_bundleContext_trackBundlesWithOptions function
+ * the called are called on the Celix framework event queue thread.
+ */
+ long trackerId = celix_bundleContext_trackBundlesWithOptionsAsync(ctx, &opts);
+ celix_bundleContext_waitForAsyncTracker(ctx, trackerId);
+ EXPECT_EQ(1, data.installedCount.load());
+ EXPECT_EQ(1, data.startedCount.load());
+ EXPECT_EQ(0, data.stoppedCount.load());
+
+
+ long bundleId2 = celix_bundleContext_installBundle(ctx, TEST_BND2_LOC, true);
+ celix_framework_waitForEmptyEventQueue(fw);
+ EXPECT_TRUE(bundleId2 >= 0);
+ EXPECT_EQ(2, data.installedCount.load());
+ EXPECT_EQ(2, data.startedCount.load());
+ EXPECT_EQ(0, data.stoppedCount.load());
+
+ celix_bundleContext_uninstallBundle(ctx, bundleId2);
+ celix_framework_waitForEmptyEventQueue(fw);
+ EXPECT_EQ(2, data.installedCount.load());
+ EXPECT_EQ(2, data.startedCount.load());
+ EXPECT_EQ(1, data.stoppedCount.load());
+
+ long bundleId3 = celix_bundleContext_installBundle(ctx, TEST_BND3_LOC, true);
+ celix_framework_waitForEmptyEventQueue(fw);
+ EXPECT_TRUE(bundleId3 >= 0);
+ EXPECT_EQ(3, data.installedCount.load());
+ EXPECT_EQ(3, data.startedCount.load());
+ EXPECT_EQ(1, data.stoppedCount.load());
+
+ bundleId2 = celix_bundleContext_installBundle(ctx, TEST_BND2_LOC, true);
+ celix_framework_waitForEmptyEventQueue(fw);
+ EXPECT_TRUE(bundleId2 >= 0);
+ EXPECT_EQ(4, data.installedCount.load());
+ EXPECT_EQ(4, data.startedCount.load());
+ EXPECT_EQ(1, data.stoppedCount.load());
+
+ celix_bundleContext_stopTrackerAsync(ctx, trackerId, nullptr, nullptr);
+ celix_bundleContext_waitForAsyncStopTracker(ctx, trackerId);
+};
+
TEST_F(CelixBundleContextBundlesTests, useBundlesConcurrentTest) {
struct data {
@@ -455,19 +539,40 @@
};
bool called = celix_bundleContext_useBundle(ctx, 0, &data, updateCountFp);
- ASSERT_TRUE(called);
- ASSERT_EQ(0, data.provideCount);
- ASSERT_EQ(0, data.requestedCount);
+ EXPECT_TRUE(called);
+ EXPECT_EQ(0, data.provideCount);
+ EXPECT_EQ(0, data.requestedCount);
long svcId = celix_bundleContext_registerService(ctx, (void*)0x42, "NopService", NULL);
long trackerId = celix_bundleContext_trackServices(ctx, "AService", NULL, NULL, NULL);
called = celix_bundleContext_useBundle(ctx, 0, &data, updateCountFp);
- ASSERT_TRUE(called);
- ASSERT_EQ(1, data.provideCount);
- ASSERT_EQ(1, data.requestedCount);
+ EXPECT_TRUE(called);
+ EXPECT_EQ(1, data.provideCount);
+ EXPECT_EQ(1, data.requestedCount);
celix_bundleContext_unregisterService(ctx, svcId);
celix_bundleContext_stopTracker(ctx, trackerId);
}
+
+TEST_F(CelixBundleContextBundlesTests, startStopBundleTrackerAsync) {
+ std::atomic<int> count{0};
+
+ auto cb = [](void* data) {
+ auto* c = static_cast<std::atomic<int>*>(data);
+ (*c)++;
+ };
+
+ celix_bundle_tracking_options_t opts{};
+ opts.trackerCreatedCallbackData = &count;
+ opts.trackerCreatedCallback = cb;
+ long trkId = celix_bundleContext_trackBundlesWithOptionsAsync(ctx, &opts);
+ EXPECT_GE(trkId, 0);
+ celix_bundleContext_waitForAsyncTracker(ctx, trkId);
+ EXPECT_EQ(count.load(), 1); //1x tracker started
+
+ celix_bundleContext_stopTrackerAsync(ctx, trkId, &count, cb);
+ celix_bundleContext_waitForAsyncStopTracker(ctx, trkId);
+ EXPECT_EQ(2, count.load()); //1x tracker started, 1x tracker stopped
+}
\ No newline at end of file
diff --git a/libs/framework/gtest/src/bundle_context_services_test.cpp b/libs/framework/gtest/src/bundle_context_services_test.cpp
index e544d2f..b19fbe9 100644
--- a/libs/framework/gtest/src/bundle_context_services_test.cpp
+++ b/libs/framework/gtest/src/bundle_context_services_test.cpp
@@ -44,6 +44,7 @@
properties_set(properties, "LOGHELPER_ENABLE_STDOUT_FALLBACK", "true");
properties_set(properties, "org.osgi.framework.storage.clean", "onFirstInit");
properties_set(properties, "org.osgi.framework.storage", ".cacheBundleContextTestFramework");
+ properties_set(properties, "CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL", "trace");
fw = celix_frameworkFactory_createFramework(properties);
ctx = framework_getContext(fw);
@@ -75,6 +76,27 @@
celix_bundleContext_unregisterService(ctx, svcId);
};
+TEST_F(CelixBundleContextServicesTests, registerServiceAsync) {
+ struct calc {
+ int (*calc)(int);
+ };
+
+ const char *calcName = "calc";
+ calc svc;
+ svc.calc = [](int n) -> int {
+ return n * 42;
+ };
+
+ long svcId = celix_bundleContext_registerServiceAsync(ctx, &svc, calcName, nullptr);
+ ASSERT_TRUE(svcId >= 0);
+ celix_bundleContext_waitForAsyncRegistration(ctx, svcId);
+ ASSERT_GE(celix_bundleContext_findService(ctx, calcName), 0L);
+
+ celix_bundleContext_unregisterServiceAsync(ctx, svcId, NULL, NULL);
+ celix_bundleContext_waitForAsyncUnregistration(ctx, svcId);
+ ASSERT_LT(celix_bundleContext_findService(ctx, calcName), 0L);
+};
+
TEST_F(CelixBundleContextServicesTests, incorrectUnregisterCalls) {
celix_bundleContext_unregisterService(ctx, 1);
celix_bundleContext_unregisterService(ctx, 2);
@@ -82,6 +104,13 @@
celix_bundleContext_unregisterService(ctx, -2);
};
+TEST_F(CelixBundleContextServicesTests, incorrectAsyncUnregisterCalls) {
+ celix_bundleContext_unregisterServiceAsync(ctx, 1, NULL, NULL);
+ celix_bundleContext_unregisterServiceAsync(ctx, 2, NULL, NULL);
+ celix_bundleContext_unregisterServiceAsync(ctx, -1, NULL, NULL);
+ celix_bundleContext_unregisterServiceAsync(ctx, -2, NULL, NULL);
+};
+
TEST_F(CelixBundleContextServicesTests, registerMultipleAndUseServices) {
struct calc {
int (*calc)(int);
@@ -94,16 +123,16 @@
};
long svcId1 = celix_bundleContext_registerService(ctx, &svc, calcName, nullptr);
- ASSERT_TRUE(svcId1 >= 0);
+ EXPECT_TRUE(svcId1 >= 0);
long svcId2 = celix_bundleContext_registerService(ctx, &svc, calcName, nullptr);
- ASSERT_TRUE(svcId2 >= 0);
+ EXPECT_TRUE(svcId2 >= 0);
long svcId3 = celix_bundleContext_registerService(ctx, &svc, calcName, nullptr);
- ASSERT_TRUE(svcId3 >= 0);
+ EXPECT_TRUE(svcId3 >= 0);
auto use = [](void *handle, void *svc) {
- ASSERT_TRUE(svc != nullptr);
+ EXPECT_TRUE(svc != nullptr);
int *total = static_cast<int*>(handle);
struct calc *calc = static_cast<struct calc*>(svc);
int tmp = calc->calc(1);
@@ -112,20 +141,20 @@
int total = 0;
auto count = celix_bundleContext_useServices(ctx, "calc", &total, use);
- ASSERT_EQ(3, count);
- ASSERT_EQ(42 * 3, total);
+ EXPECT_EQ(3, count);
+ EXPECT_EQ(42 * 3, total);
celix_bundleContext_unregisterService(ctx, svcId3);
total = 0;
count = celix_bundleContext_useServices(ctx, "calc", &total, use);
- ASSERT_EQ(2, count);
- ASSERT_EQ(42 * 2, total);
+ EXPECT_EQ(2, count);
+ EXPECT_EQ(42 * 2, total);
total = 0;
bool called = celix_bundleContext_useService(ctx, "calc", &total, use);
- ASSERT_TRUE(called);
- ASSERT_EQ(42, total);
+ EXPECT_TRUE(called);
+ EXPECT_EQ(42, total);
celix_bundleContext_unregisterService(ctx, svcId1);
celix_bundleContext_unregisterService(ctx, svcId2);
@@ -223,6 +252,56 @@
}
}
+TEST_F(CelixBundleContextServicesTests, registerAsyncAndUseServiceWithTimeout) {
+ 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);
+ struct calc {
+ int (*calc)(int);
+ };
+
+ const char *calcName = "calc";
+ struct calc svc;
+ svc.calc = [](int n) -> int {
+ return n * 42;
+ };
+
+ celix_service_use_options_t opts{};
+ opts.filter.serviceName = "calc";
+
+ bool called = celix_bundleContext_useServiceWithOptions(ctx, &opts);
+ EXPECT_FALSE(called); //service not avail.
+
+ std::future<bool> result{std::async([&] {
+ opts.waitTimeoutInSeconds = 2.0;
+ //printf("Trying to call calc with timeout of %f\n", opts.waitTimeoutInSeconds);
+ bool calledAsync = celix_bundleContext_useServiceWithOptions(ctx, &opts);
+ //printf("returned from use service with timeout. calc called %s.\n", calledAsync ? "true" : "false");
+ return calledAsync;
+ })};
+ long svcId = celix_bundleContext_registerServiceAsync(ctx, &svc, calcName, nullptr);
+ EXPECT_TRUE(svcId >= 0);
+
+
+ EXPECT_TRUE(result.get()); //should return true after waiting for the registered service.
+
+
+ celix_bundleContext_unregisterServiceAsync(ctx, svcId, NULL, NULL);
+ celix_bundleContext_waitForAsyncUnregistration(ctx, svcId);
+
+
+ //check if timeout is triggered
+ std::future<bool> result2{std::async([&] {
+ opts.waitTimeoutInSeconds = 0.1;
+ //printf("Trying to call calc with timeout of %f\n", opts.waitTimeoutInSeconds);
+ bool calledAsync = celix_bundleContext_useServiceWithOptions(ctx, &opts);
+ //printf("returned from use service with timeout. calc called %s.\n", calledAsync ? "true" : "false");
+ return calledAsync;
+ })};
+ EXPECT_FALSE(result2.get()); //note service is away, so even with a wait the service is not found.
+ }
+}
+
TEST_F(CelixBundleContextServicesTests, registerAndUseServiceWithCorrectVersion) {
struct calc {
int (*calc)(int);
@@ -410,11 +489,46 @@
celix_bundleContext_unregisterService(ctx, svcId2);
}
+TEST_F(CelixBundleContextServicesTests, servicesTrackerTestAsync) {
+ std::atomic<int> count {0};
+ auto add = [](void *handle, void *svc) {
+ ASSERT_TRUE(svc != nullptr);
+ auto *c = static_cast<std::atomic<int>*>(handle);
+ *c += 1;
+ };
+ auto remove = [](void *handle, void *svc) {
+ ASSERT_TRUE(svc != nullptr);
+ auto *c = static_cast<std::atomic<int>*>(handle);
+ *c -= 1;
+ };
+
+ long trackerId = celix_bundleContext_trackServicesAsync(ctx, "calc", &count, add, remove);
+ ASSERT_TRUE(trackerId >= 0);
+ ASSERT_EQ(0, count);
+
+ long svcId1 = celix_bundleContext_registerServiceAsync(ctx, (void*)0x100, "calc", nullptr);
+ celix_bundleContext_waitForAsyncRegistration(ctx, svcId1); //note also means the service tracker is done
+ ASSERT_TRUE(svcId1 >= 0);
+ ASSERT_EQ(1, count);
+
+ long svcId2 = celix_bundleContext_registerServiceAsync(ctx, (void*)0x200, "calc", nullptr);
+ celix_bundleContext_waitForAsyncRegistration(ctx, svcId2);
+ ASSERT_TRUE(svcId2 >= 0);
+ ASSERT_EQ(2, count);
+
+ celix_bundleContext_unregisterServiceAsync(ctx, svcId1, NULL, NULL);
+ celix_bundleContext_waitForAsyncUnregistration(ctx, svcId1);
+ ASSERT_EQ(1, count);
+
+ celix_bundleContext_stopTrackerAsync(ctx, trackerId, NULL, NULL);
+ celix_bundleContext_unregisterServiceAsync(ctx, svcId2, NULL, NULL);
+
+ celix_framework_waitForEmptyEventQueue(fw);
+}
+
TEST_F(CelixBundleContextServicesTests, servicesTrackerInvalidArgsTest) {
long trackerId = celix_bundleContext_trackServices(nullptr, nullptr, nullptr, nullptr, nullptr);
- ASSERT_TRUE(trackerId < 0); //required ctx and service name missing
- trackerId = celix_bundleContext_trackServices(ctx, nullptr, nullptr, nullptr, nullptr);
- ASSERT_TRUE(trackerId < 0); //required service name missing
+ ASSERT_TRUE(trackerId < 0); //required ctx missing
trackerId = celix_bundleContext_trackServices(ctx, "calc", nullptr, nullptr, nullptr);
ASSERT_TRUE(trackerId >= 0); //valid
celix_bundleContext_stopTracker(ctx, trackerId);
@@ -427,7 +541,9 @@
ASSERT_TRUE(trackerId < 0); //required opts missing
celix_service_tracking_options_t opts{};
trackerId = celix_bundleContext_trackServicesWithOptions(ctx, &opts);
- ASSERT_TRUE(trackerId < 0); //required opts->serviceName missing
+ ASSERT_TRUE(trackerId >= 0); //valid with empty opts
+ celix_bundleContext_stopTracker(ctx, trackerId);
+
opts.filter.serviceName = "calc";
trackerId = celix_bundleContext_trackServicesWithOptions(ctx, &opts);
ASSERT_TRUE(trackerId >= 0); //valid
@@ -661,7 +777,6 @@
void *svc4 = (void*)0x400; //5 ranking
auto set = [](void *handle, void *svc) {
- ASSERT_TRUE(svc != nullptr);
static int callCount = 0;
callCount += 1;
if (callCount == 1) {
@@ -687,32 +802,86 @@
opts.callbackHandle = (void*)&count;
opts.filter.serviceName = "NA";
opts.set = set;
- long trackerId = celix_bundleContext_trackServicesWithOptions(ctx, &opts);
+ long trackerId = celix_bundleContext_trackServicesWithOptions(ctx, &opts); //call 1
ASSERT_TRUE(trackerId >= 0);
//register svc3 should lead to second set call
properties_t *props3 = celix_properties_create();
celix_properties_set(props3, OSGI_FRAMEWORK_SERVICE_RANKING, "10");
- long svcId3 = celix_bundleContext_registerService(ctx, svc3, "NA", props3);
+ long svcId3 = celix_bundleContext_registerService(ctx, svc3, "NA", props3); //call 2
//register svc4 should lead to no set (lower ranking)
properties_t *props4 = celix_properties_create();
celix_properties_set(props4, OSGI_FRAMEWORK_SERVICE_RANKING, "10");
- long svcId4 = celix_bundleContext_registerService(ctx, svc4, "NA", props4);
+ long svcId4 = celix_bundleContext_registerService(ctx, svc4, "NA", props4); //no update
//unregister svc3 should lead to set (new highest ranking)
- celix_bundleContext_unregisterService(ctx, svcId3);
+ celix_bundleContext_unregisterService(ctx, svcId3); //call 3
- celix_bundleContext_stopTracker(ctx, trackerId);
+ celix_bundleContext_stopTracker(ctx, trackerId); //call 4 (NULL)
celix_bundleContext_unregisterService(ctx, svcId1);
celix_bundleContext_unregisterService(ctx, svcId2);
celix_bundleContext_unregisterService(ctx, svcId4);
- ASSERT_EQ(3, count); //check if the set is called the expected times
+ ASSERT_EQ(4, count); //check if the set is called the expected times
}
-//TODO test tracker with options for properties & service owners
+TEST_F(CelixBundleContextServicesTests, trackAllServices) {
+ std::atomic<size_t> count{0};
+ void *svc1 = (void *) 0x100; //no ranking
+ void *svc2 = (void *) 0x200; //no ranking
+ void *svc3 = (void *) 0x300; //10 ranking
+ void *svc4 = (void *) 0x400; //5 ranking
+
+ long svcId1 = celix_bundleContext_registerService(ctx, svc1, "svc_type1", nullptr);
+ long svcId2 = celix_bundleContext_registerService(ctx, svc2, "svc_type1", nullptr);
+ long svcId3 = celix_bundleContext_registerService(ctx, svc3, "svc_type2", nullptr);
+ long svcId4 = celix_bundleContext_registerService(ctx, svc4, "svc_type2", nullptr);
+
+ celix_service_tracking_options_t opts{};
+ opts.callbackHandle = (void *) &count;
+ opts.filter.serviceName = nullptr;
+ opts.callbackHandle = (void *) &count;
+ opts.add = [](void *handle, void *) {
+ auto c = (std::atomic<size_t> *) handle;
+ c->fetch_add(1);
+ };
+ long trackerId = celix_bundleContext_trackServicesWithOptions(ctx, &opts);
+ EXPECT_GE(trackerId, 0);
+ EXPECT_EQ(4, count.load());
+
+ celix_bundleContext_unregisterService(ctx, svcId1);
+ celix_bundleContext_unregisterService(ctx, svcId2);
+ celix_bundleContext_unregisterService(ctx, svcId3);
+ celix_bundleContext_unregisterService(ctx, svcId4);
+ celix_bundleContext_stopTracker(ctx, trackerId);
+}
+
+TEST_F(CelixBundleContextServicesTests, metaTrackAllServiceTrackers) {
+ std::atomic<size_t> count{0};
+ auto add = [](void *handle, const celix_service_tracker_info_t*) {
+ auto *c = (std::atomic<size_t>*)handle;
+ c->fetch_add(1);
+ };
+ long trkId1 = celix_bundleContext_trackServiceTrackers(ctx, nullptr, (void*)&count, add, nullptr);
+ EXPECT_TRUE(trkId1 >= 0);
+
+ celix_service_tracking_options_t opts{};
+ opts.filter.serviceName = "service1";
+ long trkId2 = celix_bundleContext_trackServicesWithOptions(ctx, &opts);
+ EXPECT_TRUE(trkId2 >= 0);
+
+ opts.filter.serviceName = "service2";
+ long trkId3 = celix_bundleContext_trackServicesWithOptions(ctx, &opts);
+ EXPECT_TRUE(trkId3 >= 0);
+
+ EXPECT_EQ(2, count.load());
+
+ celix_bundleContext_stopTracker(ctx, trkId1);
+ celix_bundleContext_stopTracker(ctx, trkId2);
+ celix_bundleContext_stopTracker(ctx, trkId3);
+}
TEST_F(CelixBundleContextServicesTests, serviceFactoryTest) {
struct calc {
@@ -754,9 +923,54 @@
celix_bundleContext_unregisterService(ctx, facId);
}
+
+TEST_F(CelixBundleContextServicesTests, asyncServiceFactoryTest) {
+ struct calc {
+ int (*calc)(int);
+ };
+ auto name = "CALC";
+
+ int count = 0;
+ celix_service_factory_t fac;
+ memset(&fac, 0, sizeof(fac));
+ fac.handle = (void*)&count;
+ fac.getService = [](void *handle, const celix_bundle_t *, const celix_properties_t *) -> void* {
+ auto *c = (int *)handle;
+ *c += 1;
+ static struct calc svc; //normally a service per bundle
+ svc.calc = [](int arg) { return arg * 42; };
+ return &svc;
+ };
+ fac.ungetService = [](void *handle, const celix_bundle_t *, const celix_properties_t *) {
+ auto *c = (int *)handle;
+ *c += 1;
+ };
+
+ long facId = celix_bundleContext_registerServiceFactoryAsync(ctx, &fac, name, nullptr);
+ ASSERT_TRUE(facId >= 0);
+ celix_bundleContext_waitForAsyncRegistration(ctx, facId);
+
+
+ int result = -1;
+ bool called = celix_bundleContext_useService(ctx, name, &result, [](void *handle, void* svc) {
+ auto *r = (int *)(handle);
+ auto *calc = (struct calc*)svc;
+ *r = calc->calc(2);
+ });
+ ASSERT_TRUE(called);
+ ASSERT_EQ(84, result);
+ ASSERT_EQ(2, count); //expecting getService & unGetService to be called during the useService call.
+
+
+ celix_bundleContext_unregisterServiceAsync(ctx, facId, NULL, NULL);
+}
+
TEST_F(CelixBundleContextServicesTests, findServicesTest) {
long svcId1 = celix_bundleContext_registerService(ctx, (void*)0x100, "example", nullptr);
long svcId2 = celix_bundleContext_registerService(ctx, (void*)0x100, "example", nullptr);
+ long svcId3 = celix_bundleContext_registerService(ctx, (void*)0x100, "example", nullptr);
+ long svcId4 = celix_bundleContext_registerService(ctx, (void*)0x100, "example", nullptr);
+
long foundId = celix_bundleContext_findService(ctx, "non existing service name");
ASSERT_EQ(-1L, foundId);
@@ -764,15 +978,17 @@
foundId = celix_bundleContext_findService(ctx, "example");
ASSERT_EQ(foundId, svcId1); //oldest should have highest ranking
- array_list_t *list = celix_bundleContext_findServices(ctx, "non existintg service name");
+ array_list_t *list = celix_bundleContext_findServices(ctx, "non existing service name");
ASSERT_EQ(0, celix_arrayList_size(list));
arrayList_destroy(list);
list = celix_bundleContext_findServices(ctx, "example");
- ASSERT_EQ(2, celix_arrayList_size(list));
+ ASSERT_EQ(4, celix_arrayList_size(list));
arrayList_destroy(list);
celix_bundleContext_unregisterService(ctx, svcId1);
+ celix_bundleContext_unregisterService(ctx, svcId3);
+ celix_bundleContext_unregisterService(ctx, svcId4);
celix_service_filter_options_t opts{};
opts.serviceName = "example";
@@ -787,41 +1003,194 @@
int count = 0;
auto add = [](void *handle, const celix_service_tracker_info_t *info) {
- ASSERT_STRCASEEQ("example", info->serviceName);
- ASSERT_STRCASEEQ(CELIX_FRAMEWORK_SERVICE_C_LANGUAGE, info->serviceLanguage);
+ EXPECT_STRCASEEQ("example", info->serviceName);
+ EXPECT_STRCASEEQ(CELIX_FRAMEWORK_SERVICE_C_LANGUAGE, info->serviceLanguage);
auto *c = static_cast<int*>(handle);
*c += 1;
};
auto remove = [](void *handle, const celix_service_tracker_info_t *info) {
- ASSERT_STRCASEEQ("example", info->serviceName);
- ASSERT_STRCASEEQ(CELIX_FRAMEWORK_SERVICE_C_LANGUAGE, info->serviceLanguage);
+ EXPECT_STRCASEEQ("example", info->serviceName);
+ EXPECT_STRCASEEQ(CELIX_FRAMEWORK_SERVICE_C_LANGUAGE, info->serviceLanguage);
auto *c = static_cast<int*>(handle);
*c -= 1;
};
long trackerId = celix_bundleContext_trackServiceTrackers(ctx, "example", &count, add, remove);
- ASSERT_TRUE(trackerId >= 0);
- ASSERT_EQ(0, count);
+ EXPECT_TRUE(trackerId >= 0);
+ EXPECT_EQ(0, count);
long tracker2 = celix_bundleContext_trackService(ctx, "example", nullptr, nullptr);
- ASSERT_TRUE(tracker2 >= 0);
- ASSERT_EQ(1, count);
+ EXPECT_TRUE(tracker2 >= 0);
+ EXPECT_EQ(1, count);
long tracker3 = celix_bundleContext_trackServices(ctx, "example", nullptr, nullptr, nullptr);
- ASSERT_TRUE(tracker3 >= 0);
- ASSERT_EQ(2, count);
+ EXPECT_TRUE(tracker3 >= 0);
+ EXPECT_EQ(2, count);
long tracker4 = celix_bundleContext_trackServices(ctx, "no-match", nullptr, nullptr, nullptr);
- ASSERT_TRUE(tracker4 >= 0);
- ASSERT_EQ(2, count);
+ EXPECT_TRUE(tracker4 >= 0);
+ EXPECT_EQ(2, count);
celix_bundleContext_stopTracker(ctx, tracker2);
- celix_serviceTracker_syncForContext(ctx); //service tracker shutdown on separate track -> need sync
- ASSERT_EQ(1, count);
+ EXPECT_EQ(1, count);
celix_bundleContext_stopTracker(ctx, tracker3);
- celix_serviceTracker_syncForContext(ctx); //service tracker shutdown on separate track -> need sync
- ASSERT_EQ(0, count);
+ EXPECT_EQ(0, count);
celix_bundleContext_stopTracker(ctx, trackerId);
celix_bundleContext_stopTracker(ctx, tracker4);
}
+
+TEST_F(CelixBundleContextServicesTests, floodEventLoopTest) {
+ struct callback_data {
+ std::mutex mutex{};
+ std::condition_variable cond{};
+ bool ready{false};
+ };
+ callback_data data{};
+
+ //test so that the framework needs to use dynamic allocated event on the event loop
+ celix_service_registration_options_t opts{};
+ opts.svc = (void*)0x42;
+ opts.serviceName = "test";
+ opts.asyncData = (void*)&data;
+ opts.asyncCallback = [](void *d, long /*svcId*/) {
+ auto *localData = static_cast<callback_data*>(d);
+ std::unique_lock<std::mutex> lck{localData->mutex};
+ localData->cond.wait_for(lck, std::chrono::seconds{30}, [&]{ return localData->ready; }); //wait til ready.
+ EXPECT_TRUE(localData->ready);
+ };
+ long svcId = celix_bundleContext_registerServiceWithOptionsAsync(ctx, &opts);
+ EXPECT_GE(svcId, 0);
+
+ int nrOfAdditionalRegistrations = 300;
+ std::vector<long> svcIds{};
+ std::vector<long> trackerIds{};
+ for (int i = 0; i < nrOfAdditionalRegistrations; ++i) {
+ 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));
+
+ //CHECK if celix_bundleContext_isServiceRegistered work
+ EXPECT_FALSE(celix_bundleContext_isServiceRegistered(ctx, id));
+ }
+
+ {
+ //let the first service registration continue and as result all the following registrations.
+ std::lock_guard<std::mutex> lck{data.mutex};
+ data.ready = true;
+ data.cond.notify_all();
+ }
+
+ celix_bundleContext_waitForAsyncRegistration(ctx, svcId);
+ EXPECT_TRUE(celix_bundleContext_isServiceRegistered(ctx, svcId));
+ long foundId = celix_bundleContext_findService(ctx, "test");
+ EXPECT_GE(foundId, 0);
+
+ celix_bundleContext_unregisterServiceAsync(ctx, svcId, nullptr, nullptr);
+ for (auto id : svcIds) {
+ celix_bundleContext_unregisterServiceAsync(ctx, id, nullptr, nullptr);
+ celix_bundleContext_findService(ctx, "test"); //just to add some entropy
+ }
+ for (auto id : trackerIds) {
+ celix_bundleContext_stopTrackerAsync(ctx, id, nullptr, nullptr);
+ }
+}
+
+
+TEST_F(CelixBundleContextServicesTests, serviceOnDemandWithAsyncRegisterTest) {
+ //NOTE that even though service are registered async, they should be found by a useService call.
+
+ bool called = celix_bundleContext_useService(ctx, "test", nullptr, [](void*, void*){/*nop*/});
+ EXPECT_FALSE(called); //service not available
+
+ struct test_service {
+ void* handle;
+ };
+
+ struct callback_data {
+ celix_bundle_context_t* ctx;
+ long svcId;
+ test_service ts;
+ };
+ callback_data cbData{ctx, -1L, {nullptr}};
+
+ long trkId = celix_bundleContext_trackServiceTrackers(ctx, "test", &cbData, [](void *voidData, const celix_service_tracker_info_t*) {
+ auto* data = static_cast<callback_data*>(voidData);
+ data->svcId = celix_bundleContext_registerServiceAsync(data->ctx, &data->ts, "test", nullptr);
+ }, nullptr);
+
+ called = celix_bundleContext_useService(ctx, "test", nullptr, nullptr);
+ EXPECT_TRUE(called); //service created on demand.
+
+ celix_bundleContext_unregisterService(ctx, cbData.svcId);
+ celix_bundleContext_stopTracker(ctx, trkId);
+}
+
+TEST_F(CelixBundleContextServicesTests, startStopServiceTrackerAsync) {
+ std::atomic<int> count{0};
+
+ auto cb = [](void* data) {
+ auto* c = static_cast<std::atomic<int>*>(data);
+ (*c)++;
+ };
+
+ celix_service_tracking_options_t opts{};
+ opts.trackerCreatedCallbackData = &count;
+ opts.trackerCreatedCallback = cb;
+ long trkId = celix_bundleContext_trackServicesWithOptionsAsync(ctx, &opts);
+ EXPECT_GE(trkId, 0);
+ celix_bundleContext_waitForAsyncTracker(ctx, trkId);
+ EXPECT_EQ(count.load(), 1); //1x tracker started
+
+ celix_bundleContext_stopTrackerAsync(ctx, trkId, &count, cb);
+ celix_bundleContext_waitForAsyncStopTracker(ctx, trkId);
+ EXPECT_EQ(2, count.load()); //1x tracker started, 1x tracker stopped
+}
+
+TEST_F(CelixBundleContextServicesTests, startStopMetaServiceTrackerAsync) {
+ std::atomic<int> count{0};
+
+ auto cb = [](void* data) {
+ auto* c = static_cast<std::atomic<int>*>(data);
+ (*c)++;
+ };
+
+ long trkId = celix_bundleContext_trackServiceTrackersAsync(ctx, "test", nullptr, nullptr, nullptr, &count, cb);
+ EXPECT_GE(trkId, 0);
+ celix_bundleContext_waitForAsyncTracker(ctx, trkId);
+ EXPECT_EQ(count.load(), 1); //1x tracker started
+
+ celix_bundleContext_stopTrackerAsync(ctx, trkId, &count, cb);
+ celix_bundleContext_waitForAsyncStopTracker(ctx, trkId);
+ EXPECT_EQ(2, count.load()); //1x tracker started, 1x tracker stopped
+}
+
+TEST_F(CelixBundleContextServicesTests, onlyCallAsyncCallbackWithAsyncApi) {
+ celix_service_tracking_options_t opts{};
+ opts.trackerCreatedCallback = [](void *) {
+ FAIL();
+ };
+ long trkId = celix_bundleContext_trackServicesWithOptions(ctx, &opts);
+ EXPECT_GT(trkId, 0);
+ celix_bundleContext_stopTracker(ctx, trkId);
+
+ celix_service_registration_options_t opts2{};
+ opts2.serviceName = "test";
+ opts2.svc = (void*)0x42;
+ opts2.asyncCallback = [](void*, long) {
+ FAIL();
+ };
+ long svcId = celix_bundleContext_registerServiceWithOptions(ctx, &opts2);
+ EXPECT_GT(svcId, 0);
+ celix_bundleContext_waitForEvents(ctx);
+ celix_bundleContext_unregisterService(ctx, trkId);
+
+ celix_bundle_tracking_options_t opts3{};
+ opts3.trackerCreatedCallback = [](void *) {
+ FAIL();
+ };
+ trkId = celix_bundleContext_trackBundlesWithOptions(ctx, &opts3);
+ EXPECT_GT(trkId, 0);
+ celix_bundleContext_stopTracker(ctx, trkId);
+}
\ No newline at end of file
diff --git a/libs/framework/gtest/src/run_tests.cpp b/libs/framework/gtest/src/run_tests.cpp
deleted file mode 100644
index 339f725..0000000
--- a/libs/framework/gtest/src/run_tests.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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.
- */
-
-#include <gtest/gtest.h>
-
-int main(int argc, char **argv) {
- ::testing::InitGoogleTest(&argc, argv);
- int rc = RUN_ALL_TESTS();
- return rc;
-}
\ No newline at end of file
diff --git a/libs/framework/gtest/src/single_framework_test.cpp b/libs/framework/gtest/src/single_framework_test.cpp
index 05ab3c2..28d1a8a 100644
--- a/libs/framework/gtest/src/single_framework_test.cpp
+++ b/libs/framework/gtest/src/single_framework_test.cpp
@@ -18,17 +18,13 @@
*/
#include <gtest/gtest.h>
+#include <atomic>
extern "C" {
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-
#include "celix_launcher.h"
#include "celix_framework_factory.h"
+#include "celix_framework.h"
static celix_framework_t *framework = nullptr;
@@ -81,6 +77,24 @@
testFramework();
}
+TEST_F(CelixFramework, testEventQueue) {
+ long eid = celix_framework_nextEventId(framework);
+ EXPECT_GE(eid, 0);
+ celix_framework_waitForGenericEvent(framework, eid); //event never issued so should return directly
+
+ std::atomic<int> count{0};
+ celix_framework_fireGenericEvent(framework, eid, -1L, "test", static_cast<void*>(&count), [](void* data) {
+ auto *c = static_cast<std::atomic<int>*>(data);
+ *c += 1;
+ }, static_cast<void*>(&count), [](void* data) {
+ auto *c = static_cast<std::atomic<int>*>(data);
+ *c += 3;
+ });
+
+ celix_framework_waitForGenericEvent(framework, eid);
+ EXPECT_EQ(4, count);
+}
+
class FrameworkFactory : public ::testing::Test {
public:
FrameworkFactory() = default;
@@ -170,6 +184,11 @@
framework_start(fw);
framework_stop(fw);
framework_waitForStop(fw);
+
+ framework_start(fw);
+ framework_stop(fw);
+ framework_waitForStop(fw);
+
framework_destroy(fw);
}
diff --git a/libs/framework/include/celix_bundle_context.h b/libs/framework/include/celix_bundle_context.h
index 2f11e06..b99c8b5 100644
--- a/libs/framework/include/celix_bundle_context.h
+++ b/libs/framework/include/celix_bundle_context.h
@@ -40,16 +40,34 @@
#define OPTS_INIT
#endif
+
/**
-* Register a service to the Celix framework.
-*
-* @param ctx The bundle context
-* @param svc the service object. Normally a pointer to a service struct (i.e. a struct with function pointers)
-* @param serviceName the service name, cannot be NULL
-* @param properties The meta properties associated with the service. The service registration will take ownership of the properties (i.e. no destroy needed)
-* @return The serviceId (>= 0) or < 0 if the registration was unsuccessful.
-*/
-long celix_bundleContext_registerService(celix_bundle_context_t *ctx, void *svc, const char *serviceName, celix_properties_t *properties);
+ * Register a service to the Celix framework.
+ *
+ * The service will be registered async on the Celix event loop thread. This means that service registration is (probably)
+ * not yet concluded when this function returns, but is added to the event loop.
+ * Use celix_bundleContext_waitForAsyncRegistration to synchronise with the
+ * actual service registration in the framework's service registry.
+ *
+ * @param ctx The bundle context
+ * @param svc the service object. Normally a pointer to a service struct (i.e. a struct with function pointers)
+ * @param serviceName the service name, cannot be NULL
+ * @param properties The meta properties associated with the service. The service registration will take ownership of the properties (i.e. no destroy needed)
+ * @return The serviceId (>=0) or -1 if the registration was unsuccessful.
+ */
+long celix_bundleContext_registerServiceAsync(celix_bundle_context_t *ctx, void *svc, const char *serviceName, celix_properties_t *properties);
+
+/**
+ * Register a service to the Celix framework.
+ * Note: Please use the celix_bundleContext_registerServiceAsync instead.
+ *
+ * @param ctx The bundle context
+ * @param svc the service object. Normally a pointer to a service struct (i.e. a struct with function pointers)
+ * @param serviceName the service name, cannot be NULL
+ * @param properties The meta properties associated with the service. The service registration will take ownership of the properties (i.e. no destroy needed)
+ * @return The serviceId (>=0) or -1 if the registration was unsuccessful.
+ */
+long celix_bundleContext_registerService(celix_bundle_context_t *ctx, void *svc, const char *serviceName, celix_properties_t *properties); //__attribute__((deprecated("Use celix_bundleContext_registerServiceAsync instead!")));
/**
* Register a service factory in the framework (for the C language).
@@ -61,13 +79,37 @@
* When a service in no longer needed for a bundle (e.g. ending the useService(s) calls or when a service tracker is stopped)
* the ungetService function of the service factory will be called.
*
+ * The service will be registered async on the Celix event loop thread. This means that service registration is (probably)
+ * not yet concluded when this function returns, but is added to the event loop.
+ * Use celix_bundleContext_waitForAsyncRegistration to synchronise with the
+ * actual service registration in the framework's service registry.
+ *
+ * @param ctx The bundle context
+ * @param factory The pointer to the factory service.
+ * @param serviceName The required service name of the services this factory will produce.
+ * @param properties The optional service factory properties. For a service consumer this will be seen as the service properties.
+ * @return The serviceId (>= 0) or < 0 if the registration was unsuccessful.
+ */
+long celix_bundleContext_registerServiceFactoryAsync(celix_bundle_context_t *ctx, celix_service_factory_t *factory, const char *serviceName, celix_properties_t *props);
+
+/**
+ * Register a service factory in the framework (for the C language).
+ * The service factory will be called for every bundle requesting/de-requesting a service. This gives the provider the
+ * option to create bundle specific service instances.
+ * Note: Please use the celix_bundleContext_registerServiceFactoryAsync instead.
+ *
+ * When a service is requested for a bundle the getService of the factory service will be called. This function must
+ * return a valid pointer to a service conform the registered service name or NULL.
+ * When a service in no longer needed for a bundle (e.g. ending the useService(s) calls or when a service tracker is stopped)
+ * the ungetService function of the service factory will be called.
+ *
* @param ctx The bundle context
* @param factory The pointer to the factory service.
* @param serviceName The required service name of the services this factory will produce.
* @param properties The optional service factory properties. For a service consumer this will be seen as the service properties.
* @return The serviceId (>= 0) or < 0 if the registration was unsuccessful.
*/
-long celix_bundleContext_registerServiceFactory(celix_bundle_context_t *ctx, celix_service_factory_t *factory, const char *serviceName, celix_properties_t *props);
+long celix_bundleContext_registerServiceFactory(celix_bundle_context_t *ctx, celix_service_factory_t *factory, const char *serviceName, celix_properties_t *props); //__attribute__((deprecated("Use celix_bundleContext_registerServiceFactoryAsync instead!")));
/**
* Service Registration Options when registering services to the Celix framework.
@@ -126,6 +168,17 @@
* for this.
*/
const char *serviceVersion OPTS_INIT;
+
+ /**
+ * Async data pointer for the async register callback.
+ */
+ void *asyncData OPTS_INIT;
+
+ /**
+ * Async callback. Will be called after the a service is registered in the service registry using a async call.
+ * Will be called on the Celix event loop.
+ */
+ void (*asyncCallback)(void *data, long serviceId) OPTS_INIT;
} celix_service_registration_options_t;
/**
@@ -137,18 +190,50 @@
.serviceName = NULL, \
.properties = NULL, \
.serviceLanguage = NULL, \
- .serviceVersion = NULL }
+ .serviceVersion = NULL, \
+ .asyncData = NULL, \
+ .asyncCallback = NULL }
#endif
+/**
+ * Register a service to the Celix framework using the provided service registration options.
+ *
+ * The service will be registered async on the Celix event loop thread. This means that service registration is (probably)
+ * not yet concluded when this function returns, but is added to the event loop..
+ * Use celix_bundleContext_waitForAsyncRegistration to synchronise with the
+ * actual service registration in the framework's service registry.
+ *
+ * @param ctx The bundle context
+ * @param opts The pointer to the registration options. The options are only in the during registration call.
+ * @return The serviceId (>= 0) or -1 if the registration was unsuccessful and -2 if the registration was cancelled (@see celix_bundleContext_reserveSvcId).
+ */
+long celix_bundleContext_registerServiceWithOptionsAsync(celix_bundle_context_t *ctx, const celix_service_registration_options_t *opts);
/**
-* Register a service to the Celix framework using the provided service registration options.
-*
-* @param ctx The bundle context
-* @param opts The pointer to the registration options. The options are only in the during registration call.
-* @return The serviceId (>= 0) or < 0 if the registration was unsuccessful.
-*/
-long celix_bundleContext_registerServiceWithOptions(celix_bundle_context_t *ctx, const celix_service_registration_options_t *opts);
+ * Register a service to the Celix framework using the provided service registration options.
+ * Note: Please use the celix_bundleContext_registerServiceAsyncWithOptions instead.
+ *
+ * @param ctx The bundle context
+ * @param opts The pointer to the registration options. The options are only in the during registration call.
+ * @return The serviceId (>= 0) or -1 if the registration was unsuccessful and -2 if the registration was cancelled (@see celix_bundleContext_reserveSvcId).
+ */
+long celix_bundleContext_registerServiceWithOptions(celix_bundle_context_t *ctx, const celix_service_registration_options_t *opts); //__attribute__((deprecated("Use celix_bundleContext_registerServiceAsyncWithOptions instead!")));
+
+/**
+ * Waits til the async service registration for the provided serviceId is done.
+ * Silently ignore service ids < 0.
+ * Will directly return if there is no pending service registration for the provided service id.
+ */
+void celix_bundleContext_waitForAsyncRegistration(celix_bundle_context_t* ctx, long serviceId);
+
+/**
+ * Checks whether a service for the provided service id is registered in the service registry.
+ * Note return false if the service for the provided service id is still pending in the event loop.
+ * Silently ignore service ids < 0 (returns false).
+ *
+ * Returns true if the service is registered in the service registry.
+ */
+bool celix_bundleContext_isServiceRegistered(celix_bundle_context_t* ctx, long serviceId);
/**
@@ -160,11 +245,30 @@
* @param ctx The bundle context
* @param serviceId The service id
*/
-void celix_bundleContext_unregisterService(celix_bundle_context_t *ctx, long serviceId);
+void celix_bundleContext_unregisterService(celix_bundle_context_t *ctx, long serviceId); //__attribute__((deprecated("Use celix_bundleContext_unregisterService instead!")));
+/**
+ * Unregister the service or service factory with service id.
+ * The service will only be unregistered if the bundle of the bundle context is the owner of the service.
+ *
+ * The service will be umregistered async on the Celix event loop thread. This means that service unregistration is (probably)
+ * not yet concluded when this function returns. Use celix_bundleContext_waitForAsyncUnregistration to synchronise with the
+ * actual service unregistration in the framework's service registry.
+ *
+ * @param ctx The bundle context
+ * @param serviceId The service id
+ * @param doneData The data used on the doneCallback (if present)
+ * @param doneCallback If not NULL, this callback will be called when the unregisration is done. (will be called on the event loop thread)
+ */
+void celix_bundleContext_unregisterServiceAsync(celix_bundle_context_t *ctx, long serviceId, void* doneData, void (*doneCallback)(void* doneData));
+/**
+ * Waits til the async service unregistration for the provided serviceId is done.
+ * Silently ignore service < 0.
+ */
+void celix_bundleContext_waitForAsyncUnregistration(celix_bundle_context_t* ctx, long serviceId);
@@ -192,7 +296,8 @@
*/
typedef struct celix_service_filter_options {
/**
- * The required service name.
+ * The service name.
+ * If NULL is used any services which matches the filter string will be tracked.
*/
const char* serviceName OPTS_INIT;
@@ -248,15 +353,39 @@
*/
celix_array_list_t* celix_bundleContext_findServicesWithOptions(celix_bundle_context_t *ctx, const celix_service_filter_options_t *opts);
-
/**
* track the highest ranking service with the provided serviceName.
* The highest ranking services will used for the callback.
* If a new and higher ranking services the callback with be called again with the new service.
* If a service is removed a the callback with be called with next highest ranking service or NULL as service.
*
+ * The service tracker will be created async on the Celix event loop thread. This means that the function can return
+ * before the tracker is created.
+ *
* @param ctx The bundle context.
- * @param serviceName The required service name to track
+ * @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.
+ */
+long celix_bundleContext_trackServiceAsync(
+ celix_bundle_context_t* ctx,
+ const char* serviceName,
+ void* callbackHandle,
+ void (*set)(void* handle, void* svc)
+);
+
+/**
+ * track the highest ranking service with the provided serviceName.
+ * The highest ranking services will used for the callback.
+ * If a new and higher ranking services the callback with be called again with the new service.
+ * If a service is removed a the callback with be called with next highest ranking service or NULL as service.
+ * Note: Please use the celix_bundleContext_trackServiceAsync instead.
+ *
+ * @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.
@@ -266,13 +395,37 @@
const char* serviceName,
void* callbackHandle,
void (*set)(void* handle, void* svc)
-);
+); //__attribute__((deprecated("Use celix_bundleContext_trackServiceSync instead!")));
/**
* track services with the provided serviceName.
*
+ * The service tracker will be created async on the Celix event loop thread. This means that the function can return
+ * before the tracker is created.
+ *
* @param ctx The bundle context.
* @param serviceName The required service name to track
+ * If NULL is all service are tracked.
+ * @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.
+ */
+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)
+);
+
+/**
+ * track services with the provided serviceName.
+ * Note: Please 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
@@ -284,7 +437,7 @@
void* callbackHandle,
void (*add)(void* handle, void* svc),
void (*remove)(void* handle, void* svc)
-);
+); //__attribute__((deprecated("Use celix_bundleContext_trackServicesAsync instead!")));;
/**
* Service Tracker Options used to fine tune which services to track and the callback to be used for the tracked services.
@@ -362,6 +515,17 @@
* 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) OPTS_INIT;
+
+
+ /**
+ * Data for the trackerCreatedCallback.
+ */
+ void *trackerCreatedCallbackData OPTS_INIT;
+
+ /**
+ * The callback called when the tracker has ben created (and is active) when using a async call.
+ */
+ void (*trackerCreatedCallback)(void *trackerCreatedCallbackData) OPTS_INIT;
} celix_service_tracking_options_t;
/**
@@ -382,30 +546,74 @@
.removeWithProperties = NULL, \
.setWithOwner = NULL, \
.addWithOwner = NULL, \
- .removeWithOwner = NULL}
+ .removeWithOwner = NULL, \
+ .trackerCreatedCallbackData = NULL, \
+ .trackerCreatedCallback = NULL }
#endif
/**
* Tracks services using the provided tracker options.
* The tracker options are only using during this call and can safely be freed/reused after this call returns.
*
+ * The service tracker will be created async on the Celix event loop thread. This means that the function can return
+ * before the tracker is created.
+ *
* @param ctx The bundle context.
* @param opts The pointer to the tracker options.
* @return the tracker id (>=0) or < 0 if unsuccessful.
*/
-long celix_bundleContext_trackServicesWithOptions(celix_bundle_context_t *ctx, const celix_service_tracking_options_t *opts);
+long celix_bundleContext_trackServicesWithOptionsAsync(celix_bundle_context_t *ctx, const celix_service_tracking_options_t *opts);
+
+/**
+ * Tracks services using the provided tracker options.
+ * 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.
+ */
+long celix_bundleContext_trackServicesWithOptions(celix_bundle_context_t *ctx, const celix_service_tracking_options_t *opts); //__attribute__((deprecated("Use celix_bundleContext_trackServicesWithOptionsAsync instead!")));
/**
* Stop the tracker with the provided track id.
* Could be a service tracker, bundle tracker or service tracker tracker.
* Only works for the trackers owned by the bundle of the bundle context.
*
+ * The service tracker will be destroyed async on the Celix event loop thread. This means that the function can return
+ * before the tracker is destroyed.
+ *
+ * if the doneCallback is not NULL, this will be called when the destruction of the service tracker is done.
+ * (will be called on the event loop thread).
+ *
* Will log a error if the provided tracker id is unknown. Will silently ignore trackerId < 0.
*/
-void celix_bundleContext_stopTracker(celix_bundle_context_t *ctx, long trackerId);
+void celix_bundleContext_stopTrackerAsync(
+ celix_bundle_context_t *ctx,
+ long trackerId,
+ void *doneCallbackData,
+ void (*doneCallback)(void* doneCallbackData));
+/**
+ * Wait for (async) creation of tracker
+ */
+void celix_bundleContext_waitForAsyncTracker(celix_bundle_context_t* ctx, long trackerId);
+/**
+ * Wait for (async) stopping of tracking.
+ */
+void celix_bundleContext_waitForAsyncStopTracker(celix_bundle_context_t* ctx, long trackerId);
+/**
+ * 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.
+ */
+void celix_bundleContext_stopTracker(celix_bundle_context_t *ctx, long trackerId); //__attribute__((deprecated("Use celix_bundleContext_stopTrackerAsync instead!")));
@@ -655,10 +863,33 @@
*/
char* celix_bundleContext_getBundleSymbolicName(celix_bundle_context_t *ctx, long bndId);
+
/**
* track bundles
* The add bundle callback will also be called for already installed bundles.
*
+ * The bundle tracker will be created async on the Celix event loop thread. This means that the function can return
+ * before the tracker is created.
+ *
+ * @param ctx The bundle context.
+ * @param callbackHandle The data pointer, which will be used in the callbacks
+ * @param add The callback which will be called for started bundles.
+ * @param remove The callback which will be called when bundles are stopped.
+ * @return The bundle tracker id or < 0 if unsuccessful.
+ */
+long celix_bundleContext_trackBundlesAsync(
+ celix_bundle_context_t* ctx,
+ void* callbackHandle,
+ void (*onStarted)(void* handle, const celix_bundle_t *bundle),
+ void (*onStopped)(void *handle, const celix_bundle_t *bundle)
+);
+
+/**
+ * track bundles
+ * The add bundle callback will also be called for already installed bundles.
+ *
+ * Note: please use celix_bundleContext_trackBundlesAsync instead.
+ *
* @param ctx The bundle context.
* @param callbackHandle The data pointer, which will be used in the callbacks
* @param add The callback which will be called for started bundles.
@@ -670,7 +901,7 @@
void* callbackHandle,
void (*onStarted)(void* handle, const celix_bundle_t *bundle),
void (*onStopped)(void *handle, const celix_bundle_t *bundle)
-);
+); //__attribute__((deprecated("Use celix_bundleContext_trackBundlesAsync instead!")));
/**
@@ -718,13 +949,24 @@
* This is done, because the framework bundle is a special bundle which is generally not needed in the callbacks.
*/
bool includeFrameworkBundle OPTS_INIT;
+
+ /**
+ * Data for the trackerCreatedCallback.
+ */
+ void *trackerCreatedCallbackData OPTS_INIT;
+
+ /**
+ * The callback called when the tracker has ben created (and is active) when using the
+ * track bundles ascync calls.
+ */
+ void (*trackerCreatedCallback)(void *trackerCreatedCallbackData) OPTS_INIT;
} celix_bundle_tracking_options_t;
/**
* C Macro to create a empty celix_service_filter_options_t type.
*/
#ifndef __cplusplus
-#define CELIX_EMPTY_BUNDLE_TRACKING_OPTIONS {.callbackHandle = NULL, .onInstalled = NULL, .onStarted = NULL, .onStopped = NULL, .onBundleEvent = NULL, .includeFrameworkBundle = false}
+#define CELIX_EMPTY_BUNDLE_TRACKING_OPTIONS {.callbackHandle = NULL, .onInstalled = NULL, .onStarted = NULL, .onStopped = NULL, .onBundleEvent = NULL, .includeFrameworkBundle = false, .trackerCreatedCallbackData = NULL, .trackerCreatedCallback = NULL}
#endif
/**
@@ -732,6 +974,25 @@
* The tracker options are only using during this call and can safely be freed/reused after this call returns.
* (i.e. can be on the stack)
*
+ * The bundle tracker will be created async on the Celix event loop thread. This means that the function can return
+ * before the tracker is created.
+ *
+ * @param ctx The bundle context.
+ * @param opts The pointer to the bundle tracker options.
+ * @return The bundle tracker id (>=0) or < 0 if unsuccessful.
+ */
+long celix_bundleContext_trackBundlesWithOptionsAsync(
+ celix_bundle_context_t* ctx,
+ const celix_bundle_tracking_options_t *opts
+);
+
+/**
+ * Tracks bundles using the provided bundle tracker options.
+ * The tracker options are only using during this call and can safely be freed/reused after this call returns.
+ * (i.e. can be on the stack)
+ *
+ * Note: please use celix_bundleContext_trackBundlesWithOptionsAsync instead;
+ *
* @param ctx The bundle context.
* @param opts The pointer to the bundle tracker options.
* @return The bundle tracker id (>=0) or < 0 if unsuccessful.
@@ -739,7 +1000,7 @@
long celix_bundleContext_trackBundlesWithOptions(
celix_bundle_context_t* ctx,
const celix_bundle_tracking_options_t *opts
-);
+); //__attribute__((deprecated("Use celix_bundleContext_trackBundlesWithOptionsAsync instead!")));
/**
* Use the bundle with the provided bundle id if it is in the active (started) state
@@ -813,8 +1074,43 @@
*
* This tracker can be stopped with the celix_bundleContext_stopTracker function.
*
+ * The service tracker tracker will be created async on the Celix event loop thread. This means that the function can return
+ * before the tracker is created.
+ *
* @param ctx The bundle context
* @param serviceName The target service name for the service tracker to track.
+ * If NULL is provided, add/remove callbacks will be called for all service trackers in the framework.
+ * @param callbackHandle The callback handle which will be provided as handle in the trackerAdd and trackerRemove callback.
+ * @param trackerAdd Called when a service tracker is added, which tracks the provided service name. Will also be called
+ * for all existing service tracker when this tracker is started.
+ * @param trackerRemove Called when a service tracker is removed, which tracks the provided service name
+ * @param doneCallbackData call back data argument provided to the done callback function.
+ * @param doneCallback If not NULL will be called when the service tracker tracker is created.
+ * @return The tracker id or <0 if something went wrong (will log an error).
+ */
+long celix_bundleContext_trackServiceTrackersAsync(
+ celix_bundle_context_t *ctx,
+ const char *serviceName,
+ void *callbackHandle,
+ void (*trackerAdd)(void *handle, const celix_service_tracker_info_t *info),
+ void (*trackerRemove)(void *handle, const celix_service_tracker_info_t *info),
+ void *doneCallbackData,
+ void (*doneCallback)(void* doneCallbackData));
+
+/**
+ * Track the service tracker targeting the provided service name. This can be used to track if there is an interest
+ * in a certain service and ad-hoc act on that interest.
+ *
+ * Note that the celix_service_tracker_info_t pointer in the trackerAdd/trackerRemove callbacks are only valid during
+ * the callback.
+ *
+ * Note: Please use celix_bundleContext_trackServiceTrackersAsync instead.
+ *
+ * This tracker can be stopped with the celix_bundleContext_stopTracker function.
+ *
+ * @param ctx The bundle context
+ * @param serviceName The target service name for the service tracker to track.
+ * If NULL is provided, add/remove callbacks will be called for all service trackers in the framework.
* @param callbackHandle The callback handle which will be provided as handle in the trackerAdd and trackerRemove callback.
* @param trackerAdd Called when a service tracker is added, which tracks the provided service name. Will also be called
* for all existing service tracker when this tracker is started.
@@ -826,7 +1122,7 @@
const char *serviceName,
void *callbackHandle,
void (*trackerAdd)(void *handle, const celix_service_tracker_info_t *info),
- void (*trackerRemove)(void *handle, const celix_service_tracker_info_t *info));
+ void (*trackerRemove)(void *handle, const celix_service_tracker_info_t *info)); //__attribute__((deprecated("Use celix_bundleContext_trackServiceTrackersAsync instead!")));
/**
* Gets the dependency manager for this bundle context.
@@ -837,6 +1133,12 @@
/**
+ * Wait till there are event for the bundle of this bundle context.
+ */
+void celix_bundleContext_waitForEvents(celix_bundle_context_t* ctx);
+
+
+/**
* Returns the bundle for this bundle context.
*/
celix_bundle_t* celix_bundleContext_getBundle(const celix_bundle_context_t *ctx);
diff --git a/libs/framework/include/celix_framework.h b/libs/framework/include/celix_framework.h
index 7af1e2c..6e1afbe 100644
--- a/libs/framework/include/celix_framework.h
+++ b/libs/framework/include/celix_framework.h
@@ -156,6 +156,44 @@
void celix_framework_setLogCallback(celix_framework_t* fw, void* logHandle, void (*logFunction)(void* handle, celix_log_level_e level, const char* file, const char *function, int line, const char *format, va_list formatArgs));
+/**
+ * wait till all events for the bundle identified by the bndId are processed.
+ */
+void celix_framework_waitUntilNoEventsForBnd(celix_framework_t* fw, long bndId);
+
+/**
+ * Returns whether the current thread is the Celix framework event loop thread.
+ */
+bool celix_framework_isCurrentThreadTheEventLoop(celix_framework_t* fw);
+
+
+/**
+ * Fire a generic event. The event will be added to the event loop and handled on the event loop thread.
+ *
+ * if bndId >=0 the bundle usage count will be increased while the event is not yet processed or finished processing.
+ * The eventName is expected to be const char* valid during til the event is finished processing.
+ *
+ * if eventId >=0 this will be used, otherwise a new event id will be generated
+ * return eventId
+ */
+long celix_framework_fireGenericEvent(celix_framework_t* fw, long eventId, long bndId, const char *eventName, void* processData, void (*processCallback)(void *data), void* doneData, void (*doneCallback)(void* doneData));
+
+/**
+ * Get the next event id.
+ *
+ * This can be used to ensure celix_framework_waitForGenericEvent can be used to wait for an event.
+ * The returned event id will not be used by the framework itself unless followed up with a
+ * celix_framework_fireGenericEvent call using the returned event id.
+ */
+long celix_framework_nextEventId(celix_framework_t *fw);
+
+/**
+ * Wait til a event with the provided event id is completely handled.
+ * This function will directly return if the provided event id is not in the event loop (already done or never issued).
+ */
+void celix_framework_waitForGenericEvent(celix_framework_t *fw, long eventId);
+
+
#ifdef __cplusplus
}
#endif
diff --git a/libs/framework/include/service_registry.h b/libs/framework/include/service_registry.h
index db93cb5..275f707 100644
--- a/libs/framework/include/service_registry.h
+++ b/libs/framework/include/service_registry.h
@@ -60,8 +60,6 @@
celix_status_t
serviceRegistry_unregisterService(service_registry_pt registry, celix_bundle_t *bundle, service_registration_pt registration);
-celix_status_t serviceRegistry_clearServiceRegistrations(service_registry_pt registry, celix_bundle_t *bundle);
-
celix_status_t serviceRegistry_getServiceReference(service_registry_pt registry, celix_bundle_t *bundle,
service_registration_pt registration,
service_reference_pt *reference);
@@ -95,6 +93,17 @@
celix_status_t celix_serviceRegistry_removeServiceListener(celix_service_registry_t *reg, celix_service_listener_t *listener);
+bool celix_serviceRegistry_isServiceRegistered(celix_service_registry_t* reg, long serviceId);
+
+celix_status_t
+celix_serviceRegistry_registerService(
+ celix_service_registry_t *reg,
+ const celix_bundle_t *bnd,
+ const char *serviceName,
+ void* service,
+ celix_properties_t* props,
+ long reserveId,
+ service_registration_t **registration);
celix_status_t
celix_serviceRegistry_registerServiceFactory(
@@ -103,6 +112,7 @@
const char *serviceName,
celix_service_factory_t *factory,
celix_properties_t* props,
+ long reserveId,
service_registration_t **registration);
/**
@@ -134,6 +144,37 @@
long celix_serviceRegistry_nextSvcId(celix_service_registry_t* registry);
+/**
+ * Unregister service for the provided service id (owned by bnd).
+ * Will print an error if the service id is invalid
+ */
+void celix_serviceRegistry_unregisterService(celix_service_registry_t* registry, celix_bundle_t* bnd, long serviceId);
+
+
+/**
+ * Create a LDAP filter for the provided filter parts.
+ * @param serviceName The optional service name
+ * @param versionRange The optional version range
+ * @param filter The optional filter
+ * @param serviceLanguage The optional service lang. if NULL, lang C will be used.
+ * @param ignoreServiceLanguage The whether the service lang filter needs to be added to the filter.
+ * @return a LDAP filter. Caller is responsible for freeing the filter.
+ */
+char* celix_serviceRegistry_createFilterFor(
+ celix_service_registry_t* registry,
+ const char* serviceName,
+ const char* versionRange,
+ const char* additionalFilter,
+ const char* serviceLanguage,
+ bool ignoreServiceLanguage);
+
+/**
+ * Find services and return a array list of service ids (long).
+ * Caller is responsible for freeing the returned array list.
+ */
+celix_array_list_t* celix_serviceRegisrty_findServices(celix_service_registry_t* registry, const char* filter);
+
+
#ifdef __cplusplus
}
#endif
diff --git a/libs/framework/include/service_tracker.h b/libs/framework/include/service_tracker.h
index 4e69621..8c377e2 100644
--- a/libs/framework/include/service_tracker.h
+++ b/libs/framework/include/service_tracker.h
@@ -113,7 +113,6 @@
bool celix_serviceTracker_useHighestRankingService(
celix_service_tracker_t *tracker,
const char *serviceName /*sanity*/,
- double waitTimeoutInSeconds /*0 -> do not wait */,
void *callbackHandle,
void (*use)(void *handle, void *svc),
void (*useWithProperties)(void *handle, void *svc, const celix_properties_t *props),
@@ -134,16 +133,6 @@
void (*useWithOwner)(void *handle, void *svc, const celix_properties_t *props, const celix_bundle_t *owner)
);
-/**
- * blocks until all shutdown threads for the service tracker instances for the provided framework are done.
- */
-void celix_serviceTracker_syncForFramework(void *fw);
-
-/**
- * blocks until all shutdown threads for the service tracker instances for the provided bundle context are done.
- */
-void celix_serviceTracker_syncForContext(void *ctx);
-
#ifdef __cplusplus
}
diff --git a/libs/framework/src/bundle.c b/libs/framework/src/bundle.c
index 9994675..6d4eafc 100644
--- a/libs/framework/src/bundle.c
+++ b/libs/framework/src/bundle.c
@@ -173,7 +173,7 @@
}
celix_status_t bundle_getState(const_bundle_pt bundle, bundle_state_e *state) {
- if(bundle==NULL){
+ if (bundle==NULL) {
*state = OSGI_FRAMEWORK_BUNDLE_UNKNOWN;
return CELIX_BUNDLE_EXCEPTION;
}
@@ -674,23 +674,25 @@
celix_array_list_t* celix_bundle_listServiceTrackers(const celix_bundle_t *bnd) {
celix_array_list_t* result = celix_arrayList_create();
- //FIXME: should not fall back to bundle context, but for now that is were the trackers are stored.
celixThreadMutex_lock(&bnd->context->mutex);
hash_map_iterator_t iter = hashMapIterator_construct(bnd->context->serviceTrackers);
while (hashMapIterator_hasNext(&iter)) {
- celix_service_tracker_t *tracker = hashMapIterator_nextValue(&iter);
- celix_bundle_service_tracker_list_entry_t *entry = calloc(1, sizeof(*entry));
- entry->filter = celix_utils_strdup(tracker->filter);
- entry->nrOfTrackedServices = serviceTracker_nrOfTrackedServices(tracker);
- entry->serviceName = celix_utils_strdup(tracker->serviceName);
- entry->bundleOwner = celix_bundle_getId(bnd);
+ celix_bundle_context_service_tracker_entry_t *trkEntry = hashMapIterator_nextValue(&iter);
+ if (trkEntry->tracker != NULL) {
+ celix_bundle_service_tracker_list_entry_t *entry = calloc(1, sizeof(*entry));
+ entry->filter = celix_utils_strdup(trkEntry->tracker->filter);
+ entry->nrOfTrackedServices = serviceTracker_nrOfTrackedServices(trkEntry->tracker);
+ entry->serviceName = celix_utils_strdup(trkEntry->tracker->serviceName);
+ entry->bundleOwner = celix_bundle_getId(bnd);
- if (entry->serviceName != NULL) {
- celix_arrayList_add(result, entry);
- } 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);
+ if (entry->serviceName != NULL) {
+ celix_arrayList_add(result, entry);
+ } 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);
+ }
}
}
celixThreadMutex_unlock(&bnd->context->mutex);
diff --git a/libs/framework/src/bundle_context.c b/libs/framework/src/bundle_context.c
index b7f5326..2f1473d 100644
--- a/libs/framework/src/bundle_context.c
+++ b/libs/framework/src/bundle_context.c
@@ -21,6 +21,8 @@
#include <stdio.h>
#include <string.h>
#include <utils.h>
+#include <assert.h>
+#include <unistd.h>
#include "celix_utils.h"
#include "celix_constants.h"
@@ -34,11 +36,15 @@
#include "dm_dependency_manager_impl.h"
#include "celix_array_list.h"
#include "module.h"
+#include "service_tracker_private.h"
+#include "celix_array_list.h"
static celix_status_t bundleContext_bundleChanged(void *handle, bundle_event_t *event);
static void bundleContext_cleanupBundleTrackers(bundle_context_t *ct);
static void bundleContext_cleanupServiceTrackers(bundle_context_t *ctx);
static void bundleContext_cleanupServiceTrackerTrackers(bundle_context_t *ctx);
+static void bundleContext_cleanupServiceRegistration(bundle_context_t* ctx);
+static long celix_bundleContext_trackServicesWithOptionsInternal(celix_bundle_context_t *ctx, const celix_service_tracking_options_t *opts, bool async);
celix_status_t bundleContext_create(framework_pt framework, celix_framework_logger_t* logger, bundle_pt bundle, bundle_context_pt *bundle_context) {
celix_status_t status = CELIX_SUCCESS;
@@ -61,6 +67,7 @@
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->nextTrackerId = 1L;
*bundle_context = context;
@@ -77,18 +84,17 @@
celix_status_t status = CELIX_SUCCESS;
if (context != NULL) {
- celixThreadMutex_lock(&context->mutex);
+ 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_arrayList_size(context->svcRegistrations) == 0);
+ celix_arrayList_destroy(context->svcRegistrations);
+ hashMap_destroy(context->stoppingTrackerEventIds, false, false);
-
- bundleContext_cleanupBundleTrackers(context);
- bundleContext_cleanupServiceTrackers(context);
- bundleContext_cleanupServiceTrackerTrackers(context);
-
- //NOTE still present service registrations will be cleared during bundle stop in the
- //service registry (serviceRegistry_clearServiceRegistrations).
- celixThreadMutex_unlock(&context->mutex);
- celixThreadMutex_destroy(&context->mutex);
- arrayList_destroy(context->svcRegistrations);
+ celixThreadMutex_destroy(&context->mutex);
if (context->mng != NULL) {
celix_dependencyManager_removeAllComponents(context->mng);
@@ -106,6 +112,15 @@
return status;
}
+void celix_bundleContext_cleanup(celix_bundle_context_t *ctx) {
+ //NOTE not perfect, because stopping of registrations/tracker when the activator is destroyed can lead to segfault.
+ //but at least we can try to warn the bundle implementer that some cleanup is missing.
+ bundleContext_cleanupBundleTrackers(ctx);
+ bundleContext_cleanupServiceTrackers(ctx);
+ bundleContext_cleanupServiceTrackerTrackers(ctx);
+ bundleContext_cleanupServiceRegistration(ctx);
+}
+
celix_status_t bundleContext_getBundle(bundle_context_pt context, bundle_pt *out) {
celix_status_t status = CELIX_SUCCESS;
celix_bundle_t *bnd = celix_bundleContext_getBundle(context);
@@ -270,7 +285,9 @@
status = CELIX_ILLEGAL_ARGUMENT;
}
- framework_logIfError(context->framework->logger, status, NULL, "Failed to get service");
+ if (status != CELIX_SUCCESS) {
+ fw_log(context->framework->logger, CELIX_LOG_LEVEL_ERROR, "Failed to get service");
+ }
return status;
}
@@ -431,6 +448,13 @@
**********************************************************************************************************************
**********************************************************************************************************************/
+long celix_bundleContext_registerServiceAsync(celix_bundle_context_t *ctx, void *svc, const char *serviceName, celix_properties_t *properties) {
+ celix_service_registration_options_t opts = CELIX_EMPTY_SERVICE_REGISTRATION_OPTIONS;
+ opts.svc = svc;
+ opts.serviceName = serviceName;
+ opts.properties = properties;
+ return celix_bundleContext_registerServiceWithOptionsAsync(ctx, &opts);
+}
long celix_bundleContext_registerService(bundle_context_t *ctx, void *svc, const char *serviceName, celix_properties_t *properties) {
celix_service_registration_options_t opts = CELIX_EMPTY_SERVICE_REGISTRATION_OPTIONS;
@@ -441,6 +465,14 @@
}
+long celix_bundleContext_registerServiceFactoryAsync(celix_bundle_context_t *ctx, celix_service_factory_t *factory, const char *serviceName, celix_properties_t *props) {
+ celix_service_registration_options_t opts = CELIX_EMPTY_SERVICE_REGISTRATION_OPTIONS;
+ opts.factory = factory;
+ opts.serviceName = serviceName;
+ opts.properties = props;
+ return celix_bundleContext_registerServiceWithOptionsAsync(ctx, &opts);
+}
+
long celix_bundleContext_registerServiceFactory(celix_bundle_context_t *ctx, celix_service_factory_t *factory, const char *serviceName, celix_properties_t *props) {
celix_service_registration_options_t opts = CELIX_EMPTY_SERVICE_REGISTRATION_OPTIONS;
opts.factory = factory;
@@ -449,9 +481,19 @@
return celix_bundleContext_registerServiceWithOptions(ctx, &opts);
}
-long celix_bundleContext_registerServiceWithOptions(bundle_context_t *ctx, const celix_service_registration_options_t *opts) {
- long svcId = -1;
- service_registration_t *reg = NULL;
+static long celix_bundleContext_registerServiceWithOptionsInternal(bundle_context_t *ctx, const celix_service_registration_options_t *opts, bool async) {
+ bool valid = opts->serviceName != NULL && strncmp("", opts->serviceName, 1) != 0;
+ if (!valid) {
+ fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Required serviceName argument is NULL or empty");
+ return -1;
+ }
+ valid = opts->svc != NULL || opts->factory != NULL;
+ if (!valid) {
+ fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Required svc or factory argument is NULL");
+ return -1;
+ }
+
+ //set properties
celix_properties_t *props = opts->properties;
if (props == NULL) {
props = celix_properties_create();
@@ -461,52 +503,113 @@
}
const char *lang = opts->serviceLanguage != NULL && strncmp("", opts->serviceLanguage, 1) != 0 ? opts->serviceLanguage : CELIX_FRAMEWORK_SERVICE_C_LANGUAGE;
celix_properties_set(props, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang);
- if (opts->serviceName != NULL && strncmp("", opts->serviceName, 1) != 0) {
- if (opts->factory != NULL) {
- reg = celix_framework_registerServiceFactory(ctx->framework, ctx->bundle, opts->serviceName, opts->factory, props);
- } else {
- bundleContext_registerService(ctx, opts->serviceName, opts->svc, props, ®);
- }
- svcId = serviceRegistration_getServiceId(reg); //save to call with NULL
+
+ long svcId = -1;
+ if (!async && celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) {
+ /*
+ * Note already on event loop, cannot register the service async, because we cannot wait a future event (the
+ * service registration) the event loop.
+ *
+ * So in this case we handle the service registration the "traditional way" and call the sync fw service
+ * registrations versions on the event loop thread
+ */
+
+ svcId = celix_framework_registerService(ctx->framework, ctx->bundle, opts->serviceName, opts->svc, opts->factory, props);
} else {
- framework_logIfError(ctx->framework->logger, CELIX_ILLEGAL_ARGUMENT, NULL, "Required serviceName argument is NULL");
+ void (*asyncCallback)(void *data, long serviceId) = async ? opts->asyncCallback : NULL; //NOTE for not async call do not use the callback.
+ svcId = celix_framework_registerServiceAsync(ctx->framework, ctx->bundle, opts->serviceName, opts->svc, opts->factory, props, opts->asyncData, asyncCallback, NULL, NULL);
+ if (!async && svcId >= 0) {
+ //note on event loop thread, but in a sync call, so waiting till service registration is concluded
+ celix_bundleContext_waitForAsyncRegistration(ctx, svcId);
+ }
}
+
+
if (svcId < 0) {
properties_destroy(props);
} else {
celixThreadMutex_lock(&ctx->mutex);
- arrayList_add(ctx->svcRegistrations, reg);
+ celix_arrayList_addLong(ctx->svcRegistrations, svcId);
celixThreadMutex_unlock(&ctx->mutex);
}
return svcId;
}
-void celix_bundleContext_unregisterService(bundle_context_t *ctx, long serviceId) {
- service_registration_t *found = NULL;
+long celix_bundleContext_registerServiceWithOptions(bundle_context_t *ctx, const celix_service_registration_options_t *opts) {
+ return celix_bundleContext_registerServiceWithOptionsInternal(ctx, opts, false);
+}
+
+long celix_bundleContext_registerServiceWithOptionsAsync(celix_bundle_context_t *ctx, const celix_service_registration_options_t *opts) {
+ return celix_bundleContext_registerServiceWithOptionsInternal(ctx, opts, true);
+}
+
+void celix_bundleContext_waitForAsyncRegistration(celix_bundle_context_t* ctx, long serviceId) {
+ if (serviceId >= 0) {
+ celix_framework_waitForAsyncRegistration(ctx->framework, serviceId);
+ }
+}
+
+bool celix_bundleContext_isServiceRegistered(celix_bundle_context_t* ctx, long serviceId) {
+ return celix_serviceRegistry_isServiceRegistered(ctx->framework->registry, serviceId);
+}
+
+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);
- unsigned int size = arrayList_size(ctx->svcRegistrations);
- for (unsigned int i = 0; i < size; ++i) {
- service_registration_t *reg = arrayList_get(ctx->svcRegistrations, i);
- if (reg != NULL) {
- long svcId = serviceRegistration_getServiceId(reg);
- if (svcId == serviceId) {
- found = reg;
- arrayList_remove(ctx->svcRegistrations, i);
- break;
- }
+ int size = celix_arrayList_size(ctx->svcRegistrations);
+ for (int i = 0; i < size; ++i) {
+ long entryId = celix_arrayList_getLong(ctx->svcRegistrations, i);
+ if (entryId == serviceId) {
+ celix_arrayList_removeAt(ctx->svcRegistrations, i);
+ found = entryId;
+ break;
}
}
celixThreadMutex_unlock(&ctx->mutex);
- if (found != NULL) {
- serviceRegistration_unregister(found);
+ if (found >= 0) {
+ if (async) {
+ celix_framework_unregisterAsync(ctx->framework, ctx->bundle, found, data, done);
+ } else if (celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) {
+ /*
+ * sync unregistration.
+ * Note already on event loop, cannot unregister the service async, because we cannot wait a future event (the
+ * service unregistration) the event loop.
+ *
+ * So in this case we handle the service unregistration the "traditional way" and call the sync fw service
+ * unregistrations versions
+ */
+ celix_framework_unregister(ctx->framework, ctx->bundle, found);
+ if (done != NULL) {
+ done(data);
+ }
+ } else {
+ celix_framework_unregisterAsync(ctx->framework, ctx->bundle, found, data, done);
+ celix_bundleContext_waitForAsyncUnregistration(ctx, serviceId);
+ }
} else {
- framework_logIfError(ctx->framework->logger, CELIX_ILLEGAL_ARGUMENT, NULL, "No service registered with svc id %li for bundle %s (bundle id: %li)!", serviceId, celix_bundle_getSymbolicName(ctx->bundle), celix_bundle_getId(ctx->bundle));
+ framework_logIfError(ctx->framework->logger, CELIX_ILLEGAL_ARGUMENT, NULL,
+ "No service registered with svc id %li for bundle %s (bundle id: %li)!", serviceId,
+ celix_bundle_getSymbolicName(ctx->bundle), celix_bundle_getId(ctx->bundle));
}
}
}
+void celix_bundleContext_unregisterServiceAsync(celix_bundle_context_t *ctx, long serviceId, void *data, void (*done)(void*)) {
+ return celix_bundleContext_unregisterServiceInternal(ctx, serviceId, true, data, done);
+}
+
+void celix_bundleContext_unregisterService(bundle_context_t *ctx, long serviceId) {
+ return celix_bundleContext_unregisterServiceInternal(ctx, serviceId, false, NULL, NULL);
+}
+
+void celix_bundleContext_waitForAsyncUnregistration(celix_bundle_context_t* ctx, long serviceId) {
+ if (serviceId >= 0) {
+ celix_framework_waitForAsyncUnregistration(ctx->framework, serviceId);
+ }
+}
+
celix_dependency_manager_t* celix_bundleContext_getDependencyManager(bundle_context_t *ctx) {
celix_dependency_manager_t* result = NULL;
if (ctx != NULL) {
@@ -555,25 +658,63 @@
return status;
}
-long celix_bundleContext_trackBundlesWithOptions(
+void celix_bundleContext_trackBundlesWithOptionsCallback(void *data) {
+ celix_bundle_context_bundle_tracker_entry_t* entry = data;
+ assert(celix_framework_isCurrentThreadTheEventLoop(entry->ctx->framework));
+ fw_addBundleListener(entry->ctx->framework, entry->ctx->bundle, &entry->listener);
+}
+
+static long celix_bundleContext_trackBundlesWithOptionsInternal(
bundle_context_t* ctx,
- const celix_bundle_tracking_options_t *opts) {
+ const celix_bundle_tracking_options_t *opts,
+ bool async) {
long trackerId = -1;
celix_bundle_context_bundle_tracker_entry_t *entry = calloc(1, sizeof(*entry));
memcpy(&entry->opts, opts, sizeof(*opts));
entry->ctx = ctx;
+ entry->createEventId = celix_framework_nextEventId(ctx->framework);
entry->listener.handle = entry;
entry->listener.bundleChanged = bundleContext_bundleChanged;
- fw_addBundleListener(ctx->framework, ctx->bundle, &entry->listener);
celixThreadMutex_lock(&ctx->mutex);
entry->trackerId = ctx->nextTrackerId++;
- hashMap_put(ctx->bundleTrackers, (void*)entry->trackerId, entry);
- celixThreadMutex_unlock(&ctx->mutex);
+ hashMap_put(ctx->bundleTrackers, (void*)(entry->trackerId), entry);
trackerId = entry->trackerId;
+ celixThreadMutex_unlock(&ctx->mutex);
+
+ void (*trackerCreatedCallback)(void *trackerCreatedCallbackData) = NULL;
+ if (async) { //note only using the async callback if this is a async call.
+ trackerCreatedCallback = opts->trackerCreatedCallback;
+ }
+ long id = celix_framework_fireGenericEvent(
+ ctx->framework,
+ entry->createEventId,
+ celix_bundle_getId(ctx->bundle),
+ "add bundle listener",
+ entry,
+ celix_bundleContext_trackBundlesWithOptionsCallback,
+ opts->trackerCreatedCallbackData,
+ trackerCreatedCallback);
+
+ if (!async) {
+ celix_framework_waitForGenericEvent(ctx->framework, id);
+ }
+
return trackerId;
}
+long celix_bundleContext_trackBundlesWithOptions(
+ bundle_context_t* ctx,
+ const celix_bundle_tracking_options_t *opts) {
+ return celix_bundleContext_trackBundlesWithOptionsInternal(ctx, opts, false);
+}
+
+long celix_bundleContext_trackBundlesWithOptionsAsync(
+ celix_bundle_context_t* ctx,
+ const celix_bundle_tracking_options_t *opts) {
+ return celix_bundleContext_trackBundlesWithOptionsInternal(ctx, opts, true);
+}
+
long celix_bundleContext_trackBundles(
bundle_context_t* ctx,
void* callbackHandle,
@@ -587,6 +728,18 @@
return celix_bundleContext_trackBundlesWithOptions(ctx, &opts);
}
+long celix_bundleContext_trackBundlesAsync(
+ celix_bundle_context_t* ctx,
+ void* callbackHandle,
+ void (*onStarted)(void* handle, const celix_bundle_t *bundle),
+ void (*onStopped)(void *handle, const celix_bundle_t *bundle)) {
+ celix_bundle_tracking_options_t opts;
+ memset(&opts, 0, sizeof(opts));
+ opts.callbackHandle = callbackHandle;
+ opts.onStarted = onStarted;
+ opts.onStopped = onStopped;
+ return celix_bundleContext_trackBundlesWithOptionsAsync(ctx, &opts);
+}
void celix_bundleContext_useBundles(
bundle_context_t *ctx,
@@ -604,76 +757,293 @@
}
static void bundleContext_cleanupBundleTrackers(bundle_context_t *ctx) {
+ module_pt module;
+ 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)) {
- celix_bundle_context_bundle_tracker_entry_t *tracker = hashMapIterator_nextValue(&iter);
- fw_removeBundleListener(ctx->framework, ctx->bundle, &tracker->listener);
- free(tracker);
+ 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) {
+ danglingTrkIds = celix_arrayList_create();
+ }
+ celix_arrayList_addLong(danglingTrkIds, trkId);
}
- hashMap_destroy(ctx->bundleTrackers, false, false);
+ celixThreadMutex_unlock(&ctx->mutex);
+
+ if (danglingTrkIds != NULL) {
+ for (int i = 0; i < celix_arrayList_size(danglingTrkIds); ++i) {
+ long trkId = celix_arrayList_getLong(danglingTrkIds, i);
+ celix_bundleContext_stopTracker(ctx, trkId);
+ }
+ celix_arrayList_destroy(danglingTrkIds);
+ }
}
static void bundleContext_cleanupServiceTrackers(bundle_context_t *ctx) {
- if(hashMap_size(ctx->serviceTrackers) > 0) {
- module_pt module;
- const char *symbolicName;
- bundle_getCurrentModule(ctx->bundle, &module);
- module_getSymbolicName(module, &symbolicName);
- fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_WARNING, "Dangling service tracker(s) for bundle %s.", symbolicName);
+ module_pt module;
+ 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);
+ if (danglingTrkIds == NULL) {
+ danglingTrkIds = celix_arrayList_create();
+ }
+ celix_arrayList_addLong(danglingTrkIds, trkId);
}
- hashMap_destroy(ctx->serviceTrackers, false, false);
+ celixThreadMutex_unlock(&ctx->mutex);
+
+ if (danglingTrkIds != NULL) {
+ for (int i = 0; i < celix_arrayList_size(danglingTrkIds); ++i) {
+ long trkId = celix_arrayList_getLong(danglingTrkIds, i);
+ celix_bundleContext_stopTracker(ctx, trkId);
+ }
+ celix_arrayList_destroy(danglingTrkIds);
+ }
}
static void bundleContext_cleanupServiceTrackerTrackers(bundle_context_t *ctx) {
+ module_pt module;
+ 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)) {
- celix_bundle_context_service_tracker_tracker_entry_t *entry = hashMapIterator_nextValue(&iter);
- serviceRegistration_unregister(entry->hookReg);
- free(entry);
+ 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);
+ if (danglingTrkIds == NULL) {
+ danglingTrkIds = celix_arrayList_create();
+ }
+ celix_arrayList_addLong(danglingTrkIds, trkId);
}
- hashMap_destroy(ctx->metaTrackers, false, false);
+ celixThreadMutex_unlock(&ctx->mutex);
+
+ if (danglingTrkIds != NULL) {
+ for (int i = 0; i < celix_arrayList_size(danglingTrkIds); ++i) {
+ long trkId = celix_arrayList_getLong(danglingTrkIds, i);
+ celix_bundleContext_stopTracker(ctx, trkId);
+ }
+ celix_arrayList_destroy(danglingTrkIds);
+ }
}
+static void bundleContext_cleanupServiceRegistration(bundle_context_t* ctx) {
+ module_pt module;
+ const char *symbolicName;
+ bundle_getCurrentModule(ctx->bundle, &module);
+ module_getSymbolicName(module, &symbolicName);
-void celix_bundleContext_stopTracker(bundle_context_t *ctx, long trackerId) {
- if (ctx != NULL && trackerId >0) {
- bool found = false;
- celix_bundle_context_bundle_tracker_entry_t *bundleTracker = NULL;
- service_tracker_t *serviceTracker = NULL;
- celix_bundle_context_service_tracker_tracker_entry_t *svcTrackerTracker = NULL;
+ celix_array_list_t* danglingSvcIds = NULL;
- celixThreadMutex_lock(&ctx->mutex);
- if (hashMap_containsKey(ctx->bundleTrackers, (void*)trackerId)) {
- found = true;
- bundleTracker = hashMap_remove(ctx->bundleTrackers, (void*)trackerId);
- } else if (hashMap_containsKey(ctx->serviceTrackers, (void*)trackerId)) {
- found = true;
- serviceTracker = hashMap_remove(ctx->serviceTrackers, (void*)trackerId);
- } else if (hashMap_containsKey(ctx->metaTrackers, (void*)trackerId)) {
- found = true;
- svcTrackerTracker = hashMap_remove(ctx->metaTrackers, (void*)trackerId);
+ celixThreadMutex_lock(&ctx->mutex);
+ for (int i = 0; i < celix_arrayList_size(ctx->svcRegistrations); ++i) {
+ service_registration_pt reg = celix_arrayList_get(ctx->svcRegistrations, i);
+ long svcId = serviceRegistration_getServiceId(reg);
+ const char* svcName = NULL;
+ serviceRegistration_getServiceName(reg, &svcName);
+ fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Dangling service registration with svcId %li, for bundle %s and with service name %s. Add missing 'celix_bundleContext_unregisterService' calls.", svcId, symbolicName, svcName);
+ if (danglingSvcIds == NULL) {
+ danglingSvcIds = celix_arrayList_create();
}
- celixThreadMutex_unlock(&ctx->mutex);
+ celix_arrayList_addLong(danglingSvcIds, svcId);
+ }
+ celixThreadMutex_unlock(&ctx->mutex);
+
+ if (danglingSvcIds != NULL) {
+ for (int i = 0; i < celix_arrayList_size(danglingSvcIds); ++i) {
+ long svcId = celix_arrayList_getLong(danglingSvcIds, i);
+ celix_bundleContext_unregisterService(ctx, svcId);
+ }
+ celix_arrayList_destroy(danglingSvcIds);
+ }
+}
+
+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);
+ 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);
+ free(tracker);
+}
+
+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);
+ free(tracker->serviceName);
+ free(tracker);
+}
+
+static void celix_bundleContext_stopTrackerInternal(bundle_context_t *ctx, long trackerId, bool async, void *doneData, void (*doneCallback)(void* doneData)) {
+ if (ctx == NULL || trackerId <= 0) {
+ return;
+ }
+
+ bool found = false;
+ celix_bundle_context_bundle_tracker_entry_t *bundleTracker = NULL;
+ celix_bundle_context_service_tracker_entry_t *serviceTracker = NULL;
+ celix_bundle_context_service_tracker_tracker_entry_t *svcTrackerTracker = NULL;
+
+ celixThreadMutex_lock(&ctx->mutex);
+
+ if (hashMap_containsKey(ctx->bundleTrackers, (void *) trackerId)) {
+ found = true;
+ bundleTracker = hashMap_remove(ctx->bundleTrackers, (void *) trackerId);
+ } else if (hashMap_containsKey(ctx->serviceTrackers, (void *) trackerId)) {
+ found = true;
+ serviceTracker = hashMap_remove(ctx->serviceTrackers, (void *) trackerId);
+ } else if (hashMap_containsKey(ctx->metaTrackers, (void *) trackerId)) {
+ found = true;
+ svcTrackerTracker = hashMap_remove(ctx->metaTrackers, (void *) trackerId);
+ }
+
+ 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
if (bundleTracker != NULL) {
fw_removeBundleListener(ctx->framework, ctx->bundle, &bundleTracker->listener);
free(bundleTracker);
- }
- if (serviceTracker != NULL) {
- celix_serviceTracker_destroy(serviceTracker);
- }
- if (svcTrackerTracker != NULL) {
- serviceRegistration_unregister(svcTrackerTracker->hookReg);
+ } else if (serviceTracker != NULL) {
+ celix_serviceTracker_destroy(serviceTracker->tracker);
+ free(serviceTracker);
+ } else if (svcTrackerTracker != NULL) {
+ celix_framework_unregister(ctx->framework, ctx->bundle, svcTrackerTracker->serviceId);
free(svcTrackerTracker->serviceName);
free(svcTrackerTracker);
}
- if (!found) {
- framework_logIfError(ctx->framework->logger, CELIX_ILLEGAL_ARGUMENT, NULL, "No tracker with id %li found'", trackerId);
+ if (doneCallback != NULL) {
+ doneCallback(doneData);
+ }
+ } 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);
+
+ if (bundleTracker != NULL) {
+ celix_framework_fireGenericEvent(ctx->framework, eventId, celix_bundle_getId(ctx->bundle), "stop tracker", bundleTracker, celix_bundleContext_removeBundleTracker, doneData, doneCallback);
+ } else if (serviceTracker != NULL) {
+ celix_framework_fireGenericEvent(ctx->framework, eventId, celix_bundle_getId(ctx->bundle), "stop tracker", serviceTracker, celix_bundleContext_removeServiceTracker, doneData, doneCallback);
+ } else if (svcTrackerTracker != NULL) {
+ celix_framework_fireGenericEvent(ctx->framework, eventId, celix_bundle_getId(ctx->bundle), "stop meta tracker", svcTrackerTracker, celix_bundleContext_removeServiceTrackerTracker, doneData, doneCallback);
+ } else {
+ fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Unexpected else branch");
+ }
+ celixThreadMutex_unlock(&ctx->mutex);
+ } else if (found) { /*sync, so waiting for events*/
+ long eventId = -1L;
+ if (bundleTracker != NULL) {
+ eventId = celix_framework_fireGenericEvent(ctx->framework, -1L, celix_bundle_getId(ctx->bundle), "stop tracker", bundleTracker, celix_bundleContext_removeBundleTracker, doneData, doneCallback);
+ } else if (serviceTracker != NULL) {
+ eventId = celix_framework_fireGenericEvent(ctx->framework, -1L, celix_bundle_getId(ctx->bundle), "stop tracker", serviceTracker, celix_bundleContext_removeServiceTracker, doneData, doneCallback);
+ } else if (svcTrackerTracker != NULL) {
+ eventId = celix_framework_fireGenericEvent(ctx->framework, -1L, celix_bundle_getId(ctx->bundle), "stop meta tracker", svcTrackerTracker, celix_bundleContext_removeServiceTrackerTracker, doneData, doneCallback);
+ } else {
+ fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Unexpected else branch");
+ }
+ celixThreadMutex_unlock(&ctx->mutex);
+ celix_framework_waitForGenericEvent(ctx->framework, eventId);
+ } else {
+ celixThreadMutex_unlock(&ctx->mutex);
+ fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "No tracker with id %li found'", trackerId);
+ }
+}
+
+void celix_bundleContext_stopTracker(bundle_context_t *ctx, long trackerId) {
+ return celix_bundleContext_stopTrackerInternal(ctx, trackerId, false, NULL, NULL);
+}
+
+void celix_bundleContext_stopTrackerAsync(celix_bundle_context_t *ctx, long trackerId, void *doneData, void (*doneCallback)(void* doneData)) {
+ return celix_bundleContext_stopTrackerInternal(ctx, trackerId, true, doneData, doneCallback);
+}
+
+static void celix_bundleContext_waitForTrackerInternal(celix_bundle_context_t* ctx, long trackerId, bool waitForStart) {
+ if (ctx == NULL || trackerId < 0) {
+ //silent ignore
+ return;
+ }
+
+
+ bool found = false;
+ long eventId = -1;
+ long svcId = -1;
+
+ if (waitForStart) {
+ celixThreadMutex_lock(&ctx->mutex);
+ if (hashMap_containsKey(ctx->bundleTrackers, (void *) trackerId)) {
+ found = true;
+ celix_bundle_context_bundle_tracker_entry_t* bundleTracker = hashMap_get(ctx->bundleTrackers, (void *) trackerId);
+ eventId = bundleTracker->createEventId;
+ } else if (hashMap_containsKey(ctx->serviceTrackers, (void *) trackerId)) {
+ found = true;
+ celix_bundle_context_service_tracker_entry_t* serviceTracker = hashMap_get(ctx->serviceTrackers, (void *) trackerId);
+ eventId = serviceTracker->createEventId;
+ } else if (hashMap_containsKey(ctx->metaTrackers, (void *) trackerId)) {
+ found = true;
+ celix_bundle_context_service_tracker_tracker_entry_t* svcTrackerTracker = hashMap_get(ctx->metaTrackers, (void *) trackerId);
+ svcId = svcTrackerTracker->serviceId;
+ }
+ celixThreadMutex_unlock(&ctx->mutex);
+ } else {
+ celixThreadMutex_lock(&ctx->mutex);
+ if (hashMap_containsKey(ctx->stoppingTrackerEventIds, (void*)trackerId)) {
+ found = true;
+ eventId = (long)hashMap_get(ctx->stoppingTrackerEventIds, (void*)trackerId);
+ }
+ celixThreadMutex_unlock(&ctx->mutex);
+ }
+
+ if (found) {
+ if (svcId >= 0 && waitForStart) {
+ celix_framework_waitForAsyncRegistration(ctx->framework, svcId);
+ } else if (svcId >= 0) {
+ celix_framework_waitForAsyncUnregistration(ctx->framework, svcId);
+ } else {
+ celix_framework_waitForGenericEvent(ctx->framework, eventId);
}
}
}
+
+void celix_bundleContext_waitForAsyncTracker(celix_bundle_context_t* ctx, long trackerId) {
+ return celix_bundleContext_waitForTrackerInternal(ctx, trackerId, true);
+}
+
+void celix_bundleContext_waitForAsyncStopTracker(celix_bundle_context_t* ctx, long trackerId) {
+ return celix_bundleContext_waitForTrackerInternal(ctx, trackerId, false);
+}
+
long celix_bundleContext_installBundle(bundle_context_t *ctx, const char *bundleLoc, bool autoStart) {
return celix_framework_installBundle(ctx->framework, bundleLoc, autoStart);
}
@@ -756,58 +1126,154 @@
return celix_bundleContext_useServicesWithOptions(ctx, &opts);
}
+typedef struct celix_bundle_context_use_service_data {
+ celix_bundle_context_t* ctx;
+ celix_service_use_options_t opts;
+
+ celix_thread_mutex_t mutex; //protects below
+ bool called;
+ size_t count;
+ 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.serviceName = d->opts.filter.serviceName;
+ trkOpts.filter = d->opts.filter;
+ trkOpts.filter.versionRange = d->opts.filter.versionRange;
+ trkOpts.filter.serviceLanguage = d->opts.filter.serviceLanguage;
+
+ celixThreadMutex_lock(&d->mutex);
+ d->svcTracker = celix_serviceTracker_createWithOptions(d->ctx, &trkOpts);
+ celixThreadMutex_unlock(&d->mutex);
+}
+
+static void celix_bundleContext_useServiceWithOptions_2_UseService(void *data) {
+ celix_bundle_context_use_service_data_t* d = data;
+ assert(celix_framework_isCurrentThreadTheEventLoop(d->ctx->framework));
+
+ celixThreadMutex_lock(&d->mutex);
+ celix_service_tracker_t *tracker = d->svcTracker;
+ d->svcTracker = NULL;
+ celixThreadMutex_unlock(&d->mutex);
+
+ bool called = celix_serviceTracker_useHighestRankingService(tracker, d->opts.filter.serviceName, d->opts.callbackHandle, d->opts.use, d->opts.useWithProperties, d->opts.useWithOwner);
+ celix_serviceTracker_destroy(tracker);
+
+ celixThreadMutex_lock(&d->mutex);
+ d->called = called;
+ celixThreadMutex_unlock(&d->mutex);
+}
bool celix_bundleContext_useServiceWithOptions(
celix_bundle_context_t *ctx,
const celix_service_use_options_t *opts) {
- bool called = false;
- celix_service_tracking_options_t trkOpts;
- memset(&trkOpts, 0, sizeof(trkOpts));
-
- if (opts != NULL) {
- trkOpts.filter.serviceName = opts->filter.serviceName;
- trkOpts.filter = opts->filter;
- trkOpts.filter.versionRange = opts->filter.versionRange;
- trkOpts.filter.serviceLanguage = opts->filter.serviceLanguage;
-
- service_tracker_t *trk = celix_serviceTracker_createWithOptions(ctx, &trkOpts);
- if (trk != NULL) {
- if (opts->use != NULL) {
-
- }
- if (opts->useWithProperties != NULL) {
-
- }
- if (opts->useWithOwner != NULL) {
-
- }
- called = celix_serviceTracker_useHighestRankingService(trk, opts->filter.serviceName, opts->waitTimeoutInSeconds, opts->callbackHandle, opts->use, opts->useWithProperties, opts->useWithOwner);
- celix_serviceTracker_destroy(trk);
- }
+ if (opts == NULL) {
+ return false;
}
+
+ celix_bundle_context_use_service_data_t data = {0};
+ data.ctx = ctx;
+ data.opts = *opts;
+ celixThreadMutex_create(&data.mutex, NULL);
+
+ struct timespec startTime = celix_gettime(CLOCK_MONOTONIC);
+
+ bool useServiceIsDone = false;
+ do {
+ if (celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) {
+ celix_bundleContext_useServiceWithOptions_1_CreateServiceTracker(&data);
+ celix_bundleContext_useServiceWithOptions_2_UseService(&data);
+ } else {
+ long eventId = celix_framework_fireGenericEvent(ctx->framework, -1, celix_bundle_getId(ctx->bundle), "create service tracker", &data, celix_bundleContext_useServiceWithOptions_1_CreateServiceTracker, NULL, NULL);
+ celix_framework_waitForGenericEvent(ctx->framework, eventId);
+
+ eventId = celix_framework_fireGenericEvent(ctx->framework, -1, celix_bundle_getId(ctx->bundle), "use service", &data, celix_bundleContext_useServiceWithOptions_2_UseService, NULL, NULL);
+ celix_framework_waitForGenericEvent(ctx->framework, eventId);
+ }
+
+ bool timeoutNotUsed = opts->waitTimeoutInSeconds == 0;
+ bool timeoutExpired = celix_elapsedtime(CLOCK_MONOTONIC, startTime) > opts->waitTimeoutInSeconds;
+ celixThreadMutex_lock(&data.mutex);
+ bool called = data.called;
+ celixThreadMutex_unlock(&data.mutex);
+
+ useServiceIsDone = timeoutNotUsed || timeoutExpired || called;
+ if (!useServiceIsDone) {
+ usleep(10);
+ }
+ } while (!useServiceIsDone);
+
+ celixThreadMutex_lock(&data.mutex);
+ bool called = data.called;
+ celixThreadMutex_unlock(&data.mutex);
+ celixThreadMutex_destroy(&data.mutex);
+
return called;
}
+static void celix_bundleContext_useServicesWithOptions_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.serviceName = d->opts.filter.serviceName;
+ trkOpts.filter = d->opts.filter;
+ trkOpts.filter.versionRange = d->opts.filter.versionRange;
+ trkOpts.filter.serviceLanguage = d->opts.filter.serviceLanguage;
+
+ celixThreadMutex_lock(&d->mutex);
+ d->svcTracker = celix_serviceTracker_createWithOptions(d->ctx, &trkOpts);
+ celixThreadMutex_unlock(&d->mutex);
+}
+
+static void celix_bundleContext_useServicesWithOptions_2_UseServices(void *data) {
+ celix_bundle_context_use_service_data_t* d = data;
+ assert(celix_framework_isCurrentThreadTheEventLoop(d->ctx->framework));
+
+ celixThreadMutex_lock(&d->mutex);
+ celix_service_tracker_t *tracker = d->svcTracker;
+ d->svcTracker = NULL;
+ celixThreadMutex_unlock(&d->mutex);
+
+ size_t count = celix_serviceTracker_useServices(tracker, d->opts.filter.serviceName, d->opts.callbackHandle, d->opts.use, d->opts.useWithProperties, d->opts.useWithOwner);
+ celix_serviceTracker_destroy(tracker);
+
+ celixThreadMutex_lock(&d->mutex);
+ d->count = count;
+ celixThreadMutex_unlock(&d->mutex);
+}
size_t celix_bundleContext_useServicesWithOptions(
celix_bundle_context_t *ctx,
const celix_service_use_options_t *opts) {
- size_t count = 0;
- celix_service_tracking_options_t trkOpts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS;
- if (opts != NULL) {
- trkOpts.filter.serviceName = opts->filter.serviceName;
- trkOpts.filter.filter = opts->filter.filter;
- trkOpts.filter.versionRange = opts->filter.versionRange;
- trkOpts.filter.serviceLanguage = opts->filter.serviceLanguage;
- trkOpts.filter.ignoreServiceLanguage = opts->filter.ignoreServiceLanguage;
-
- service_tracker_t *trk = celix_serviceTracker_createWithOptions(ctx, &trkOpts);
- if (trk != NULL) {
- count = celix_serviceTracker_useServices(trk, opts->filter.serviceName, opts->callbackHandle, opts->use, opts->useWithProperties, opts->useWithOwner);
- celix_serviceTracker_destroy(trk);
- }
+ if (opts == 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_useServicesWithOptions_1_CreateServiceTracker(&data);
+ celix_bundleContext_useServicesWithOptions_2_UseServices(&data);
+ } else {
+ long eventId = celix_framework_fireGenericEvent(ctx->framework, -1, celix_bundle_getId(ctx->bundle), "create service tracker", &data, celix_bundleContext_useServicesWithOptions_1_CreateServiceTracker, NULL, NULL);
+ celix_framework_waitForGenericEvent(ctx->framework, eventId);
+
+ eventId = celix_framework_fireGenericEvent(ctx->framework, -1, celix_bundle_getId(ctx->bundle), "use service", &data, celix_bundleContext_useServicesWithOptions_2_UseServices, NULL, NULL);
+ celix_framework_waitForGenericEvent(ctx->framework, eventId);
+ }
+
+ celixThreadMutex_lock(&data.mutex);
+ size_t count = data.count;
+ celixThreadMutex_unlock(&data.mutex);
+ celixThreadMutex_destroy(&data.mutex);
+
return count;
}
@@ -821,7 +1287,19 @@
opts.filter.serviceName = serviceName;
opts.callbackHandle = callbackHandle;
opts.set = set;
- return celix_bundleContext_trackServicesWithOptions(ctx, &opts);
+ 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)) {
+ 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);
}
@@ -836,20 +1314,107 @@
opts.callbackHandle = callbackHandle;
opts.add = add;
opts.remove = remove;
- return celix_bundleContext_trackServicesWithOptions(ctx, &opts);
+ 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);
}
-long celix_bundleContext_trackServicesWithOptions(bundle_context_t *ctx, const celix_service_tracking_options_t *opts) {
- long trackerId = -1;
- celix_service_tracker_t *tracker = celix_serviceTracker_createWithOptions(ctx, opts);
+static void celix_bundleContext_createTrackerOnEventLoop(void *data) {
+ celix_bundle_context_service_tracker_entry_t* entry = data;
+ assert(celix_framework_isCurrentThreadTheEventLoop(entry->ctx->framework));
+ celix_service_tracker_t *tracker = celix_serviceTracker_createWithOptions(entry->ctx, &entry->opts);
if (tracker != NULL) {
- celixThreadMutex_lock(&ctx->mutex);
- trackerId = ctx->nextTrackerId++;
- hashMap_put(ctx->serviceTrackers, (void*)trackerId, tracker);
- celixThreadMutex_unlock(&ctx->mutex);
+ celixThreadMutex_lock(&entry->ctx->mutex);
+ entry->tracker = tracker;
+ celixThreadMutex_unlock(&entry->ctx->mutex);
+ } else {
+ fw_log(entry->ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Cannot create tracker for bnd %s (%li)", celix_bundle_getSymbolicName(entry->ctx->bundle), celix_bundle_getId(entry->ctx->bundle));
}
- return trackerId;
+}
+
+static void celix_bundleContext_doneCreatingTrackerOnEventLoop(void *data) {
+ celix_bundle_context_service_tracker_entry_t* entry = data;
+ if (entry->trackerCreatedCallback != NULL) {
+ entry->trackerCreatedCallback(entry->trackerCreatedCallbackData);
+ }
+}
+
+
+
+static long celix_bundleContext_trackServicesWithOptionsInternal(celix_bundle_context_t *ctx, const celix_service_tracking_options_t *opts, bool async) {
+ if (ctx == NULL) {
+ return -1L;
+ } else if (opts == NULL) {
+ fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Cannot track services with a NULL service tracking options argument");
+ return -1L;
+ }
+
+ if (opts->filter.serviceName == NULL) {
+ fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_DEBUG, "Starting a tracker for any services");
+ }
+
+ if (!async && celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) {
+ //already in event loop thread. To keep the old behavior just create the tracker traditionally (chained in the current thread).
+ celix_service_tracker_t *tracker = celix_serviceTracker_createWithOptions(ctx, opts);
+ long trackerId = -1L;
+ if (tracker != NULL) {
+ celix_bundle_context_service_tracker_entry_t* entry = calloc(1, sizeof(*entry));
+ entry->ctx = ctx;
+ entry->tracker = tracker;
+ entry->opts = *opts;
+ entry->createEventId = -1;
+ celixThreadMutex_lock(&ctx->mutex);
+ entry->trackerId = ctx->nextTrackerId++;
+ trackerId = entry->trackerId;
+ hashMap_put(ctx->serviceTrackers, (void *) trackerId, entry);
+ celixThreadMutex_unlock(&ctx->mutex);
+ }
+ return trackerId;
+ } else {
+ celix_bundle_context_service_tracker_entry_t* entry = calloc(1, sizeof(*entry));
+ entry->ctx = ctx;
+ entry->createEventId = celix_framework_nextEventId(ctx->framework);
+ entry->tracker = NULL; //will be set async
+ entry->opts = *opts;
+ if (async) { //note only setting the async callback if this is a async call
+ entry->trackerCreatedCallbackData = opts->trackerCreatedCallbackData;
+ entry->trackerCreatedCallback = opts->trackerCreatedCallback;
+ }
+ celixThreadMutex_lock(&ctx->mutex);
+ entry->trackerId = ctx->nextTrackerId++;
+ long trackerId = entry->trackerId;
+ hashMap_put(ctx->serviceTrackers, (void *)entry->trackerId, entry);
+ celixThreadMutex_unlock(&ctx->mutex);
+
+ long id = celix_framework_fireGenericEvent(ctx->framework, entry->createEventId, celix_bundle_getId(ctx->bundle), "create service tracker event", entry, celix_bundleContext_createTrackerOnEventLoop, entry, celix_bundleContext_doneCreatingTrackerOnEventLoop);
+
+ if (!async) {
+ celix_framework_waitForGenericEvent(ctx->framework, id);
+ }
+
+ return trackerId;
+ }
+}
+
+long celix_bundleContext_trackServicesWithOptions(celix_bundle_context_t *ctx, const celix_service_tracking_options_t *opts) {
+ return celix_bundleContext_trackServicesWithOptionsInternal(ctx, opts, false);
+}
+
+long celix_bundleContext_trackServicesWithOptionsAsync(celix_bundle_context_t *ctx, const celix_service_tracking_options_t *opts) {
+ return celix_bundleContext_trackServicesWithOptionsInternal(ctx, opts, true);
}
long celix_bundleContext_findService(celix_bundle_context_t *ctx, const char *serviceName) {
@@ -858,19 +1423,20 @@
return celix_bundleContext_findServiceWithOptions(ctx, &opts);
}
-static void bundleContext_retrieveSvcId(void *handle, void *svc __attribute__((unused)), const celix_properties_t *props) {
- long *svcId = handle;
- *svcId = celix_properties_getAsLong(props, OSGI_FRAMEWORK_SERVICE_ID, -1L);
-}
-
long celix_bundleContext_findServiceWithOptions(celix_bundle_context_t *ctx, const celix_service_filter_options_t *opts) {
- long svcId = -1L;
- celix_service_use_options_t useOpts = CELIX_EMPTY_SERVICE_USE_OPTIONS;
- memcpy(&useOpts.filter, opts, sizeof(useOpts.filter));
- useOpts.callbackHandle = &svcId;
- useOpts.useWithProperties = bundleContext_retrieveSvcId;
- celix_bundleContext_useServiceWithOptions(ctx, &useOpts);
- return svcId;
+ long result = -1L;
+ char* filter = celix_serviceRegistry_createFilterFor(ctx->framework->registry, opts->serviceName, opts->versionRange, opts->filter, opts->serviceLanguage, opts->ignoreServiceLanguage);
+ if (filter != NULL) {
+ celix_array_list_t *svcIds = celix_serviceRegisrty_findServices(ctx->framework->registry, filter);
+ if (svcIds != NULL && celix_arrayList_size(svcIds) > 0) {
+ result = celix_arrayList_getLong(svcIds, 0);
+ }
+ if (svcIds != NULL) {
+ celix_arrayList_destroy(svcIds);
+ }
+ free(filter);
+ }
+ return result;
}
@@ -880,22 +1446,14 @@
return celix_bundleContext_findServicesWithOptions(ctx, &opts);
}
-static void bundleContext_retrieveSvcIds(void *handle, void *svc __attribute__((unused)), const celix_properties_t *props) {
- celix_array_list_t *list = handle;
- if (list != NULL) {
- long svcId = celix_properties_getAsLong(props, OSGI_FRAMEWORK_SERVICE_ID, -1L);
- celix_arrayList_addLong(list, svcId);
- }
-}
-
celix_array_list_t* celix_bundleContext_findServicesWithOptions(celix_bundle_context_t *ctx, const celix_service_filter_options_t *opts) {
- celix_array_list_t* list = celix_arrayList_create();
- celix_service_use_options_t useOpts = CELIX_EMPTY_SERVICE_USE_OPTIONS;
- memcpy(&useOpts.filter, opts, sizeof(useOpts.filter));
- useOpts.callbackHandle = list;
- useOpts.useWithProperties = bundleContext_retrieveSvcIds;
- celix_bundleContext_useServicesWithOptions(ctx, &useOpts);
- return list;
+ celix_array_list_t* result = NULL;
+ char* filter = celix_serviceRegistry_createFilterFor(ctx->framework->registry, opts->serviceName, opts->versionRange, opts->filter, opts->serviceLanguage, opts->ignoreServiceLanguage);
+ if (filter != NULL) {
+ result = celix_serviceRegisrty_findServices(ctx->framework->registry, filter);
+ free(filter);
+ }
+ return result;
}
static celix_status_t bundleContext_callServicedTrackerTrackerCallback(void *handle, celix_array_list_t *listeners, bool add) {
@@ -915,9 +1473,11 @@
trkInfo.serviceLanguage = celix_filter_findAttribute(trkInfo.filter, CELIX_FRAMEWORK_SERVICE_LANGUAGE);
const char *filterSvcName = celix_filter_findAttribute(trkInfo.filter, OSGI_FRAMEWORK_OBJECTCLASS);
- if (add && entry->add != NULL && filterSvcName != NULL && strncmp(filterSvcName, entry->serviceName, 1024*1024) == 0) {
+ bool match = entry->serviceName == NULL || (filterSvcName != NULL && strncmp(filterSvcName, entry->serviceName, 1024*1024) == 0);
+
+ if (add && entry->add != NULL && match) {
entry->add(entry->callbackHandle, &trkInfo);
- } else if (entry->remove != NULL && filterSvcName != NULL && strncmp(filterSvcName, entry->serviceName, 1024*1024) == 0) {
+ } else if (!add && entry->remove != NULL && match) {
entry->remove(entry->callbackHandle, &trkInfo);
}
celix_filter_destroy(trkInfo.filter);
@@ -934,43 +1494,79 @@
return bundleContext_callServicedTrackerTrackerCallback(handle, listeners, false);
}
+long celix_bundleContext_trackServiceTrackersInternal(
+ celix_bundle_context_t *ctx,
+ const char *serviceName,
+ void *callbackHandle,
+ void (*trackerAdd)(void *handle, const celix_service_tracker_info_t *info),
+ void (*trackerRemove)(void *handle, const celix_service_tracker_info_t *info),
+ bool async,
+ void *doneData,
+ void (*doneCallback)(void*)) {
+ if (serviceName == NULL) {
+ fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_DEBUG, "Note starting a meta tracker for all services", __FUNCTION__);
+ }
+
+ celix_bundle_context_service_tracker_tracker_entry_t *entry = calloc(1, sizeof(*entry));
+ celixThreadMutex_lock(&ctx->mutex);
+ entry->trackerId = ctx->nextTrackerId++;
+ celixThreadMutex_unlock(&ctx->mutex);
+ entry->ctx = ctx;
+ entry->callbackHandle = callbackHandle;
+ entry->add = trackerAdd;
+ entry->remove = trackerRemove;
+ entry->serviceName = celix_utils_strdup(serviceName);
+ entry->hook.handle = entry;
+ entry->hook.added = bundleContext_callServicedTrackerTrackerAdd;
+ entry->hook.removed = bundleContext_callServicedTrackerTrackerRemove;
+
+ if (!async && celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) {
+ //already on event loop, registering the "traditional way" i.e. chaining on the current thread
+ service_registration_t* reg = NULL;
+ bundleContext_registerService(ctx, OSGI_FRAMEWORK_LISTENER_HOOK_SERVICE_NAME, &entry->hook, NULL, ®);
+ entry->serviceId = serviceRegistration_getServiceId(reg);
+ } else if (async) {
+ entry->serviceId = celix_framework_registerServiceAsync(ctx->framework, ctx->bundle, OSGI_FRAMEWORK_LISTENER_HOOK_SERVICE_NAME, &entry->hook, NULL, NULL, NULL, NULL, doneData, doneCallback);
+ } else {
+ entry->serviceId = celix_framework_registerServiceAsync(ctx->framework, ctx->bundle, OSGI_FRAMEWORK_LISTENER_HOOK_SERVICE_NAME, &entry->hook, NULL, NULL, NULL, NULL, doneData, doneCallback);
+ celix_framework_waitForAsyncRegistration(ctx->framework, entry->serviceId);
+ }
+
+ if (entry->serviceId >= 0) {
+ celixThreadMutex_lock(&ctx->mutex);
+ hashMap_put(ctx->metaTrackers, (void*)entry->trackerId, entry);
+ long trkId = entry->trackerId;
+ celixThreadMutex_unlock(&ctx->mutex);
+ return trkId;
+ } else {
+ framework_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, __FUNCTION__, __BASE_FILE__, __LINE__, "Error registering service listener hook for service tracker tracker\n");
+ free(entry);
+ return -1L;
+ }
+}
+
long celix_bundleContext_trackServiceTrackers(
celix_bundle_context_t *ctx,
const char *serviceName,
void *callbackHandle,
void (*trackerAdd)(void *handle, const celix_service_tracker_info_t *info),
void (*trackerRemove)(void *handle, const celix_service_tracker_info_t *info)) {
+ return celix_bundleContext_trackServiceTrackersInternal(ctx, serviceName, callbackHandle, trackerAdd, trackerRemove, false, NULL, NULL);
+}
- long trackerId = -1L;
+long celix_bundleContext_trackServiceTrackersAsync(
+ celix_bundle_context_t *ctx,
+ const char *serviceName,
+ void *callbackHandle,
+ void (*trackerAdd)(void *handle, const celix_service_tracker_info_t *info),
+ void (*trackerRemove)(void *handle, const celix_service_tracker_info_t *info),
+ void *doneCallbackData,
+ void (*doneCallback)(void* doneCallbackData)) {
+ return celix_bundleContext_trackServiceTrackersInternal(ctx, serviceName, callbackHandle, trackerAdd, trackerRemove, true, doneCallbackData, doneCallback);
+}
- if (serviceName == NULL) {
- fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Required serviceName not provided for function ", __FUNCTION__);
- return trackerId;
- }
-
- celix_bundle_context_service_tracker_tracker_entry_t *entry = calloc(1, sizeof(*entry));
-
- entry->callbackHandle = callbackHandle;
- entry->add = trackerAdd;
- entry->remove = trackerRemove;
- entry->serviceName = strndup(serviceName, 1024*1024);
-
- entry->hook.handle = entry;
- entry->hook.added = bundleContext_callServicedTrackerTrackerAdd;
- entry->hook.removed = bundleContext_callServicedTrackerTrackerRemove;
- bundleContext_registerService(ctx, OSGI_FRAMEWORK_LISTENER_HOOK_SERVICE_NAME, &entry->hook, NULL, &entry->hookReg);
-
- if (entry->hookReg != NULL) {
- celixThreadMutex_lock(&ctx->mutex);
- entry->trackerId = ctx->nextTrackerId++;
- hashMap_put(ctx->metaTrackers, (void*)entry->trackerId, entry);
- trackerId = entry->trackerId;
- celixThreadMutex_unlock(&ctx->mutex);
- } else {
- framework_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, __FUNCTION__, __BASE_FILE__, __LINE__, "Error registering service listener hook for service tracker tracker\n");
- free(entry);
- }
- return trackerId;
+void celix_bundleContext_waitForEvents(celix_bundle_context_t* ctx) {
+ celix_framework_waitUntilNoEventsForBnd(ctx->framework, celix_bundle_getId(ctx->bundle));
}
celix_bundle_t* celix_bundleContext_getBundle(const celix_bundle_context_t *ctx) {
@@ -1052,4 +1648,4 @@
char *name = NULL;
celix_framework_useBundle(ctx->framework, false, bndId, &name, celix_bundleContext_getBundleSymbolicNameCallback);
return name;
-}
\ No newline at end of file
+}
diff --git a/libs/framework/src/bundle_context_private.h b/libs/framework/src/bundle_context_private.h
index e3b7619..1b77bf5 100644
--- a/libs/framework/src/bundle_context_private.h
+++ b/libs/framework/src/bundle_context_private.h
@@ -25,24 +25,45 @@
#include "bundle_listener.h"
#include "celix_bundle_context.h"
#include "listener_hook_service.h"
+#include "service_tracker.h"
typedef struct celix_bundle_context_bundle_tracker_entry {
celix_bundle_context_t *ctx;
long trackerId;
bundle_listener_t listener;
celix_bundle_tracking_options_t opts;
+
+ //used for sync
+ long createEventId;
} celix_bundle_context_bundle_tracker_entry_t;
+typedef struct celix_bundle_context_service_tracker_entry {
+ celix_bundle_context_t *ctx;
+ long trackerId;
+ celix_service_tracking_options_t opts;
+ celix_service_tracker_t* tracker;
+ void *trackerCreatedCallbackData;
+ void (*trackerCreatedCallback)(void *trackerCreatedCallbackData);
+
+
+ //used for sync
+ long createEventId;
+} celix_bundle_context_service_tracker_entry_t;
+
typedef struct celix_bundle_context_service_tracker_tracker_entry {
+ celix_bundle_context_t* ctx;
long trackerId;
struct listener_hook_service hook;
- service_registration_t *hookReg;
+ 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);
+
+ //used for sync
+ long createEventId;
} celix_bundle_context_service_tracker_tracker_entry_t;
struct celix_bundle_context {
@@ -50,13 +71,17 @@
celix_bundle_t *bundle;
celix_thread_mutex_t mutex; //protects fields below (NOTE/FIXME also used by bundle.c for listing service tracker usage)
- array_list_t *svcRegistrations;
+ 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_service_tracker_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.
};
+void celix_bundleContext_cleanup(celix_bundle_context_t *ctx);
+
+
#endif /* BUNDLE_CONTEXT_PRIVATE_H_ */
diff --git a/libs/framework/src/celix_framework_factory.c b/libs/framework/src/celix_framework_factory.c
index 098f80e..bd138de 100644
--- a/libs/framework/src/celix_framework_factory.c
+++ b/libs/framework/src/celix_framework_factory.c
@@ -42,7 +42,6 @@
void celix_frameworkFactory_destroyFramework(celix_framework_t *fw) {
if (fw != NULL) {
framework_stop(fw);
- framework_waitForStop(fw);
framework_destroy(fw);
}
}
diff --git a/libs/framework/src/framework.c b/libs/framework/src/framework.c
index 152c921..1cf3e83 100644
--- a/libs/framework/src/framework.c
+++ b/libs/framework/src/framework.c
@@ -55,16 +55,6 @@
destroy_function_fp destroy;
};
-typedef struct celix_framework_bundle_entry {
- celix_bundle_t *bnd;
- long bndId;
-
- celix_thread_mutex_t useMutex; //protects useCount
- celix_thread_cond_t useCond;
- size_t useCount;
-} celix_framework_bundle_entry_t;
-
-
static inline celix_framework_bundle_entry_t* fw_bundleEntry_create(celix_bundle_t *bnd) {
celix_framework_bundle_entry_t *entry = calloc(1, sizeof(*entry));
entry->bnd = bnd;
@@ -79,10 +69,16 @@
static inline void fw_bundleEntry_waitTillUseCountIs(celix_framework_bundle_entry_t *entry, size_t desiredUseCount) {
celixThreadMutex_lock(&entry->useMutex);
+ time_t start = time(NULL);
+ bool warningPrinted = false;
while (entry->useCount != desiredUseCount) {
celixThreadCondition_timedwaitRelative(&entry->useCond, &entry->useMutex, 5, 0);
if (entry->useCount != desiredUseCount) {
- fw_log(celix_frameworkLogger_globalLogger(), CELIX_LOG_LEVEL_WARNING, "Bundle %s (%li) still in use. Use count is %u, desired is ", celix_bundle_getSymbolicName(entry->bnd), entry->bndId, entry->useCount, desiredUseCount);
+ time_t now = time(NULL);
+ if (!warningPrinted && (now-start) > 5) {
+ fw_log(celix_frameworkLogger_globalLogger(), CELIX_LOG_LEVEL_WARNING, "Bundle %s (%li) still in use. Use count is %u, desired is %li", celix_bundle_getSymbolicName(entry->bnd), entry->bndId, entry->useCount, desiredUseCount);
+ warningPrinted = true;
+ }
}
}
celixThreadMutex_unlock(&entry->useMutex);
@@ -147,7 +143,6 @@
return found;
}
-celix_status_t framework_setBundleStateAndNotify(framework_pt framework, bundle_pt bundle, int state);
celix_status_t framework_markBundleResolved(framework_pt framework, module_pt module);
long framework_getNextBundleId(framework_pt framework);
@@ -159,8 +154,8 @@
celix_status_t fw_populateDependentGraph(framework_pt framework, bundle_pt exporter, hash_map_pt *map);
-celix_status_t fw_fireBundleEvent(framework_pt framework, bundle_event_type_e, celix_framework_bundle_entry_t* entry);
-celix_status_t fw_fireFrameworkEvent(framework_pt framework, framework_event_type_e eventType, celix_status_t errorCode);
+void fw_fireBundleEvent(framework_pt framework, bundle_event_type_e, celix_framework_bundle_entry_t* entry);
+void fw_fireFrameworkEvent(framework_pt framework, framework_event_type_e eventType, celix_status_t errorCode);
static void *fw_eventDispatcher(void *fw);
celix_status_t fw_invokeBundleListener(framework_pt framework, bundle_listener_pt listener, bundle_event_pt event, bundle_pt bundle);
@@ -177,6 +172,7 @@
static void framework_autoStartConfiguredBundles(bundle_context_t *fwCtx);
static void framework_autoInstallConfiguredBundlesForList(bundle_context_t *fwCtx, const char *autoStart, celix_array_list_t *installedBundles);
static void framework_autoStartConfiguredBundlesForList(bundle_context_t *fwCtx, const celix_array_list_t *installedBundles);
+static void celix_framework_addToEventQueue(celix_framework_t *fw, const celix_framework_event_t* event);
struct fw_refreshHelper {
framework_pt framework;
@@ -238,95 +234,93 @@
typedef struct fw_frameworkListener * fw_framework_listener_pt;
-enum event_type {
- FRAMEWORK_EVENT_TYPE,
- BUNDLE_EVENT_TYPE,
- EVENT_TYPE_SERVICE,
-};
-typedef enum event_type event_type_e;
+celix_status_t framework_create(framework_pt *out, properties_pt config) {
+ celix_framework_t* framework = calloc(1, sizeof(*framework));
+// celix_thread_mutexattr_t attr;
+// celixThreadMutexAttr_create(&attr);
+// celixThreadMutexAttr_settype(&attr, CELIX_THREAD_MUTEX_RECURSIVE);
-struct request {
- event_type_e type;
+ celixThreadCondition_init(&framework->shutdown.cond, NULL);
+ celixThreadMutex_create(&framework->shutdown.mutex, NULL);
+ celixThreadMutex_create(&framework->dispatcher.mutex, NULL);
+ celixThreadMutex_create(&framework->frameworkListenersLock, NULL);
+ celixThreadMutex_create(&framework->bundleListenerLock, NULL);
+ celixThreadMutex_create(&framework->installedBundles.mutex, NULL);
+ celixThreadCondition_init(&framework->dispatcher.cond, NULL);
+ framework->dispatcher.active = true;
+ framework->nextBundleId = 1L; //system bundle is 0
+ framework->installRequestMap = hashMap_create(utils_stringHash, utils_stringHash, utils_stringEquals, utils_stringEquals);
+ framework->installedBundles.entries = celix_arrayList_create();
+ framework->configurationMap = config;
+ framework->bundleListeners = celix_arrayList_create();
+ framework->frameworkListeners = celix_arrayList_create();
+ framework->dispatcher.dynamicEventQueue = celix_arrayList_create();
- int eventType;
- celix_status_t errorCode;
- const char *error;
+ //create and store framework uuid
+ char uuid[37];
+ uuid_t uid;
+ uuid_generate(uid);
+ uuid_unparse(uid, uuid);
+ properties_set(framework->configurationMap, (char*) OSGI_FRAMEWORK_FRAMEWORK_UUID, uuid);
- char *filter;
- celix_framework_bundle_entry_t* bndEntry;
-};
+ //setup framework logger
+ const char* logStr = getenv(CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL_CONFIG_NAME);
+ if (logStr == NULL) {
+ logStr = celix_properties_get(config, CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL_CONFIG_NAME, CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL_DEFAULT_VALUE);
+ }
+ framework->logger = celix_frameworkLogger_create(celix_logUtils_logLevelFromString(logStr, CELIX_LOG_LEVEL_INFO));
-typedef struct request request_t;
+ celix_status_t status =bundle_create(&framework->bundle);
+ status = CELIX_DO_IF(status, bundle_getBundleId(framework->bundle, &framework->bundleId));
+ status = CELIX_DO_IF(status, bundle_setFramework(framework->bundle, framework));
+ status = CELIX_DO_IF(status, bundleCache_create(uuid, framework->configurationMap, &framework->cache));
+ status = CELIX_DO_IF(status, serviceRegistry_create(framework, &framework->registry));
+ bundle_context_t *context = NULL;
+ status = CELIX_DO_IF(status, bundleContext_create(framework, framework->logger, framework->bundle, &context));
+ status = CELIX_DO_IF(status, bundle_setContext(framework->bundle, context));
+ //create framework bundle entry
+ long bndId = -1L;
+ bundle_getBundleId(framework->bundle, &bndId);
+ celix_framework_bundle_entry_t *entry = fw_bundleEntry_create(framework->bundle);
+ celixThreadMutex_lock(&framework->installedBundles.mutex);
+ celix_arrayList_add(framework->installedBundles.entries, entry);
+ celixThreadMutex_unlock(&framework->installedBundles.mutex);
-celix_status_t framework_create(framework_pt *framework, properties_pt config) {
- celix_status_t status = CELIX_SUCCESS;
-
- *framework = (framework_pt) malloc(sizeof(**framework));
- if (*framework != NULL) {
- celix_thread_mutexattr_t attr;
- celixThreadMutexAttr_create(&attr);
- celixThreadMutexAttr_settype(&attr, CELIX_THREAD_MUTEX_RECURSIVE);
-
- status = CELIX_DO_IF(status, celixThreadCondition_init(&(*framework)->shutdown.cond, NULL));
- status = CELIX_DO_IF(status, celixThreadMutex_create(&(*framework)->shutdown.mutex, NULL));
- status = CELIX_DO_IF(status, celixThreadMutex_create(&(*framework)->dispatcher.mutex, NULL));
- status = CELIX_DO_IF(status, celixThreadMutex_create(&(*framework)->frameworkListenersLock, &attr));
- status = CELIX_DO_IF(status, celixThreadMutex_create(&(*framework)->bundleListenerLock, NULL));
- status = CELIX_DO_IF(status, celixThreadMutex_create(&(*framework)->installedBundles.mutex, NULL));
- status = CELIX_DO_IF(status, celixThreadCondition_init(&(*framework)->dispatcher.cond, NULL));
- if (status == CELIX_SUCCESS) {
- (*framework)->bundle = NULL;
- (*framework)->registry = NULL;
- (*framework)->shutdown.done = false;
- (*framework)->shutdown.initialized = false;
- (*framework)->dispatcher.active = true;
- (*framework)->nextBundleId = 1L; //system bundle is 0
- (*framework)->cache = NULL;
- (*framework)->installRequestMap = hashMap_create(utils_stringHash, utils_stringHash, utils_stringEquals, utils_stringEquals);
- (*framework)->installedBundles.entries = celix_arrayList_create();
- (*framework)->bundleListeners = NULL;
- (*framework)->frameworkListeners = NULL;
- (*framework)->dispatcher.requests = NULL;
- (*framework)->dispatcher.nrOfLocalRequest = 0;
- (*framework)->configurationMap = config;
-
- const char* logStr = getenv(CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL_CONFIG_NAME);
- if (logStr == NULL) {
- logStr = celix_properties_get(config, CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL_CONFIG_NAME, CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL_DEFAULT_VALUE);
- }
- (*framework)->logger = celix_frameworkLogger_create(celix_logUtils_logLevelFromString(logStr, CELIX_LOG_LEVEL_INFO));
-
- status = CELIX_DO_IF(status, bundle_create(&(*framework)->bundle));
- status = CELIX_DO_IF(status, bundle_getBundleId((*framework)->bundle, &(*framework)->bundleId));
- status = CELIX_DO_IF(status, bundle_setFramework((*framework)->bundle, (*framework)));
- if (status == CELIX_SUCCESS) {
- //nop
- } else {
- status = CELIX_FRAMEWORK_EXCEPTION;
- fw_logCode((*framework)->logger, CELIX_LOG_LEVEL_ERROR, status, "Could not create framework");
- }
- } else {
- status = CELIX_FRAMEWORK_EXCEPTION;
- fw_logCode((*framework)->logger, CELIX_LOG_LEVEL_ERROR, status, "Could not create framework");
- }
- } else {
- status = CELIX_FRAMEWORK_EXCEPTION;
- fw_logCode((*framework)->logger, CELIX_LOG_LEVEL_ERROR, CELIX_ENOMEM, "Could not create framework");
+ if (status != CELIX_SUCCESS) {
+ fw_logCode(framework->logger, CELIX_LOG_LEVEL_ERROR, status, "Could not create framework");
+ free(framework);
+ framework = NULL;
}
+ *out = framework;
return status;
}
celix_status_t framework_destroy(framework_pt framework) {
celix_status_t status = CELIX_SUCCESS;
+ celixThreadMutex_lock(&framework->shutdown.mutex);
+ bool shutdownInitialized = framework->shutdown.initialized;
+ celixThreadMutex_unlock(&framework->shutdown.mutex);
+
+ if (shutdownInitialized) {
+ framework_waitForStop(framework);
+ } else {
+ fw_log(framework->logger, CELIX_LOG_LEVEL_FATAL, "Cannot destroy framework. framework is not stopped or stopping!");
+ return CELIX_ILLEGAL_STATE;
+ }
+
//Note the shutdown thread can not be joined on the framework_shutdown (which is normally more logical),
//because a shutdown can be initiated from a bundle.
//A bundle cannot be stopped when it is waiting for a framework shutdown -> hence a shutdown thread which
//has not been joined yet.
- celixThread_join(framework->shutdown.thread, NULL);
+ if (!framework->shutdown.joined) {
+ celixThread_join(framework->shutdown.thread, NULL);
+ framework->shutdown.joined = true;
+ }
+
serviceRegistry_destroy(framework->registry);
@@ -341,7 +335,7 @@
const char *bndName = celix_bundle_getSymbolicName(bnd);
fw_log(framework->logger, CELIX_LOG_LEVEL_FATAL, "Cannot destroy framework. The use count of bundle %s (bnd id %li) is not 0, but %u.", bndName, entry->bndId, count);
celixThreadMutex_lock(&framework->dispatcher.mutex);
- int nrOfRequests = celix_arrayList_size(framework->dispatcher.requests);
+ int nrOfRequests = framework->dispatcher.eventQueueSize + celix_arrayList_size(framework->dispatcher.dynamicEventQueue);
celixThreadMutex_unlock(&framework->dispatcher.mutex);
fw_log(framework->logger, CELIX_LOG_LEVEL_WARNING, "nr of request left: %i (should be 0).", nrOfRequests);
}
@@ -375,6 +369,7 @@
bundle_destroy(bnd);
}
+ celixThreadMutex_unlock(&framework->installedBundles.mutex);
celix_arrayList_destroy(framework->installedBundles.entries);
celixThreadMutex_destroy(&framework->installedBundles.mutex);
@@ -387,8 +382,8 @@
arrayList_destroy(framework->frameworkListeners);
}
- assert(celix_arrayList_size(framework->dispatcher.requests) == 0);
- celix_arrayList_destroy(framework->dispatcher.requests);
+ assert(celix_arrayList_size(framework->dispatcher.dynamicEventQueue) == 0);
+ celix_arrayList_destroy(framework->dispatcher.dynamicEventQueue);
bundleCache_destroy(&framework->cache);
@@ -409,66 +404,26 @@
}
celix_status_t fw_init(framework_pt framework) {
- bundle_state_e state = OSGI_FRAMEWORK_BUNDLE_UNKNOWN;
- const char *location = NULL;
- module_pt module = NULL;
- linked_list_pt wires = NULL;
- array_list_pt archives = NULL;
- bundle_archive_pt archive = NULL;
+ celixThreadMutex_lock(&framework->dispatcher.mutex);
+ framework->dispatcher.active = true;
+ celixThreadMutex_unlock(&framework->dispatcher.mutex);
- /*create and store framework uuid*/
- char uuid[37];
+ celixThreadMutex_lock(&framework->shutdown.mutex);
+ framework->shutdown.done = false;
+ framework->shutdown.joined = false;
+ framework->shutdown.initialized = false;
+ celixThreadMutex_unlock(&framework->shutdown.mutex);
- uuid_t uid;
- uuid_generate(uid);
- uuid_unparse(uid, uuid);
- properties_set(framework->configurationMap, (char*) OSGI_FRAMEWORK_FRAMEWORK_UUID, uuid);
+ celixThread_create(&framework->dispatcher.thread, NULL, fw_eventDispatcher, framework);
- celix_status_t status = CELIX_SUCCESS;
- status = CELIX_DO_IF(status, arrayList_create(&framework->bundleListeners));
- status = CELIX_DO_IF(status, arrayList_create(&framework->frameworkListeners));
- status = CELIX_DO_IF(status, arrayList_create(&framework->dispatcher.requests));
- status = CELIX_DO_IF(status, celixThread_create(&framework->dispatcher.thread, NULL, fw_eventDispatcher, framework));
- status = CELIX_DO_IF(status, bundle_getState(framework->bundle, &state));
- if (status == CELIX_SUCCESS) {
- if ((state == OSGI_FRAMEWORK_BUNDLE_INSTALLED) || (state == OSGI_FRAMEWORK_BUNDLE_RESOLVED)) {
- status = CELIX_DO_IF(status, bundleCache_create(uuid, framework->configurationMap,&framework->cache));
- status = CELIX_DO_IF(status, bundle_getState(framework->bundle, &state));
- if (status == CELIX_SUCCESS) {
- if (state == OSGI_FRAMEWORK_BUNDLE_INSTALLED) {
- const char *clean = properties_get(framework->configurationMap, OSGI_FRAMEWORK_FRAMEWORK_STORAGE_CLEAN_NAME);
- bool cleanCache = clean == NULL ? true : strcmp(clean, "false") != 0;
- if (cleanCache) {
- bundleCache_delete(framework->cache);
- }
- }
- }
- }
- }
-
- status = CELIX_DO_IF(status, bundle_getArchive(framework->bundle, &archive));
- status = CELIX_DO_IF(status, bundleArchive_getLocation(archive, &location));
- if (status == CELIX_SUCCESS) {
- long bndId = -1L;
- bundle_getBundleId(framework->bundle, &bndId);
- celix_framework_bundle_entry_t *entry = fw_bundleEntry_create(framework->bundle);
- celixThreadMutex_lock(&framework->installedBundles.mutex);
- celix_arrayList_add(framework->installedBundles.entries, entry);
- celixThreadMutex_unlock(&framework->installedBundles.mutex);
- }
- status = CELIX_DO_IF(status, bundle_getCurrentModule(framework->bundle, &module));
- if (status == CELIX_SUCCESS) {
- wires = resolver_resolve(module);
- if (wires != NULL) {
- framework_markResolvedModules(framework, wires);
- } else {
- status = CELIX_BUNDLE_EXCEPTION;
- fw_logCode(framework->logger, CELIX_LOG_LEVEL_ERROR, status, "Unresolved constraints in System Bundle");
- }
+ bool cleanCache = celix_properties_getAsBool(framework->configurationMap, OSGI_FRAMEWORK_FRAMEWORK_STORAGE_CLEAN_NAME, OSGI_FRAMEWORK_FRAMEWORK_STORAGE_CLEAN_DEFAULT);
+ if (cleanCache) {
+ bundleCache_delete(framework->cache);
}
- status = CELIX_DO_IF(status, bundleCache_getArchives(framework->cache, &archives));
+ celix_array_list_t* archives = NULL;
+ celix_status_t status = bundleCache_getArchives(framework->cache, &archives);
if (status == CELIX_SUCCESS) {
unsigned int arcIdx;
for (arcIdx = 0; arcIdx < arrayList_size(archives); arcIdx++) {
@@ -491,12 +446,9 @@
arrayList_destroy(archives);
}
- status = CELIX_DO_IF(status, serviceRegistry_create(framework, &framework->registry));
- status = CELIX_DO_IF(status, framework_setBundleStateAndNotify(framework, framework->bundle, OSGI_FRAMEWORK_BUNDLE_STARTING));
- bundle_context_t *context = NULL;
- status = CELIX_DO_IF(status, bundleContext_create(framework, framework->logger, framework->bundle, &context));
- status = CELIX_DO_IF(status, bundle_setContext(framework->bundle, context));
+ status = CELIX_DO_IF(status, bundle_setState(framework->bundle, OSGI_FRAMEWORK_BUNDLE_STARTING));
+
if (status == CELIX_SUCCESS) {
celix_bundle_activator_t *activator = calloc(1,(sizeof(*activator)));
if (activator != NULL) {
@@ -515,19 +467,11 @@
status = CELIX_DO_IF(status, bundle_getContext(framework->bundle, &validateContext));
if (status == CELIX_SUCCESS) {
- /* This code part is in principle dead, but in future it may do something.
- * That's why it's outcommented and not deleted
- if (create != NULL) {
- create(context, &userData);
- }
- */
activator->userData = userData;
-
if (start != NULL) {
start(userData, validateContext);
}
- }
- else{
+ } else {
free(activator);
}
} else {
@@ -554,17 +498,15 @@
}
status = CELIX_DO_IF(status, bundle_getState(framework->bundle, &state));
- if (status == CELIX_SUCCESS) {
- if (state == OSGI_FRAMEWORK_BUNDLE_STARTING) {
- status = CELIX_DO_IF(status, framework_setBundleStateAndNotify(framework, framework->bundle, OSGI_FRAMEWORK_BUNDLE_ACTIVE));
- }
+ if (status == CELIX_SUCCESS && state == OSGI_FRAMEWORK_BUNDLE_STARTING) {
+ bundle_setState(framework->bundle, OSGI_FRAMEWORK_BUNDLE_ACTIVE);
}
celix_framework_bundle_entry_t* entry = fw_bundleEntry_getBundleEntryAndIncreaseUseCount(framework, framework->bundleId);
- status = CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_STARTED, entry));
+ CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_STARTED, entry));
fw_bundleEntry_decreaseUseCount(entry);
- status = CELIX_DO_IF(status, fw_fireFrameworkEvent(framework, OSGI_FRAMEWORK_EVENT_STARTED, framework->bundleId));
+ CELIX_DO_IF(status, fw_fireFrameworkEvent(framework, OSGI_FRAMEWORK_EVENT_STARTED, framework->bundleId));
if (status != CELIX_SUCCESS) {
status = CELIX_BUNDLE_EXCEPTION;
@@ -903,8 +845,8 @@
status = CELIX_DO_IF(status, bundle_setActivator(entry->bnd, activator));
- status = CELIX_DO_IF(status, framework_setBundleStateAndNotify(framework, entry->bnd, OSGI_FRAMEWORK_BUNDLE_STARTING));
- status = CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_STARTING, entry));
+ status = CELIX_DO_IF(status, bundle_setState(entry->bnd, OSGI_FRAMEWORK_BUNDLE_STARTING));
+ CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_STARTING, entry));
status = CELIX_DO_IF(status, bundle_getContext(entry->bnd, &context));
@@ -922,8 +864,8 @@
}
}
- status = CELIX_DO_IF(status, framework_setBundleStateAndNotify(framework, entry->bnd, OSGI_FRAMEWORK_BUNDLE_ACTIVE));
- status = CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_STARTED, entry));
+ status = CELIX_DO_IF(status, bundle_setState(entry->bnd, OSGI_FRAMEWORK_BUNDLE_ACTIVE));
+ CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_STARTED, entry));
if (status != CELIX_SUCCESS) {
//state is still STARTING, back to resolved
@@ -935,7 +877,7 @@
bundle_setActivator(entry->bnd, NULL);
bundleContext_destroy(context);
free(activator);
- framework_setBundleStateAndNotify(framework, entry->bnd, OSGI_FRAMEWORK_BUNDLE_RESOLVED);
+ status = bundle_setState(entry->bnd, OSGI_FRAMEWORK_BUNDLE_RESOLVED);
}
}
}
@@ -986,7 +928,7 @@
status = CELIX_DO_IF(status, bundle_revise(bundle, location, inputFile));
status = CELIX_DO_IF(status, bundleArchive_setLastModified(archive, time(NULL)));
- status = CELIX_DO_IF(status, framework_setBundleStateAndNotify(framework, bundle, OSGI_FRAMEWORK_BUNDLE_INSTALLED));
+ status = CELIX_DO_IF(status, bundle_setState(bundle, OSGI_FRAMEWORK_BUNDLE_INSTALLED));
bundle_revision_pt revision = NULL;
array_list_pt handles = NULL;
@@ -1001,8 +943,8 @@
}
- status = CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_UNRESOLVED, entry));
- status = CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_UPDATED, entry));
+ CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_UNRESOLVED, entry));
+ CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_UPDATED, entry));
// Refresh packages?
@@ -1080,10 +1022,10 @@
}
- status = CELIX_DO_IF(status, framework_setBundleStateAndNotify(framework, entry->bnd, OSGI_FRAMEWORK_BUNDLE_STOPPING));
- status = CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_STOPPING, entry));
+ CELIX_DO_IF(status, bundle_setState(entry->bnd, OSGI_FRAMEWORK_BUNDLE_STOPPING));
+ CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_STOPPING, entry));
if (status == CELIX_SUCCESS) {
- if (wasActive || (bndId == 0)) {
+ if (wasActive) {
activator = bundle_getActivator(entry->bnd);
status = CELIX_DO_IF(status, bundle_getContext(entry->bnd, &context));
@@ -1103,8 +1045,7 @@
}
if (bndId > 0) {
- celix_serviceTracker_syncForContext(entry->bnd->context);
- status = CELIX_DO_IF(status, serviceRegistry_clearServiceRegistrations(framework->registry, entry->bnd));
+ celix_bundleContext_cleanup(entry->bnd->context);
if (status == CELIX_SUCCESS) {
module_pt module = NULL;
const char *symbolicName = NULL;
@@ -1112,8 +1053,6 @@
bundle_getCurrentModule(entry->bnd, &module);
module_getSymbolicName(module, &symbolicName);
bundle_getBundleId(entry->bnd, &id);
-
- serviceRegistry_clearReferencesFor(framework->registry, entry->bnd);
}
if (context != NULL) {
@@ -1121,10 +1060,14 @@
status = CELIX_DO_IF(status, bundle_setContext(entry->bnd, NULL));
}
- status = CELIX_DO_IF(status, framework_setBundleStateAndNotify(framework, entry->bnd, OSGI_FRAMEWORK_BUNDLE_RESOLVED));
+ status = CELIX_DO_IF(status, bundle_setState(entry->bnd, OSGI_FRAMEWORK_BUNDLE_RESOLVED));
+ CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_STOPPED, entry));
} else if (bndId == 0) {
//framework bundle
- celix_serviceTracker_syncForContext(framework->bundle->context);
+ celix_bundleContext_cleanup(entry->bnd->context);
+
+ status = CELIX_DO_IF(status, bundle_setState(entry->bnd, OSGI_FRAMEWORK_BUNDLE_RESOLVED));
+ CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_STOPPED, entry));
}
}
@@ -1151,7 +1094,6 @@
}
fw_bundleEntry_decreaseUseCount(entry);
- celix_serviceTracker_syncForFramework(framework);
return status;
}
@@ -1184,12 +1126,12 @@
}
}
- status = CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_UNRESOLVED, entry));
+ CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_UNRESOLVED, entry));
- status = CELIX_DO_IF(status, framework_setBundleStateAndNotify(framework, bnd, OSGI_FRAMEWORK_BUNDLE_UNINSTALLED));
+ status = CELIX_DO_IF(status, bundle_setState(bnd, OSGI_FRAMEWORK_BUNDLE_UNINSTALLED));
status = CELIX_DO_IF(status, bundleArchive_setLastModified(archive, time(NULL)));
- status = CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_UNINSTALLED, entry));
+ CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_UNINSTALLED, entry));
//NOTE wait outside installedBundles.mutex
fw_bundleEntry_decreaseUseCount(entry);
@@ -1301,7 +1243,7 @@
bundle_refresh(entry->bnd);
if (fire) {
- framework_setBundleStateAndNotify(framework, entry->bnd, OSGI_FRAMEWORK_BUNDLE_INSTALLED);
+ bundle_setState(entry->bnd, OSGI_FRAMEWORK_BUNDLE_INSTALLED);
fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_UNRESOLVED, entry);
}
@@ -1761,8 +1703,8 @@
status = CELIX_DO_IF(status, framework_loadBundleLibraries(framework, bundle));
}
- status = CELIX_DO_IF(status, framework_setBundleStateAndNotify(framework, bundle, OSGI_FRAMEWORK_BUNDLE_RESOLVED));
- status = CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_RESOLVED, entry));
+ status = CELIX_DO_IF(status, bundle_setState(bundle, OSGI_FRAMEWORK_BUNDLE_RESOLVED));
+ CELIX_DO_IF(status, fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_RESOLVED, entry));
}
if (status != CELIX_SUCCESS) {
@@ -1823,17 +1765,16 @@
return bnd;
}
-celix_status_t framework_setBundleStateAndNotify(framework_pt framework, bundle_pt bundle, int state) {
- int ret = CELIX_SUCCESS;
- bundle_setState(bundle, state);
- return ret;
-}
-
celix_status_t framework_waitForStop(framework_pt framework) {
celixThreadMutex_lock(&framework->shutdown.mutex);
while (!framework->shutdown.done) {
celixThreadCondition_wait(&framework->shutdown.cond, &framework->shutdown.mutex);
}
+ if (!framework->shutdown.joined) {
+ celixThread_join(framework->shutdown.thread, NULL);
+ framework->shutdown.joined = true;
+ }
+
celixThreadMutex_unlock(&framework->shutdown.mutex);
return CELIX_SUCCESS;
@@ -1894,6 +1835,15 @@
fw_bundleEntry_decreaseUseCount(fwEntry);
}
+ //join dispatcher thread
+ celixThreadMutex_lock(&fw->dispatcher.mutex);
+ fw->dispatcher.active = false;
+ celixThreadCondition_broadcast(&fw->dispatcher.cond);
+ celixThreadMutex_unlock(&fw->dispatcher.mutex);
+ celixThread_join(fw->dispatcher.thread, NULL);
+ fw_log(fw->logger, CELIX_LOG_LEVEL_TRACE, "Joined event loop thread for framework %s", celix_framework_getUUID(framework));
+
+
celixThreadMutex_lock(&fw->shutdown.mutex);
fw->shutdown.done = true;
celixThreadCondition_broadcast(&fw->shutdown.cond);
@@ -1923,88 +1873,67 @@
return result;
}
-celix_status_t fw_fireBundleEvent(framework_pt framework, bundle_event_type_e eventType, celix_framework_bundle_entry_t* entry) {
- celix_status_t status = CELIX_SUCCESS;
-
+void fw_fireBundleEvent(framework_pt framework, bundle_event_type_e eventType, celix_framework_bundle_entry_t* entry) {
if (eventType == OSGI_FRAMEWORK_BUNDLE_EVENT_STOPPING || eventType == OSGI_FRAMEWORK_BUNDLE_EVENT_UNINSTALLED || eventType == OSGI_FRAMEWORK_BUNDLE_EVENT_STOPPED) {
if (entry->bndId == framework->bundleId) {
//NOTE for framework bundle not triggering events while framework is stopped (and as result in use)
- return CELIX_SUCCESS;
+ return;
}
}
- request_t* request = (request_t*) calloc(1, sizeof(*request));
- if (!request) {
- status = CELIX_ENOMEM;
- } else {
- fw_bundleEntry_increaseUseCount(entry);
+ fw_bundleEntry_increaseUseCount(entry);
- request->eventType = eventType;
- request->filter = NULL;
- request->type = BUNDLE_EVENT_TYPE;
- request->error = NULL;
- request->bndEntry = entry;
-
- celixThreadMutex_lock(&framework->dispatcher.mutex);
- if (framework->dispatcher.active) {
- //fw_log(framework->logger, CELIX_LOG_LEVEL_TRACE, "Adding dispatcher bundle event request for bnd id %li with event type %i", entry->bndId, eventType);
- celix_arrayList_add(framework->dispatcher.requests, request);
- celixThreadCondition_broadcast(&framework->dispatcher.cond);
- } else {
- /*
- * NOTE because stopping the framework is done through stopping the framework bundle,
- * most bundle stopping / stopped events cannot be fired.
- *
- * TBD if this needs to addressed.
- */
- fw_log(framework->logger, CELIX_LOG_LEVEL_TRACE, "Cannot fire event dispatcher not active. Event is %x for bundle %s", eventType, celix_bundle_getSymbolicName(entry->bnd));
- fw_bundleEntry_decreaseUseCount(entry);
- free(request);
- }
- celixThreadMutex_unlock(&framework->dispatcher.mutex);
- }
-
- framework_logIfError(framework->logger, status, NULL, "Failed to fire bundle event");
-
- return status;
+ celix_framework_event_t event;
+ memset(&event, 0, sizeof(event));
+ event.type = CELIX_BUNDLE_EVENT_TYPE;
+ event.bndEntry = entry;
+ event.bundleEvent = eventType;
+ celix_framework_addToEventQueue(framework, &event);
}
-celix_status_t fw_fireFrameworkEvent(framework_pt framework, framework_event_type_e eventType, celix_status_t errorCode) {
- celix_status_t status = CELIX_SUCCESS;
-
- request_t* request = calloc(1, sizeof(*request));
- if (!request) {
- status = CELIX_ENOMEM;
- } else {
- request->eventType = eventType;
- request->filter = NULL;
- request->type = FRAMEWORK_EVENT_TYPE;
- request->errorCode = errorCode;
- request->error = "";
-
- if (errorCode != CELIX_SUCCESS) {
- request->error = celix_strerror(errorCode);
- }
-
- celixThreadMutex_lock(&framework->dispatcher.mutex);
- if (framework->dispatcher.active) {
- //fw_log(framework->logger, CELIX_LOG_LEVEL_TRACE, "Adding dispatcher framework event request for event type %i", eventType);
- celix_arrayList_add(framework->dispatcher.requests, request);
- celixThreadCondition_broadcast(&framework->dispatcher.cond);
- } else {
- free(request);
- }
- celixThreadMutex_unlock(&framework->dispatcher.mutex);
+void fw_fireFrameworkEvent(framework_pt framework, framework_event_type_e eventType, celix_status_t errorCode) {
+ celix_framework_event_t event;
+ memset(&event, 0, sizeof(event));
+ event.type = CELIX_FRAMEWORK_EVENT_TYPE;
+ event.fwEvent = eventType;
+ event.errorCode = errorCode;
+ event.error = "";
+ if (errorCode != CELIX_SUCCESS) {
+ event.error = celix_strerror(errorCode);
}
- framework_logIfError(framework->logger, status, NULL, "Failed to fire framework event");
-
- return status;
+ celix_framework_addToEventQueue(framework, &event);
}
+static void celix_framework_addToEventQueue(celix_framework_t *fw, const celix_framework_event_t* event) {
+ celixThreadMutex_lock(&fw->dispatcher.mutex);
+ //try to add to static queue
+ if (celix_arrayList_size(fw->dispatcher.dynamicEventQueue) > 0) { //always to dynamic queue if not empty (to ensure order)
+ celix_framework_event_t *e = malloc(sizeof(*e));
+ *e = *event; //shallow copy
+ celix_arrayList_add(fw->dispatcher.dynamicEventQueue, e);
+ if (celix_arrayList_size(fw->dispatcher.dynamicEventQueue) % 100 == 0) {
+ fw_log(fw->logger, CELIX_LOG_LEVEL_WARNING, "dynamic event queue size is %i. Is there a bundle blocking on the event loop thread?", celix_arrayList_size(fw->dispatcher.dynamicEventQueue));
+ }
+ } else if (fw->dispatcher.eventQueueSize < CELIX_FRAMEWORK_STATIC_EVENT_QUEUE_SIZE) {
+ size_t index = (fw->dispatcher.eventQueueFirstEntry + fw->dispatcher.eventQueueSize) %
+ CELIX_FRAMEWORK_STATIC_EVENT_QUEUE_SIZE;
+ fw->dispatcher.eventQueue[index] = *event; //shallow copy
+ fw->dispatcher.eventQueueSize += 1;
+ } else {
+ //static queue is full, dynamics queue is empty. Add first entry to dynamic queue
+ fw_log(fw->logger, CELIX_LOG_LEVEL_WARNING,
+ "Static event queue for celix framework is full, falling back to dynamic allocated events. Increase static event queue size, current size is %i", CELIX_FRAMEWORK_STATIC_EVENT_QUEUE_SIZE);
+ celix_framework_event_t *e = malloc(sizeof(*e));
+ *e = *event; //shallow copy
+ celix_arrayList_add(fw->dispatcher.dynamicEventQueue, e);
+ }
+ celixThreadCondition_broadcast(&fw->dispatcher.cond);
+ celixThreadMutex_unlock(&fw->dispatcher.mutex);
+}
-static void fw_handleEventRequest(celix_framework_t *framework, request_t* request) {
- if (request->type == BUNDLE_EVENT_TYPE) {
+static void fw_handleEventRequest(celix_framework_t *framework, celix_framework_event_t* event) {
+ if (event->type == CELIX_BUNDLE_EVENT_TYPE) {
celix_array_list_t *localListeners = celix_arrayList_create();
celixThreadMutex_lock(&framework->bundleListenerLock);
for (int i = 0; i < celix_arrayList_size(framework->bundleListeners); ++i) {
@@ -2016,67 +1945,108 @@
for (int i = 0; i < celix_arrayList_size(localListeners); ++i) {
fw_bundle_listener_pt listener = arrayList_get(localListeners, i);
- bundle_event_t event;
- memset(&event, 0, sizeof(event));
- event.bnd = request->bndEntry->bnd;
- event.type = request->eventType;
- fw_invokeBundleListener(framework, listener->listener, &event, listener->bundle);
+ bundle_event_t bEvent;
+ memset(&bEvent, 0, sizeof(bEvent));
+ bEvent.bnd = event->bndEntry->bnd;
+ bEvent.type = event->bundleEvent;
+ fw_invokeBundleListener(framework, listener->listener, &bEvent, listener->bundle);
fw_bundleListener_decreaseUseCount(listener);
}
celix_arrayList_destroy(localListeners);
- } else if (request->type == FRAMEWORK_EVENT_TYPE) {
+ } else if (event->type == CELIX_FRAMEWORK_EVENT_TYPE) {
celixThreadMutex_lock(&framework->frameworkListenersLock);
- //TODO refactor use of framework listeners to use a useCount + conditition.
for (int i = 0; i < celix_arrayList_size(framework->frameworkListeners); ++i) {
fw_framework_listener_pt listener = celix_arrayList_get(framework->frameworkListeners, i);
- framework_event_t event;
- memset(&event, 0, sizeof(event));
- event.type = request->eventType;
- event.error = request->error;
- event.errorCode = request->errorCode;
+ framework_event_t fEvent;
+ memset(&fEvent, 0, sizeof(fEvent));
+ fEvent.type = event->fwEvent;
+ fEvent.error = event->error;
+ fEvent.errorCode = event->errorCode;
- fw_invokeFrameworkListener(framework, listener->listener, &event, listener->bundle);
+ fw_invokeFrameworkListener(framework, listener->listener, &fEvent, listener->bundle);
}
celixThreadMutex_unlock(&framework->frameworkListenersLock);
+ } else if (event->type == CELIX_REGISTER_SERVICE_EVENT) {
+ service_registration_t* reg = NULL;
+ celix_status_t status;
+ if (event->factory != NULL) {
+ status = celix_serviceRegistry_registerServiceFactory(framework->registry, event->bndEntry->bnd, event->serviceName, event->factory, event->properties, event->registerServiceId, ®);
+ } else {
+ status = celix_serviceRegistry_registerService(framework->registry, event->bndEntry->bnd, event->serviceName, event->svc, event->properties, event->registerServiceId, ®);
+ }
+ if (status != CELIX_SUCCESS) {
+ fw_log(framework->logger, CELIX_LOG_LEVEL_ERROR, "Could not register service async. svc name is %s, error is %s", event->serviceName, celix_strerror(status));
+ } else if (event->registerCallback != NULL) {
+ event->registerCallback(event->registerData, serviceRegistration_getServiceId(reg));
+ }
+ } else if (event->type == CELIX_UNREGISTER_SERVICE_EVENT) {
+ celix_serviceRegistry_unregisterService(framework->registry, event->bndEntry->bnd, event->unregisterServiceId);
+ } else if (event->type == CELIX_GENERIC_EVENT) {
+ if (event->genericProcess != NULL) {
+ event->genericProcess(event->genericProcessData);
+ }
+ }
+
+ if (event->doneCallback != NULL) {
+ event->doneCallback(event->doneData);
}
}
-static inline void fw_handleEvents(celix_framework_t* framework, celix_array_list_t* localRequests) {
+static inline celix_framework_event_t* fw_topEventFromQueue(celix_framework_t* fw) {
+ celix_framework_event_t* e = NULL;
+ celixThreadMutex_lock(&fw->dispatcher.mutex);
+ if (fw->dispatcher.eventQueueSize > 0) {
+ e = &fw->dispatcher.eventQueue[fw->dispatcher.eventQueueFirstEntry];
+ } else if (celix_arrayList_size(fw->dispatcher.dynamicEventQueue) > 0) {
+ e = celix_arrayList_get(fw->dispatcher.dynamicEventQueue, 0);
+ }
+ celixThreadMutex_unlock(&fw->dispatcher.mutex);
+ return e;
+}
+
+static inline bool fw_removeTopEventFromQueue(celix_framework_t* fw) {
+ bool dynamicallyAllocated = false;
+ celixThreadMutex_lock(&fw->dispatcher.mutex);
+ if (fw->dispatcher.eventQueueSize > 0) {
+ fw->dispatcher.eventQueueFirstEntry = (fw->dispatcher.eventQueueFirstEntry+1) % CELIX_FRAMEWORK_STATIC_EVENT_QUEUE_SIZE;
+ fw->dispatcher.eventQueueSize -= 1;
+ } else if (celix_arrayList_size(fw->dispatcher.dynamicEventQueue) > 0) {
+ celix_arrayList_removeAt(fw->dispatcher.dynamicEventQueue, 0);
+ dynamicallyAllocated = true;
+ }
+ celixThreadMutex_unlock(&fw->dispatcher.mutex);
+ return dynamicallyAllocated;
+}
+
+
+static inline void fw_handleEvents(celix_framework_t* framework) {
celixThreadMutex_lock(&framework->dispatcher.mutex);
- if (celix_arrayList_size(framework->dispatcher.requests) == 0) {
- //TODO needs a timed wait, because this loop sometimes misses the active=false/broadcast.
- //FIXME an go back to 'normal' cond wait.
+ int size = framework->dispatcher.eventQueueSize + celix_arrayList_size(framework->dispatcher.dynamicEventQueue);
+ if (size == 0) {
celixThreadCondition_timedwaitRelative(&framework->dispatcher.cond, &framework->dispatcher.mutex, 1, 0);
}
- for (int i = 0; i < celix_arrayList_size(framework->dispatcher.requests); ++i) {
- request_t* r = celix_arrayList_get(framework->dispatcher.requests, i);
- celix_arrayList_add(localRequests, r);
-// if (r->type == BUNDLE_EVENT_TYPE) {
-// fw_log(framework->logger, CELIX_LOG_LEVEL_TRACE,
-// "Removing dispatcher bundle event request for bnd id %li with event type %i", r->bndEntry->bndId, r->eventType);
-// } else {
-// fw_log(framework->logger, CELIX_LOG_LEVEL_TRACE,
-// "Removing dispatcher framework event request for event type %i", r->eventType);
-// }
- }
- celix_arrayList_clear(framework->dispatcher.requests);
- framework->dispatcher.nrOfLocalRequest = celix_arrayList_size(localRequests);
+ size = framework->dispatcher.eventQueueSize + celix_arrayList_size(framework->dispatcher.dynamicEventQueue);
celixThreadMutex_unlock(&framework->dispatcher.mutex);
- for (int i = 0; i < celix_arrayList_size(localRequests); ++i) {
- request_t* request = celix_arrayList_get(localRequests, i);
- fw_handleEventRequest(framework, request);
- if (request->bndEntry != NULL) {
- fw_bundleEntry_decreaseUseCount(request->bndEntry);
+ while (size > 0) {
+ celix_framework_event_t* topEvent = fw_topEventFromQueue(framework);
+ fw_handleEventRequest(framework, topEvent);
+ bool dynamiclyAllocatedEvent = fw_removeTopEventFromQueue(framework);
+
+ if (topEvent->bndEntry != NULL) {
+ fw_bundleEntry_decreaseUseCount(topEvent->bndEntry);
}
- free(request);
- }
- celix_arrayList_clear(localRequests);
+ free(topEvent->serviceName);
+ if (dynamiclyAllocatedEvent) {
+ free(topEvent);
+ }
- celixThreadMutex_lock(&framework->dispatcher.mutex);
- framework->dispatcher.nrOfLocalRequest = 0;
- celixThreadMutex_unlock(&framework->dispatcher.mutex);
+ celixThreadMutex_lock(&framework->dispatcher.mutex);
+ size = framework->dispatcher.eventQueueSize + celix_arrayList_size(framework->dispatcher.dynamicEventQueue);
+ celixThreadCondition_broadcast(&framework->dispatcher.cond);
+ celixThreadMutex_unlock(&framework->dispatcher.mutex);
+ }
}
static void *fw_eventDispatcher(void *fw) {
@@ -2086,27 +2056,21 @@
bool active = framework->dispatcher.active;
celixThreadMutex_unlock(&framework->dispatcher.mutex);
- celix_array_list_t *localRequests = celix_arrayList_create();
-
while (active) {
- fw_handleEvents(framework, localRequests);
-
+ fw_handleEvents(framework);
celixThreadMutex_lock(&framework->dispatcher.mutex);
- celixThreadCondition_broadcast(&framework->dispatcher.cond); //trigger threads waiting for an empty event queue (after local events are handled)
active = framework->dispatcher.active;
celixThreadMutex_unlock(&framework->dispatcher.mutex);
}
//not active any more, last run for possible request left overs
celixThreadMutex_lock(&framework->dispatcher.mutex);
- bool needLastRun = celix_arrayList_size(framework->dispatcher.requests) > 0;
+ bool needLastRun = framework->dispatcher.eventQueueSize > 0 || celix_arrayList_size(framework->dispatcher.dynamicEventQueue) > 0;
celixThreadMutex_unlock(&framework->dispatcher.mutex);
if (needLastRun) {
- fw_handleEvents(framework, localRequests);
+ fw_handleEvents(framework);
}
-
- celix_arrayList_destroy(localRequests);
celixThread_exit(NULL);
return NULL;
@@ -2153,14 +2117,7 @@
celixThreadMutex_unlock(&framework->shutdown.mutex);
if (!alreadyInitialized) {
- celixThreadMutex_lock(&framework->dispatcher.mutex);
- framework->dispatcher.active = false;
- celixThreadCondition_broadcast(&framework->dispatcher.cond);
- celixThreadMutex_unlock(&framework->dispatcher.mutex);
- celixThread_join(framework->dispatcher.thread, NULL);
- fw_log(framework->logger, CELIX_LOG_LEVEL_TRACE, "Joined shutdown thread for framework %s", celix_framework_getUUID(framework));
-
- celixThread_create(&framework->shutdown.thread, NULL, &framework_shutdown, framework);
+ celixThread_create(&framework->shutdown.thread, NULL, framework_shutdown, framework);
}
} else {
status = CELIX_FRAMEWORK_EXCEPTION;
@@ -2385,9 +2342,9 @@
return called;
}
-service_registration_t* celix_framework_registerServiceFactory(framework_t *fw , const celix_bundle_t *bnd, const char* serviceName, celix_service_factory_t *factory, celix_properties_t *properties) {
+long celix_framework_registerService(framework_t *fw, celix_bundle_t *bnd, const char* serviceName, void* svc, celix_service_factory_t *factory, celix_properties_t *properties) {
const char *error = NULL;
- celix_status_t status = CELIX_SUCCESS;
+ celix_status_t status;
service_registration_t *reg = NULL;
long bndId = celix_bundle_getId(bnd);
@@ -2395,14 +2352,166 @@
if (serviceName != NULL && factory != NULL) {
- status = CELIX_DO_IF(status, celix_serviceRegistry_registerServiceFactory(fw->registry, bnd, serviceName, factory, properties, ®));
+ status = celix_serviceRegistry_registerServiceFactory(fw->registry, bnd, serviceName, factory, properties, 0, ®);
+ } else if (serviceName != NULL) {
+ status = celix_serviceRegistry_registerService(fw->registry, bnd, serviceName, svc, properties, 0, ®);
+ } else {
+ fw_log(fw->logger, CELIX_LOG_LEVEL_ERROR, "Invalid arguments serviceName", serviceName);
+ status = CELIX_ILLEGAL_ARGUMENT;
}
fw_bundleEntry_decreaseUseCount(entry);
- framework_logIfError(fw->logger, status, error, "Cannot register service factory: %s", serviceName);
+ framework_logIfError(fw->logger, status, error, "Cannot register %s '%s'", factory == NULL ? "service" : "service factory", serviceName);
- return reg;
+ return serviceRegistration_getServiceId(reg);
+}
+
+long celix_framework_registerServiceAsync(
+ framework_t *fw,
+ celix_bundle_t *bnd,
+ const char* serviceName,
+ void* svc,
+ celix_service_factory_t* factory,
+ celix_properties_t *properties,
+ void* registerDoneData,
+ void(*registerDoneCallback)(void *registerDoneData, long serviceId),
+ void* eventDoneData,
+ void (*eventDoneCallback)(void* eventDoneData)) {
+ long bndId = celix_bundle_getId(bnd);
+ celix_framework_bundle_entry_t *entry = fw_bundleEntry_getBundleEntryAndIncreaseUseCount(fw, bndId);
+
+ long svcId = celix_serviceRegistry_nextSvcId(fw->registry);
+
+ celix_framework_event_t event;
+ memset(&event, 0, sizeof(event));
+ event.type = CELIX_REGISTER_SERVICE_EVENT;
+ event.bndEntry = entry;
+ event.registerServiceId = svcId;
+ event.serviceName = celix_utils_strdup(serviceName);
+ event.properties = properties;
+ event.svc = svc;
+ event.factory = factory;
+ event.registerData = registerDoneData;
+ event.registerCallback = registerDoneCallback;
+ event.doneData = eventDoneData;
+ event.doneCallback = eventDoneCallback;
+ celix_framework_addToEventQueue(fw, &event);
+
+ return svcId;
+}
+
+void celix_framework_unregisterAsync(celix_framework_t* fw, celix_bundle_t* bnd, long serviceId, void *doneData, void (*doneCallback)(void*)) {
+ long bndId = celix_bundle_getId(bnd);
+ celix_framework_bundle_entry_t *entry = fw_bundleEntry_getBundleEntryAndIncreaseUseCount(fw, bndId);
+
+ celix_framework_event_t event;
+ memset(&event, 0, sizeof(event));
+ event.type = CELIX_UNREGISTER_SERVICE_EVENT;
+ event.bndEntry = entry;
+ event.unregisterServiceId = serviceId;
+ event.doneData = doneData;
+ event.doneCallback = doneCallback;
+
+ celix_framework_addToEventQueue(fw, &event);
+}
+
+void celix_framework_unregister(celix_framework_t* fw, celix_bundle_t* bnd, long serviceId) {
+ celix_serviceRegistry_unregisterService(fw->registry, bnd, serviceId);
+}
+
+void celix_framework_waitForAsyncRegistration(framework_t *fw, long svcId) {
+ assert(!celix_framework_isCurrentThreadTheEventLoop(fw));
+
+ celixThreadMutex_lock(&fw->dispatcher.mutex);
+ bool registrationsInProgress = true;
+ while (registrationsInProgress) {
+ registrationsInProgress = false;
+ for (int i = 0; i < fw->dispatcher.eventQueueSize; ++i) {
+ int index = (fw->dispatcher.eventQueueFirstEntry + i) % CELIX_FRAMEWORK_STATIC_EVENT_QUEUE_SIZE;
+ celix_framework_event_t* e = &fw->dispatcher.eventQueue[index];
+ if (e->type == CELIX_REGISTER_SERVICE_EVENT && e->registerServiceId == svcId) {
+ registrationsInProgress = true;
+ break;
+ }
+ }
+ for (int i = 0; !registrationsInProgress && i < celix_arrayList_size(fw->dispatcher.dynamicEventQueue); ++i) {
+ celix_framework_event_t* e = celix_arrayList_get(fw->dispatcher.dynamicEventQueue, i);
+ if (e->type == CELIX_REGISTER_SERVICE_EVENT && e->registerServiceId == svcId) {
+ registrationsInProgress = true;
+ break;
+ }
+ }
+ if (registrationsInProgress) {
+ celixThreadCondition_timedwaitRelative(&fw->dispatcher.cond, &fw->dispatcher.mutex, 5, 0);
+ }
+ }
+
+ celixThreadMutex_unlock(&fw->dispatcher.mutex);
+}
+
+void celix_framework_waitForAsyncUnregistration(framework_t *fw, long svcId) {
+ assert(!celix_framework_isCurrentThreadTheEventLoop(fw));
+
+ celixThreadMutex_lock(&fw->dispatcher.mutex);
+ bool registrationsInProgress = true;
+ while (registrationsInProgress) {
+ registrationsInProgress = false;
+ for (int i = 0; i < fw->dispatcher.eventQueueSize; ++i) {
+ int index = (fw->dispatcher.eventQueueFirstEntry + i) % CELIX_FRAMEWORK_STATIC_EVENT_QUEUE_SIZE;
+ celix_framework_event_t* e = &fw->dispatcher.eventQueue[index];
+ if (e->type == CELIX_UNREGISTER_SERVICE_EVENT && e->unregisterServiceId == svcId) {
+ registrationsInProgress = true;
+ break;
+ }
+ }
+ for (int i = 0; !registrationsInProgress && i < celix_arrayList_size(fw->dispatcher.dynamicEventQueue); ++i) {
+ celix_framework_event_t* e = celix_arrayList_get(fw->dispatcher.dynamicEventQueue, i);
+ if (e->type == CELIX_UNREGISTER_SERVICE_EVENT && e->unregisterServiceId == svcId) {
+ registrationsInProgress = true;
+ break;
+ }
+ }
+ if (registrationsInProgress) {
+ celixThreadCondition_timedwaitRelative(&fw->dispatcher.cond, &fw->dispatcher.mutex, 5, 0);
+ }
+ }
+
+ celixThreadMutex_unlock(&fw->dispatcher.mutex);
+}
+
+void celix_framework_waitForAsyncRegistrations(framework_t *fw, long bndId) {
+ assert(!celix_framework_isCurrentThreadTheEventLoop(fw));
+
+ celixThreadMutex_lock(&fw->dispatcher.mutex);
+ bool registrationsInProgress = true;
+ while (registrationsInProgress) {
+ registrationsInProgress = false;
+ for (int i = 0; i < fw->dispatcher.eventQueueSize; ++i) {
+ int index = (fw->dispatcher.eventQueueFirstEntry + i) % CELIX_FRAMEWORK_STATIC_EVENT_QUEUE_SIZE;
+ celix_framework_event_t* e = &fw->dispatcher.eventQueue[index];
+ if ((e->type == CELIX_REGISTER_SERVICE_EVENT || e->type == CELIX_UNREGISTER_SERVICE_EVENT) && e->bndEntry->bndId == bndId) {
+ registrationsInProgress = true;
+ break;
+ }
+ }
+ for (int i = 0; i < !registrationsInProgress && celix_arrayList_size(fw->dispatcher.dynamicEventQueue); ++i) {
+ celix_framework_event_t* e = celix_arrayList_get(fw->dispatcher.dynamicEventQueue, i);
+ if ((e->type == CELIX_REGISTER_SERVICE_EVENT || e->type == CELIX_UNREGISTER_SERVICE_EVENT) && e->bndEntry->bndId == bndId) {
+ registrationsInProgress = true;
+ break;
+ }
+ }
+ if (registrationsInProgress) {
+ celixThreadCondition_timedwaitRelative(&fw->dispatcher.cond, &fw->dispatcher.mutex, 5, 0);
+ }
+ }
+
+ celixThreadMutex_unlock(&fw->dispatcher.mutex);
+}
+
+bool celix_framework_isCurrentThreadTheEventLoop(framework_t* fw) {
+ return celixThread_equals(celixThread_self(), fw->dispatcher.thread);
}
const char* celix_framework_getUUID(const celix_framework_t *fw) {
@@ -2462,6 +2571,12 @@
return isActive;
}
+static void celix_framework_waitForBundleEvents(celix_framework_t *fw, long bndId) {
+ if (bndId >= 0 && !celix_framework_isCurrentThreadTheEventLoop(fw)) {
+ celix_framework_waitUntilNoEventsForBnd(fw, bndId);
+ }
+}
+
long celix_framework_installBundle(celix_framework_t *fw, const char *bundleLoc, bool autoStart) {
long bundleId = -1;
bundle_t *bnd = NULL;
@@ -2471,9 +2586,11 @@
status = bundle_getBundleId(bnd, &bundleId);
if (status == CELIX_SUCCESS && autoStart) {
status = bundle_start(bnd);
+
}
}
+ celix_framework_waitForBundleEvents(fw, bundleId);
framework_logIfError(fw->logger, status, NULL, "Failed to install bundle '%s'", bundleLoc);
return bundleId;
@@ -2495,6 +2612,7 @@
uninstalled = status == CELIX_SUCCESS;
}
}
+ celix_framework_waitForBundleEvents(fw, bndId);
return uninstalled;
}
@@ -2511,6 +2629,7 @@
}
fw_bundleEntry_decreaseUseCount(entry);
}
+ celix_framework_waitForBundleEvents(fw, bndId);
return stopped;
}
@@ -2525,17 +2644,112 @@
}
fw_bundleEntry_decreaseUseCount(entry);
}
+ celix_framework_waitForBundleEvents(fw, bndId);
return started;
}
void celix_framework_waitForEmptyEventQueue(celix_framework_t *fw) {
+ assert(!celix_framework_isCurrentThreadTheEventLoop(fw));
+
celixThreadMutex_lock(&fw->dispatcher.mutex);
- while ((celix_arrayList_size(fw->dispatcher.requests) + fw->dispatcher.nrOfLocalRequest) != 0) {
+ while (fw->dispatcher.eventQueueSize > 0 || celix_arrayList_size(fw->dispatcher.dynamicEventQueue) > 0) {
celixThreadCondition_wait(&fw->dispatcher.cond, &fw->dispatcher.mutex);
}
celixThreadMutex_unlock(&fw->dispatcher.mutex);
}
+void celix_framework_waitUntilNoEventsForBnd(celix_framework_t* fw, long bndId) {
+ assert(!celix_framework_isCurrentThreadTheEventLoop(fw));
+
+ celixThreadMutex_lock(&fw->dispatcher.mutex);
+ bool eventInProgress = true;
+ while (eventInProgress) {
+ eventInProgress = false;
+ for (int i = 0; i < fw->dispatcher.eventQueueSize; ++i) {
+ int index = (fw->dispatcher.eventQueueFirstEntry + i) % CELIX_FRAMEWORK_STATIC_EVENT_QUEUE_SIZE;
+ celix_framework_event_t* e = &fw->dispatcher.eventQueue[index];
+ if (e->bndEntry != NULL && e->bndEntry->bndId == bndId) {
+ eventInProgress = true;
+ break;
+ }
+ }
+ for (int i = 0; !eventInProgress && i < celix_arrayList_size(fw->dispatcher.dynamicEventQueue); ++i) {
+ celix_framework_event_t* e = celix_arrayList_get(fw->dispatcher.dynamicEventQueue, i);
+ if (e->bndEntry != NULL && e->bndEntry->bndId == bndId) {
+ eventInProgress = true;
+ break;
+ }
+ }
+ if (eventInProgress) {
+ celixThreadCondition_timedwaitRelative(&fw->dispatcher.cond, &fw->dispatcher.mutex, 5, 0);
+ }
+ }
+ celixThreadMutex_unlock(&fw->dispatcher.mutex);
+}
+
+
void celix_framework_setLogCallback(celix_framework_t* fw, void* logHandle, void (*logFunction)(void* handle, celix_log_level_e level, const char* file, const char *function, int line, const char *format, va_list formatArgs)) {
celix_frameworkLogger_setLogCallback(fw->logger, logHandle, logFunction);
}
+
+long celix_framework_fireGenericEvent(framework_t* fw, long eventId, long bndId, const char *eventName, void* processData, void (*processCallback)(void *data), void* doneData, void (*doneCallback)(void* doneData)) {
+ celix_framework_bundle_entry_t* bndEntry = NULL;
+ if (bndId >=0) {
+ bndEntry = fw_bundleEntry_getBundleEntryAndIncreaseUseCount(fw, bndId);
+ if (bndEntry == NULL) {
+ fw_log(fw->logger, CELIX_LOG_LEVEL_ERROR, "Cannot find bundle for id %li", bndId);
+ return -1L;
+ }
+ }
+
+ if (eventId < 0) {
+ eventId = celix_framework_nextEventId(fw);
+ }
+
+ celix_framework_event_t event;
+ memset(&event, 0, sizeof(event));
+ event.type = CELIX_GENERIC_EVENT;
+ event.bndEntry = bndEntry;
+ event.genericEventId = eventId;
+ event.genericEventName = eventName;
+ event.genericProcessData = processData;
+ event.genericProcess = processCallback;
+ event.doneData = doneData;
+ event.doneCallback = doneCallback;
+ celix_framework_addToEventQueue(fw, &event);
+
+ return eventId;
+}
+
+long celix_framework_nextEventId(framework_t *fw) {
+ return __atomic_fetch_add(&fw->nextGenericEventId, 1, __ATOMIC_RELAXED);
+}
+
+void celix_framework_waitForGenericEvent(framework_t *fw, long eventId) {
+ assert(!celix_framework_isCurrentThreadTheEventLoop(fw));
+
+ celixThreadMutex_lock(&fw->dispatcher.mutex);
+ bool eventInProgress = true;
+ while (eventInProgress) {
+ eventInProgress = false;
+ for (int i = 0; i < fw->dispatcher.eventQueueSize; ++i) {
+ int index = (fw->dispatcher.eventQueueFirstEntry + i) % CELIX_FRAMEWORK_STATIC_EVENT_QUEUE_SIZE;
+ celix_framework_event_t* e = &fw->dispatcher.eventQueue[index];
+ if (e->type == CELIX_GENERIC_EVENT && e->genericEventId == eventId) {
+ eventInProgress = true;
+ break;
+ }
+ }
+ for (int i = 0; !eventInProgress && i < celix_arrayList_size(fw->dispatcher.dynamicEventQueue); ++i) {
+ celix_framework_event_t* e = celix_arrayList_get(fw->dispatcher.dynamicEventQueue, i);
+ if (e->type == CELIX_GENERIC_EVENT && e->genericEventId == eventId) {
+ eventInProgress = true;
+ break;
+ }
+ }
+ if (eventInProgress) {
+ celixThreadCondition_timedwaitRelative(&fw->dispatcher.cond, &fw->dispatcher.mutex, 5, 0);
+ }
+ }
+ celixThreadMutex_unlock(&fw->dispatcher.mutex);
+}
\ No newline at end of file
diff --git a/libs/framework/src/framework_private.h b/libs/framework/src/framework_private.h
index 4c0b517..7eaa0c7 100644
--- a/libs/framework/src/framework_private.h
+++ b/libs/framework/src/framework_private.h
@@ -41,10 +41,65 @@
#include "celix_threads.h"
#include "service_registry.h"
+#define CELIX_FRAMEWORK_STATIC_EVENT_QUEUE_SIZE 256
+
+typedef struct celix_framework_bundle_entry {
+ celix_bundle_t *bnd;
+ long bndId;
+
+ celix_thread_mutex_t useMutex; //protects useCount
+ celix_thread_cond_t useCond;
+ size_t useCount;
+} celix_framework_bundle_entry_t;
+
+enum celix_framework_event_type {
+ CELIX_FRAMEWORK_EVENT_TYPE = 0x01,
+ CELIX_BUNDLE_EVENT_TYPE = 0x11,
+ CELIX_REGISTER_SERVICE_EVENT = 0x21,
+ CELIX_UNREGISTER_SERVICE_EVENT = 0x22,
+ CELIX_GENERIC_EVENT = 0x30
+};
+
+typedef enum celix_framework_event_type celix_framework_event_type_e;
+
+struct celix_framework_event {
+ celix_framework_event_type_e type;
+ celix_framework_bundle_entry_t* bndEntry;
+
+ void *doneData;
+ void (*doneCallback)(void*);
+
+ //for framework event
+ framework_event_type_e fwEvent;
+ celix_status_t errorCode;
+ const char *error;
+
+ //for bundle event
+ bundle_event_type_e bundleEvent;
+
+ //for register event
+ long registerServiceId;
+ char *serviceName;
+ void *svc;
+ celix_service_factory_t* factory;
+ celix_properties_t* properties;
+ void* registerData;
+ void (*registerCallback)(void *data, long serviceId);
+
+ //for unregister event
+ long unregisterServiceId;
+
+ //for the generic event
+ long genericEventId;
+ const char* genericEventName;
+ void *genericProcessData;
+ void (*genericProcess)(void*);
+
+};
+
+typedef struct celix_framework_event celix_framework_event_t;
+
struct celix_framework {
-#ifdef WITH_APR
- apr_pool_t *pool;
-#endif
celix_bundle_t *bundle;
long bundleId; //the bundle id of the framework (normally 0)
hash_map_pt installRequestMap;
@@ -63,6 +118,7 @@
celix_thread_mutex_t mutex;
celix_thread_cond_t cond;
bool done; //true is shutdown is done
+ bool joined; //true if shutdown thread is joined
bool initialized; //true is a shutdown is initialized
celix_thread_t thread;
} shutdown;
@@ -80,13 +136,17 @@
struct {
celix_thread_cond_t cond;
celix_thread_t thread;
- celix_thread_mutex_t mutex; //protect active and requests
+ celix_thread_mutex_t mutex; //protects below
bool active;
- celix_array_list_t *requests;
- size_t nrOfLocalRequest;
+ celix_framework_event_t eventQueue[CELIX_FRAMEWORK_STATIC_EVENT_QUEUE_SIZE]; //ring buffer
+ int eventQueueSize;
+ int eventQueueFirstEntry;
+ celix_array_list_t *dynamicEventQueue; //entry = celix_framework_event_t*. Used when the eventQueue is full
} dispatcher;
celix_framework_logger_t* logger;
+
+ long nextGenericEventId;
};
FRAMEWORK_EXPORT celix_status_t fw_getProperty(framework_pt framework, const char* name, const char* defaultValue, const char** value);
@@ -151,6 +211,56 @@
**********************************************************************************************************************
**********************************************************************************************************************/
-service_registration_t* celix_framework_registerServiceFactory(framework_t *fw , const celix_bundle_t *bnd, const char* serviceName, celix_service_factory_t *factory, celix_properties_t *properties);
+/**
+ * register service or service factory. Will return a svc id directly and return a service registration in a callback.
+ * callback is called on the fw event loop thread
+ */
+long celix_framework_registerService(framework_t *fw, celix_bundle_t *bnd, const char* serviceName, void* svc, celix_service_factory_t *factory, celix_properties_t *properties);
+
+/**
+ * register service or service factory async. Will return a svc id directly and return a service registration in a callback.
+ * callback is called on the fw event loop thread
+ */
+long celix_framework_registerServiceAsync(
+ framework_t *fw,
+ celix_bundle_t *bnd,
+ const char* serviceName,
+ void* svc,
+ celix_service_factory_t* factory,
+ celix_properties_t *properties,
+ void* registerDoneData,
+ void(*registerDoneCallback)(void *registerDoneData, long serviceId),
+ void* eventDoneData,
+ void (*eventDoneCallback)(void* eventDoneData));
+
+/**
+ * Unregister service async on the event loop thread.
+ */
+void celix_framework_unregisterAsync(celix_framework_t* fw, celix_bundle_t* bnd, long serviceId, void *doneData, void (*doneCallback)(void*));
+
+/**
+ * Unregister service
+ */
+void celix_framework_unregister(celix_framework_t* fw, celix_bundle_t* bnd, long serviceId);
+
+/**
+ * Wait til all service registration or unregistration events for a specific bundle are no longer present in the event queue.
+ */
+void celix_framework_waitForAsyncRegistrations(celix_framework_t *fw, long bndId);
+
+/**
+ * Wait til the async service registration for the provided service id is no longer present in the event queue.
+ */
+void celix_framework_waitForAsyncRegistration(celix_framework_t *fw, long svcId);
+
+/**
+ * Wait til the async service unregistration for the provided service id is no longer present in the event queue.
+ */
+void celix_framework_waitForAsyncUnregistration(framework_t *fw, long svcId);
+
+/**
+ * Returns whether the current thread is the Celix framework event loop thread.
+ */
+bool celix_framework_isCurrentThreadTheEventLoop(framework_t* fw);
#endif /* FRAMEWORK_PRIVATE_H_ */
diff --git a/libs/framework/src/resolver.c b/libs/framework/src/resolver.c
index ac34609..5b68632 100644
--- a/libs/framework/src/resolver.c
+++ b/libs/framework/src/resolver.c
@@ -288,6 +288,9 @@
}
void resolver_removeModule(module_pt module) {
+ if (m_modules == NULL) {
+ return;
+ }
linked_list_pt caps = NULL;
linkedList_removeElement(m_modules, module);
caps = module_getCapabilities(module);
diff --git a/libs/framework/src/service_registry.c b/libs/framework/src/service_registry.c
index e9b8d01..1b2924c 100644
--- a/libs/framework/src/service_registry.c
+++ b/libs/framework/src/service_registry.c
@@ -30,23 +30,10 @@
#include "service_reference_private.h"
#include "framework_private.h"
-#ifdef DEBUG
-#define CHECK_DELETED_REFERENCES true
-#else
-#define CHECK_DELETED_REFERENCES false
-#endif
-
-static celix_status_t serviceRegistry_registerServiceInternal(service_registry_pt registry, bundle_pt bundle, const char* serviceName, const void * serviceObject, properties_pt dictionary, enum celix_service_type svcType, service_registration_pt *registration);
+static celix_status_t serviceRegistry_registerServiceInternal(service_registry_pt registry, bundle_pt bundle, const char* serviceName, const void * serviceObject, properties_pt dictionary, long reservedId, enum celix_service_type svcType, service_registration_pt *registration);
static celix_status_t serviceRegistry_addHooks(service_registry_pt registry, const char* serviceName, const void *serviceObject, service_registration_pt registration);
static celix_status_t serviceRegistry_removeHook(service_registry_pt registry, service_registration_pt registration);
static void serviceRegistry_logWarningServiceReferenceUsageCount(service_registry_pt registry, bundle_pt bundle, service_reference_pt ref, size_t usageCount, size_t refCount);
-static void serviceRegistry_logWarningServiceRegistration(service_registry_pt registry, service_registration_pt reg);
-static celix_status_t serviceRegistry_checkReference(service_registry_pt registry, service_reference_pt ref,
- reference_status_t *refStatus);
-static void serviceRegistry_logIllegalReference(service_registry_pt registry, service_reference_pt reference,
- reference_status_t refStatus);
-static celix_status_t serviceRegistry_setReferenceStatus(service_registry_pt registry, service_reference_pt reference,
- bool deleted);
static celix_status_t serviceRegistry_getUsingBundles(service_registry_pt registry, service_registration_pt reg, array_list_pt *bundles);
static celix_status_t serviceRegistry_getServiceReference_internal(service_registry_pt registry, bundle_pt owner, service_registration_pt registration, service_reference_pt *out);
static void celix_serviceRegistry_serviceChanged(celix_service_registry_t *registry, celix_service_event_type_t eventType, service_registration_pt registration);
@@ -79,11 +66,8 @@
reg->serviceRegistrations = hashMap_create(NULL, NULL, NULL, NULL);
reg->framework = framework;
- reg->nextServiceId = 1L;
- reg->serviceReferences = hashMap_create(NULL, NULL, NULL, NULL);
-
- reg->checkDeletedReferences = CHECK_DELETED_REFERENCES;
- reg->deletedServiceReferences = hashMap_create(NULL, NULL, NULL, NULL);
+ reg->nextServiceId = 1L;
+ reg->serviceReferences = hashMap_create(NULL, NULL, NULL, NULL);
reg->listenerHooks = celix_arrayList_create();
reg->serviceListeners = celix_arrayList_create();
@@ -155,8 +139,6 @@
}
celix_arrayList_destroy(registry->listenerHooks);
- hashMap_destroy(registry->deletedServiceReferences, false, false);
-
size = hashMap_size(registry->pendingRegisterEvents.map);
assert(size == 0);
celixThreadMutex_destroy(®istry->pendingRegisterEvents.mutex);
@@ -198,16 +180,16 @@
}
celix_status_t serviceRegistry_registerService(service_registry_pt registry, bundle_pt bundle, const char* serviceName, const void* serviceObject, properties_pt dictionary, service_registration_pt *registration) {
- return serviceRegistry_registerServiceInternal(registry, bundle, serviceName, serviceObject, dictionary, CELIX_PLAIN_SERVICE, registration);
+ return serviceRegistry_registerServiceInternal(registry, bundle, serviceName, serviceObject, dictionary, 0 /*TODO*/, CELIX_PLAIN_SERVICE, registration);
}
celix_status_t serviceRegistry_registerServiceFactory(service_registry_pt registry, bundle_pt bundle, const char* serviceName, service_factory_pt factory, properties_pt dictionary, service_registration_pt *registration) {
- return serviceRegistry_registerServiceInternal(registry, bundle, serviceName, (const void *) factory, dictionary, CELIX_DEPRECATED_FACTORY_SERVICE, registration);
+ return serviceRegistry_registerServiceInternal(registry, bundle, serviceName, (const void *) factory, dictionary, 0 /*TODO*/, CELIX_DEPRECATED_FACTORY_SERVICE, registration);
}
-static celix_status_t serviceRegistry_registerServiceInternal(service_registry_pt registry, bundle_pt bundle, const char* serviceName, const void * serviceObject, properties_pt dictionary, enum celix_service_type svcType, service_registration_pt *registration) {
+static celix_status_t serviceRegistry_registerServiceInternal(service_registry_pt registry, bundle_pt bundle, const char* serviceName, const void * serviceObject, properties_pt dictionary, long reservedId, enum celix_service_type svcType, service_registration_pt *registration) {
array_list_pt regs;
- long svcId = celix_serviceRegistry_nextSvcId(registry);
+ long svcId = reservedId > 0 ? reservedId : celix_serviceRegistry_nextSvcId(registry);
if (svcType == CELIX_DEPRECATED_FACTORY_SERVICE) {
*registration = serviceRegistration_createServiceFactory(registry->callback, bundle, serviceName,
@@ -299,50 +281,6 @@
return CELIX_SUCCESS;
}
-celix_status_t serviceRegistry_clearServiceRegistrations(service_registry_pt registry, bundle_pt bundle) {
- celix_status_t status = CELIX_SUCCESS;
- array_list_pt registrations = NULL;
- bool registrationsLeft;
-
- celixThreadRwlock_writeLock(®istry->lock);
- registrations = hashMap_get(registry->serviceRegistrations, bundle);
- registrationsLeft = (registrations != NULL);
- if (registrationsLeft) {
- registrationsLeft = (arrayList_size(registrations) > 0);
- }
- celixThreadRwlock_unlock(®istry->lock);
-
- while (registrationsLeft) {
- service_registration_pt reg = arrayList_get(registrations, 0);
-
- serviceRegistry_logWarningServiceRegistration(registry, reg);
-
- if (serviceRegistration_isValid(reg)) {
- serviceRegistration_unregister(reg);
- }
- else {
- arrayList_remove(registrations, 0);
- }
-
- // not removed by last unregister call?
- celixThreadRwlock_writeLock(®istry->lock);
- registrations = hashMap_get(registry->serviceRegistrations, bundle);
- registrationsLeft = (registrations != NULL);
- if (registrationsLeft) {
- registrationsLeft = (arrayList_size(registrations) > 0);
- }
- celixThreadRwlock_unlock(®istry->lock);
- }
-
- return status;
-}
-
-static void serviceRegistry_logWarningServiceRegistration(service_registry_pt registry __attribute__((unused)), service_registration_pt reg) {
- const char *servName = NULL;
- serviceRegistration_getServiceName(reg, &servName);
- fw_log(registry->framework->logger, CELIX_LOG_LEVEL_WARNING, "Dangling service registration for service %s. Look for missing bundleContext_unregisterService/serviceRegistration_unregister calls.", servName);
-}
-
celix_status_t serviceRegistry_getServiceReference(service_registry_pt registry, bundle_pt owner,
service_registration_pt registration, service_reference_pt *out) {
celix_status_t status = CELIX_SUCCESS;
@@ -378,7 +316,6 @@
}
if (status == CELIX_SUCCESS) {
hashMap_put(references, (void*)registration->serviceId, ref);
- hashMap_put(registry->deletedServiceReferences, ref, (void *)false);
}
} else {
serviceReference_retain(ref);
@@ -479,21 +416,15 @@
celix_status_t serviceRegistry_retainServiceReference(service_registry_pt registry, bundle_pt bundle, service_reference_pt reference) {
celix_status_t status = CELIX_SUCCESS;
- reference_status_t refStatus;
bundle_pt refBundle = NULL;
celixThreadRwlock_writeLock(®istry->lock);
- serviceRegistry_checkReference(registry, reference, &refStatus);
- if (refStatus == REF_ACTIVE) {
- serviceReference_getOwner(reference, &refBundle);
- if (refBundle == bundle) {
- serviceReference_retain(reference);
- } else {
- status = CELIX_ILLEGAL_ARGUMENT;
- fw_log(registry->framework->logger, CELIX_LOG_LEVEL_ERROR, "cannot retain a service reference from an other bundle (in ref %p) (provided %p).", refBundle, bundle);
- }
+ serviceReference_getOwner(reference, &refBundle);
+ if (refBundle == bundle) {
+ serviceReference_retain(reference);
} else {
- serviceRegistry_logIllegalReference(registry, reference, refStatus);
+ status = CELIX_ILLEGAL_ARGUMENT;
+ fw_log(registry->framework->logger, CELIX_LOG_LEVEL_ERROR, "cannot retain a service reference from an other bundle (in ref %p) (provided %p).", refBundle, bundle);
}
celixThreadRwlock_unlock(®istry->lock);
@@ -505,97 +436,54 @@
celix_status_t status = CELIX_SUCCESS;
bool destroyed = false;
size_t count = 0;
- reference_status_t refStatus;
celixThreadRwlock_writeLock(®istry->lock);
- serviceRegistry_checkReference(registry, reference, &refStatus);
- if (refStatus == REF_ACTIVE) {
- serviceReference_getUsageCount(reference, &count);
- serviceReference_release(reference, &destroyed);
- if (destroyed) {
+ serviceReference_getUsageCount(reference, &count);
+ serviceReference_release(reference, &destroyed);
+ if (destroyed) {
- if (count > 0) {
- serviceRegistry_logWarningServiceReferenceUsageCount(registry, bundle, reference, count, 0);
- }
+ if (count > 0) {
+ serviceRegistry_logWarningServiceReferenceUsageCount(registry, bundle, reference, count, 0);
+ }
- hash_map_pt refsMap = hashMap_get(registry->serviceReferences, bundle);
+ hash_map_pt refsMap = hashMap_get(registry->serviceReferences, bundle);
- unsigned long refId = 0UL;
- service_reference_pt ref = NULL;
+ unsigned long refId = 0UL;
+ service_reference_pt ref = NULL;
- if (refsMap != NULL) {
- hash_map_iterator_t iter = hashMapIterator_construct(refsMap);
- while (hashMapIterator_hasNext(&iter)) {
- hash_map_entry_pt entry = hashMapIterator_nextEntry(&iter);
- refId = (unsigned long) hashMapEntry_getKey(entry); //note could be invalid e.g. freed
- ref = hashMapEntry_getValue(entry);
+ if (refsMap != NULL) {
+ hash_map_iterator_t iter = hashMapIterator_construct(refsMap);
+ while (hashMapIterator_hasNext(&iter)) {
+ hash_map_entry_pt entry = hashMapIterator_nextEntry(&iter);
+ refId = (unsigned long) hashMapEntry_getKey(entry); //note could be invalid e.g. freed
+ ref = hashMapEntry_getValue(entry);
- if (ref == reference) {
- break;
- } else {
- ref = NULL;
- refId = 0UL;
- }
+ if (ref == reference) {
+ break;
+ } else {
+ ref = NULL;
+ refId = 0UL;
}
}
-
- if (ref != NULL) {
- hashMap_remove(refsMap, (void*)refId);
- int size = hashMap_size(refsMap);
- if (size == 0) {
- hashMap_destroy(refsMap, false, false);
- hashMap_remove(registry->serviceReferences, bundle);
- }
- serviceRegistry_setReferenceStatus(registry, reference, true);
- } else {
- fw_log(registry->framework->logger, CELIX_LOG_LEVEL_ERROR, "Cannot find reference %p in serviceReferences map",
- reference);
- }
}
- } else {
- serviceRegistry_logIllegalReference(registry, reference, refStatus);
+
+ if (ref != NULL) {
+ hashMap_remove(refsMap, (void*)refId);
+ int size = hashMap_size(refsMap);
+ if (size == 0) {
+ hashMap_destroy(refsMap, false, false);
+ hashMap_remove(registry->serviceReferences, bundle);
+ }
+ } else {
+ fw_log(registry->framework->logger, CELIX_LOG_LEVEL_FATAL, "Cannot find reference %p in serviceReferences map",
+ reference);
+ }
}
celixThreadRwlock_unlock(®istry->lock);
return status;
}
-static celix_status_t serviceRegistry_setReferenceStatus(service_registry_pt registry, service_reference_pt reference,
- bool deleted) {
- //precondition write locked on registry->lock
- if (registry->checkDeletedReferences) {
- hashMap_put(registry->deletedServiceReferences, reference, (void *) deleted);
- }
- return CELIX_SUCCESS;
-}
-
-static void serviceRegistry_logIllegalReference(service_registry_pt registry __attribute__((unused)), service_reference_pt reference,
- reference_status_t refStatus) {
- fw_log(registry->framework->logger, CELIX_LOG_LEVEL_ERROR,
- "Error handling service reference %p, status is %i",reference, refStatus);
-}
-
-static celix_status_t serviceRegistry_checkReference(service_registry_pt registry, service_reference_pt ref,
- reference_status_t *out) {
- //precondition read or write locked on registry->lock
- celix_status_t status = CELIX_SUCCESS;
-
- if (registry->checkDeletedReferences) {
- reference_status_t refStatus = REF_UNKNOWN;
-
- if (hashMap_containsKey(registry->deletedServiceReferences, ref)) {
- bool deleted = (bool) hashMap_get(registry->deletedServiceReferences, ref);
- refStatus = deleted ? REF_DELETED : REF_ACTIVE;
- }
-
- *out = refStatus;
- } else {
- *out = REF_ACTIVE;
- }
-
- return status;
-}
-
static void serviceRegistry_logWarningServiceReferenceUsageCount(service_registry_pt registry __attribute__((unused)), bundle_pt bundle, service_reference_pt ref, size_t usageCount, size_t refCount) {
if (usageCount > 0) {
fw_log(registry->framework->logger, CELIX_LOG_LEVEL_WARNING, "Service Reference destroyed with usage count is %zu, expected 0. Look for missing bundleContext_ungetService calls.", usageCount);
@@ -651,8 +539,6 @@
while (!destroyed) {
serviceReference_release(ref, &destroyed);
}
- serviceRegistry_setReferenceStatus(registry, ref, true);
-
}
hashMapIterator_destroy(iter);
hashMap_destroy(refsMap, false, false);
@@ -697,78 +583,53 @@
size_t count = 0;
const void *service = NULL;
bool valid = false;
- reference_status_t refStatus;
celixThreadRwlock_readLock(®istry->lock);
- serviceRegistry_checkReference(registry, reference, &refStatus);
- if (refStatus == REF_ACTIVE) {
- serviceReference_getServiceRegistration(reference, ®istration);
- valid = serviceRegistration_isValid(registration);
- if (valid) {
- serviceRegistration_retain(registration);
- serviceReference_increaseUsage(reference, &count);
- } else {
- *out = NULL; //invalid service registration
- }
+ serviceReference_getServiceRegistration(reference, ®istration);
+ valid = serviceRegistration_isValid(registration);
+ if (valid) {
+ serviceRegistration_retain(registration);
+ serviceReference_increaseUsage(reference, &count);
+ } else {
+ *out = NULL; //invalid service registration
}
celixThreadRwlock_unlock(®istry->lock);
- if (valid && refStatus == REF_ACTIVE) {
- if (count == 1) {
- serviceRegistration_getService(registration, bundle, &service);
- serviceReference_setService(reference, service);
- }
- serviceRegistration_release(registration);
-
- /* NOTE the out argument of sr_getService should be 'const void**'
- To ensure backwards compatibility a cast is made instead.
- */
- serviceReference_getService(reference, (void **)out);
- } else {
- if (refStatus != REF_ACTIVE) {
- serviceRegistry_logIllegalReference(registry, reference, refStatus);
- }
- status = CELIX_BUNDLE_EXCEPTION;
+ if (count == 1) {
+ serviceRegistration_getService(registration, bundle, &service);
+ serviceReference_setService(reference, service);
}
+ serviceRegistration_release(registration);
+
+ /* NOTE the out argument of sr_getService should be 'const void**'
+ To ensure backwards compatibility a cast is made instead.
+ */
+ serviceReference_getService(reference, (void **)out);
return status;
}
celix_status_t serviceRegistry_ungetService(service_registry_pt registry, bundle_pt bundle, service_reference_pt reference, bool *result) {
- celix_status_t status = CELIX_SUCCESS;
service_registration_pt reg = NULL;
const void *service = NULL;
size_t count = 0;
- celix_status_t subStatus = CELIX_SUCCESS;
- reference_status_t refStatus;
celixThreadRwlock_readLock(®istry->lock);
- serviceRegistry_checkReference(registry, reference, &refStatus);
celixThreadRwlock_unlock(®istry->lock);
- if (refStatus == REF_ACTIVE) {
- subStatus = serviceReference_decreaseUsage(reference, &count);
- if (count == 0) {
- /*NOTE the argument service of sr_getService should be 'const void**'
- To ensure backwards compatibility a cast is made instead.
- */
- serviceReference_getService(reference, (void**)&service);
- serviceReference_getServiceRegistration(reference, ®);
- if (reg != NULL) {
- serviceRegistration_ungetService(reg, bundle, &service);
- }
+ celix_status_t status = serviceReference_decreaseUsage(reference, &count);
+ if (count == 0) {
+ /*NOTE the argument service of sr_getService should be 'const void**'
+ To ensure backwards compatibility a cast is made instead.
+ */
+ serviceReference_getService(reference, (void**)&service);
+ serviceReference_getServiceRegistration(reference, ®);
+ if (reg != NULL) {
+ serviceRegistration_ungetService(reg, bundle, &service);
}
- } else {
- serviceRegistry_logIllegalReference(registry, reference, refStatus);
- status = CELIX_BUNDLE_EXCEPTION;
}
- if (result) {
- *result = (subStatus == CELIX_SUCCESS);
- }
-
-
return status;
}
@@ -804,6 +665,7 @@
celix_increaseCountServiceListener(listenerEntry);
celix_arrayList_add(listeners, listenerEntry);
}
+ celixThreadRwlock_unlock(®istry->lock);
}
if (infos != NULL && listeners != NULL) {
@@ -963,8 +825,21 @@
const char *serviceName,
celix_service_factory_t *factory,
celix_properties_t* props,
+ long reserveId,
service_registration_t **registration) {
- return serviceRegistry_registerServiceInternal(reg, (celix_bundle_t*)bnd, serviceName, (const void *) factory, props, CELIX_FACTORY_SERVICE, registration);
+ return serviceRegistry_registerServiceInternal(reg, (celix_bundle_t*)bnd, serviceName, (const void *) factory, props, reserveId, CELIX_FACTORY_SERVICE, registration);
+}
+
+celix_status_t
+celix_serviceRegistry_registerService(
+ celix_service_registry_t *reg,
+ const celix_bundle_t *bnd,
+ const char *serviceName,
+ void* service,
+ celix_properties_t* props,
+ long reserveId,
+ service_registration_t **registration) {
+ return serviceRegistry_registerServiceInternal(reg, (celix_bundle_t*)bnd, serviceName, (const void *) service, props, reserveId, CELIX_PLAIN_SERVICE, registration);
}
static celix_service_registry_listener_hook_entry_t* celix_createHookEntry(long svcId, celix_listener_hook_service_t *hook) {
@@ -1046,6 +921,128 @@
free(entry);
}
+char* celix_serviceRegistry_createFilterFor(celix_service_registry_t* registry, const char* serviceName, const char* versionRangeStr, const char* additionalFilterIn, const char* lang, bool ignoreServiceLanguage) {
+ char* filter = NULL;
+
+ //setting lang
+ if (lang == NULL || strncmp("", lang, 1) == 0) {
+ lang = CELIX_FRAMEWORK_SERVICE_C_LANGUAGE;
+ }
+
+ if (serviceName == NULL) {
+ serviceName = "*";
+ }
+
+ char* versionRange = NULL;
+ if (versionRangeStr != NULL) {
+ version_range_pt range;
+ celix_status_t status = versionRange_parse(versionRangeStr, &range);
+ if(status != CELIX_SUCCESS) {
+ framework_log(registry->framework->logger, CELIX_LOG_LEVEL_ERROR, __FUNCTION__, __BASE_FILE__, __LINE__,
+ "Error incorrect version range.");
+ return NULL;
+ }
+ versionRange = versionRange_createLDAPFilter(range, CELIX_FRAMEWORK_SERVICE_VERSION);
+ versionRange_destroy(range);
+ if (versionRange == NULL) {
+ framework_log(registry->framework->logger, CELIX_LOG_LEVEL_ERROR, __FUNCTION__, __BASE_FILE__, __LINE__,
+ "Error creating LDAP filter.");
+ return NULL;
+ }
+ }
+
+ //setting filter
+ if (ignoreServiceLanguage) {
+ if (additionalFilterIn != NULL && versionRange != NULL) {
+ asprintf(&filter, "(&(%s=%s)%s%s)", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, versionRange, additionalFilterIn);
+ } else if (versionRange != NULL) {
+ asprintf(&filter, "(&(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, versionRange);
+ } else if (additionalFilterIn != NULL) {
+ asprintf(&filter, "(&(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, additionalFilterIn);
+ } else {
+ asprintf(&filter, "(&(%s=%s))", OSGI_FRAMEWORK_OBJECTCLASS, serviceName);
+ }
+ } else {
+ if (additionalFilterIn != NULL && versionRange != NULL) {
+ asprintf(&filter, "(&(%s=%s)(%s=%s)%s%s)", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang, versionRange, additionalFilterIn);
+ } else if (versionRange != NULL) {
+ asprintf(&filter, "(&(%s=%s)(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang, versionRange);
+ } else if (additionalFilterIn != NULL) {
+ asprintf(&filter, "(&(%s=%s)(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang, additionalFilterIn);
+ } else {
+ asprintf(&filter, "(&(%s=%s)(%s=%s))", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang);
+ }
+ }
+
+ if (versionRange != NULL){
+ free(versionRange);
+ }
+
+ return filter;
+}
+
+static int celix_serviceRegistry_compareRegistrations(const void *a, const void *b) {
+ const service_registration_t* regA = a;
+ const service_registration_t* regB = b;
+
+ celix_properties_t* propsA = NULL;
+ celix_properties_t* propsB = NULL;
+ serviceRegistration_getProperties((service_registration_t*)regA, &propsA);
+ serviceRegistration_getProperties((service_registration_t*)regB, &propsB);
+
+ long servIdA = celix_properties_getAsLong(propsA, OSGI_FRAMEWORK_SERVICE_ID, 0);
+ long servIdB = celix_properties_getAsLong(propsB, OSGI_FRAMEWORK_SERVICE_ID, 0);
+
+ long servRankingA = celix_properties_getAsLong(propsA, OSGI_FRAMEWORK_SERVICE_RANKING, 0);
+ long servRankingB = celix_properties_getAsLong(propsB, OSGI_FRAMEWORK_SERVICE_RANKING, 0);
+
+ return utils_compareServiceIdsAndRanking(servIdA, servRankingA, servIdB, servRankingB);
+}
+
+celix_array_list_t* celix_serviceRegisrty_findServices(
+ celix_service_registry_t* registry,
+ const char* filterStr) {
+
+ celix_filter_t* filter = celix_filter_create(filterStr);
+ if (filter == NULL) {
+ framework_log(registry->framework->logger, CELIX_LOG_LEVEL_ERROR, __FUNCTION__, __BASE_FILE__, __LINE__,
+ "Error incorrect filter.");
+ return NULL;
+ }
+
+ celix_array_list_t *result = celix_arrayList_create();
+ celix_array_list_t* matchedRegistrations = celix_arrayList_create();
+
+ celixThreadRwlock_readLock(®istry->lock);
+
+ hash_map_iterator_t iter = hashMapIterator_construct(registry->serviceRegistrations);
+ while (hashMapIterator_hasNext(&iter)) {
+ celix_array_list_t *regs = hashMapIterator_nextValue(&iter);
+ for (int i = 0; i < celix_arrayList_size(regs); ++i) {
+ service_registration_t *reg = celix_arrayList_get(regs, i);
+ celix_properties_t* svcProps = NULL;
+ serviceRegistration_getProperties(reg, &svcProps);
+ if (svcProps != NULL && celix_filter_match(filter, svcProps)) {
+ celix_arrayList_add(matchedRegistrations, reg);
+ }
+ }
+ }
+
+ //sort matched registration and add the svc id to the result list.
+ if (celix_arrayList_size(matchedRegistrations) > 1) {
+ celix_arrayList_sort(matchedRegistrations, celix_serviceRegistry_compareRegistrations);
+ }
+ for (int i = 0; i < celix_arrayList_size(matchedRegistrations); ++i) {
+ service_registration_t* reg = celix_arrayList_get(matchedRegistrations, i);
+ celix_arrayList_addLong(result, serviceRegistration_getServiceId(reg));
+ }
+ celixThreadRwlock_unlock(®istry->lock);
+
+ celix_filter_destroy(filter);
+ celix_arrayList_destroy(matchedRegistrations);
+ return result;
+}
+
celix_array_list_t* celix_serviceRegistry_listServiceIdsForOwner(celix_service_registry_t* registry, long bndId) {
celix_array_list_t *result = celix_arrayList_create();
@@ -1121,7 +1118,7 @@
celixThreadMutex_create(&entry->mutex, NULL);
celixThreadCondition_init(&entry->cond, NULL);
- celix_array_list_t *registrations = celix_arrayList_create();
+ celix_array_list_t *references = celix_arrayList_create();
celixThreadRwlock_writeLock(®istry->lock);
celix_arrayList_add(registry->serviceListeners, entry); //use count 1
@@ -1137,7 +1134,9 @@
if (celix_filter_match(filter, props)) {
serviceRegistration_retain(registration);
long svcId = serviceRegistration_getServiceId(registration);
- celix_arrayList_add(registrations, registration);
+ service_reference_pt ref = NULL;
+ serviceRegistry_getServiceReference_internal(registry, bundle, registration, &ref);
+ celix_arrayList_add(references, ref);
//update pending register event count
celix_increasePendingRegisteredEvent(registry, svcId);
}
@@ -1150,24 +1149,23 @@
//The handling of pending registered events is to ensure that the UNREGISTERING event is always
//after the 1 or 2 REGISTERED events.
- for (int i = 0; i < celix_arrayList_size(registrations); ++i) {
- service_registration_pt reg = celix_arrayList_get(registrations, i);
- long svcId = serviceRegistration_getServiceId(reg);
- service_reference_pt ref = NULL;
- celixThreadRwlock_writeLock(®istry->lock);
- serviceRegistry_getServiceReference_internal(registry, bundle, reg, &ref);
- celixThreadRwlock_unlock(®istry->lock);
+ for (int i = 0; i < celix_arrayList_size(references); ++i) {
+ service_reference_pt ref = celix_arrayList_get(references, i);
+ long svcId = serviceReference_getServiceId(ref);
celix_service_event_t event;
event.reference = ref;
event.type = OSGI_FRAMEWORK_SERVICE_EVENT_REGISTERED;
listener->serviceChanged(listener->handle, &event);
serviceReference_release(ref, NULL);
+
+ service_registration_t* reg = NULL;
+ serviceReference_getServiceRegistration(ref, ®);
serviceRegistration_release(reg);
//update pending register event count
celix_decreasePendingRegisteredEvent(registry, svcId);
}
- celix_arrayList_destroy(registrations);
+ celix_arrayList_destroy(references);
serviceRegistry_callHooksForListenerFilter(registry, bundle, entry->filter, false);
@@ -1288,6 +1286,46 @@
}
long celix_serviceRegistry_nextSvcId(celix_service_registry_t* registry) {
- long scvId = __atomic_fetch_add(®istry->nextServiceId, 1, __ATOMIC_SEQ_CST);
+ long scvId = __atomic_fetch_add(®istry->nextServiceId, 1, __ATOMIC_RELAXED);
return scvId;
+}
+
+bool celix_serviceRegistry_isServiceRegistered(celix_service_registry_t* reg, long serviceId) {
+ bool isRegistered = false;
+ if (serviceId >= 0) {
+ celixThreadRwlock_readLock(®->lock);
+ hash_map_iterator_t iter = hashMapIterator_construct(reg->serviceRegistrations);
+ while (!isRegistered && hashMapIterator_hasNext(&iter)) {
+ celix_array_list_t *regs = hashMapIterator_nextValue(&iter);
+ for (int i = 0; i < celix_arrayList_size(regs); ++i) {
+ service_registration_t* r = celix_arrayList_get(regs, i);
+ if (serviceId == serviceRegistration_getServiceId(r)) {
+ isRegistered = true;
+ break;
+ }
+ }
+ }
+ celixThreadRwlock_unlock(®->lock);
+ }
+ return isRegistered;
+}
+
+void celix_serviceRegistry_unregisterService(celix_service_registry_t* registry, celix_bundle_t* bnd, long serviceId) {
+ service_registration_t *reg = NULL;
+ celixThreadRwlock_readLock(®istry->lock);
+ celix_array_list_t* registrations = hashMap_get(registry->serviceRegistrations, (void*)bnd);
+ for (int i = 0; i < celix_arrayList_size(registrations); ++i) {
+ service_registration_t *entry = celix_arrayList_get(registrations, i);
+ if (serviceRegistration_getServiceId(entry) == serviceId) {
+ reg = entry;
+ break;
+ }
+ }
+ celixThreadRwlock_unlock(®istry->lock);
+
+ if (reg != NULL) {
+ serviceRegistration_unregister(reg);
+ } else {
+ fw_log(registry->framework->logger, CELIX_LOG_LEVEL_ERROR, "Cannot unregister service for service id %li. This id is not present or owned by the provided bundle (bnd id %li)", serviceId, celix_bundle_getId(bnd));
+ }
}
\ No newline at end of file
diff --git a/libs/framework/src/service_registry_private.h b/libs/framework/src/service_registry_private.h
index fb8e37f..f181731 100644
--- a/libs/framework/src/service_registry_private.h
+++ b/libs/framework/src/service_registry_private.h
@@ -32,6 +32,27 @@
#include "listener_hook_service.h"
#include "service_reference.h"
+#define CELIX_SERVICE_REGISTRY_STATIC_EVENT_QUEUE_SIZE 64
+
+typedef struct celix_service_registry_event {
+ //TODO call from framework to ensure bundle entries usage count is increased
+ bool isRegistrationEvent;
+
+ //for register event
+ long serviceId;
+ char *serviceName;
+ void *svc;
+ celix_service_factory_t* factory;
+ celix_properties_t* properties;
+ void* registerData;
+ void (*registerCallback)(void *data, service_registration_t*);
+
+ //for unregister event
+ service_registration_t* registration;
+ void* unregisterData;
+ void (*unregisterCallback)(void *data);
+} celix_service_registry_event_t;
+
struct celix_serviceRegistry {
framework_pt framework;
registry_callback_t callback;
@@ -41,9 +62,6 @@
hash_map_t *serviceRegistrations; //key = bundle (reg owner), value = list ( registration )
hash_map_t *serviceReferences; //key = bundle, value = map (key = serviceId, value = reference)
- bool checkDeletedReferences; //If enabled. check if provided service references are still valid
- hash_map_t *deletedServiceReferences; //key = ref pointer, value = bool
-
long nextServiceId;
celix_array_list_t *listenerHooks; //celix_service_registry_listener_hook_entry_t*
@@ -82,12 +100,6 @@
unsigned int useCount;
} celix_service_registry_service_listener_entry_t;
-typedef enum reference_status_enum {
- REF_ACTIVE,
- REF_DELETED,
- REF_UNKNOWN
-} reference_status_t;
-
struct usageCount {
unsigned int count;
service_reference_pt reference;
diff --git a/libs/framework/src/service_tracker.c b/libs/framework/src/service_tracker.c
index 53fd31a..4a47397 100644
--- a/libs/framework/src/service_tracker.c
+++ b/libs/framework/src/service_tracker.c
@@ -34,19 +34,14 @@
#include "bundle_context_private.h"
#include "celix_array_list.h"
-static celix_status_t serviceTracker_track(celix_service_tracker_instance_t *tracker, service_reference_pt reference, celix_service_event_t *event);
-static celix_status_t serviceTracker_untrack(celix_service_tracker_instance_t *tracker, service_reference_pt reference, celix_service_event_t *event);
-static void serviceTracker_untrackTracked(celix_service_tracker_instance_t *tracker, celix_tracked_entry_t *tracked);
-static celix_status_t serviceTracker_invokeAddingService(celix_service_tracker_instance_t *tracker, service_reference_pt ref, void **svcOut);
-static celix_status_t serviceTracker_invokeAddService(celix_service_tracker_instance_t *tracker, celix_tracked_entry_t *tracked);
-static celix_status_t serviceTracker_invokeRemovingService(celix_service_tracker_instance_t *tracker, celix_tracked_entry_t *tracked);
+static celix_status_t serviceTracker_track(service_tracker_t *tracker, service_reference_pt reference, celix_service_event_t *event);
+static celix_status_t serviceTracker_untrack(service_tracker_t *tracker, service_reference_pt reference);
+static void serviceTracker_untrackTracked(service_tracker_t *tracker, celix_tracked_entry_t *tracked);
+static celix_status_t serviceTracker_invokeAddingService(service_tracker_t *tracker, service_reference_pt ref, void **svcOut);
+static celix_status_t serviceTracker_invokeAddService(service_tracker_t *tracker, celix_tracked_entry_t *tracked);
+static celix_status_t serviceTracker_invokeRemovingService(service_tracker_t *tracker, celix_tracked_entry_t *tracked);
static void serviceTracker_checkAndInvokeSetService(void *handle, void *highestSvc, const properties_t *props, const bundle_t *bnd);
-static bool serviceTracker_useHighestRankingServiceInternal(celix_service_tracker_instance_t *instance,
- const char *serviceName /*sanity*/,
- void *callbackHandle,
- void (*use)(void *handle, void *svc),
- 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));
+
#ifdef CELIX_SERVICE_TRACKER_USE_SHUTDOWN_THREAD
static void serviceTracker_addInstanceFromShutdownList(celix_service_tracker_instance_t *instance);
@@ -56,18 +51,6 @@
static void serviceTracker_serviceChanged(void *handle, celix_service_event_t *event);
-static celix_thread_once_t g_once = CELIX_THREAD_ONCE_INIT; //once for g_shutdownMutex, g_shutdownCond
-
-
-static celix_thread_mutex_t g_shutdownMutex;
-static celix_thread_cond_t g_shutdownCond;
-static celix_array_list_t *g_shutdownInstances = NULL; //value = celix_service_tracker_instance -> used for syncing with shutdown threads
-
-
-static void serviceTracker_once(void) {
- celixThreadMutex_create(&g_shutdownMutex, NULL);
- celixThreadCondition_init(&g_shutdownCond, NULL);
-}
static inline celix_tracked_entry_t* tracked_create(service_reference_pt ref, void *svc, celix_properties_t *props, celix_bundle_t *bnd) {
celix_tracked_entry_t *tracked = calloc(1, sizeof(*tracked));
@@ -128,332 +111,272 @@
return status;
}
-celix_status_t serviceTracker_createWithFilter(bundle_context_pt context, const char * filter, service_tracker_customizer_pt customizer, service_tracker_pt *tracker) {
- celix_status_t status = CELIX_SUCCESS;
- *tracker = (service_tracker_pt) calloc(1, sizeof(**tracker));
- if (!*tracker) {
- status = CELIX_ENOMEM;
- } else {
- (*tracker)->context = context;
- (*tracker)->filter = strdup(filter);
- (*tracker)->customizer = customizer;
- }
+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->context = context;
+ tracker->filter = celix_utils_strdup(filter);
+ tracker->customizer = *customizer;
+ free(customizer);
- framework_logIfError(celix_frameworkLogger_globalLogger(), status, NULL, "Cannot create service tracker [filter=%s]", filter);
+ celixThreadMutex_create(&tracker->closeSync.mutex, NULL);
+ celixThreadCondition_init(&tracker->closeSync.cond, NULL);
- return status;
+ celixThreadMutex_create(&tracker->mutex, NULL);
+ celixThreadCondition_init(&tracker->cond, NULL);
+ tracker->trackedServices = celix_arrayList_create();
+ tracker->untrackingServices = celix_arrayList_create();
+
+ celixThreadMutex_create(&tracker->mutex, NULL);
+ tracker->currentHighestServiceId = -1;
+
+ tracker->listener.handle = tracker;
+ tracker->listener.serviceChanged = (void *) serviceTracker_serviceChanged;
+
+ tracker->callbackHandle = tracker->callbackHandle;
+
+ tracker->add = tracker->add;
+ tracker->addWithProperties = tracker->addWithProperties;
+ tracker->addWithOwner = tracker->addWithOwner;
+ tracker->set = tracker->set;
+ tracker->setWithProperties = tracker->setWithProperties;
+ tracker->setWithOwner = tracker->setWithOwner;
+ tracker->remove = tracker->remove;
+ tracker->removeWithProperties = tracker->removeWithProperties;
+ tracker->removeWithOwner = tracker->removeWithOwner;
+
+ return CELIX_SUCCESS;
}
celix_status_t serviceTracker_destroy(service_tracker_pt tracker) {
- if (tracker->customizer != NULL) {
- serviceTrackerCustomizer_destroy(tracker->customizer);
- }
-
free(tracker->serviceName);
free(tracker->filter);
- free(tracker);
-
+ celixThreadMutex_destroy(&tracker->closeSync.mutex);
+ celixThreadCondition_destroy(&tracker->closeSync.cond);
+ celixThreadMutex_destroy(&tracker->mutex);
+ celixThreadCondition_destroy(&tracker->cond);
+ celix_arrayList_destroy(tracker->trackedServices);
+ celix_arrayList_destroy(tracker->untrackingServices);
+ free(tracker);
return CELIX_SUCCESS;
}
celix_status_t serviceTracker_open(service_tracker_pt tracker) {
- celix_service_listener_t *listener = NULL;
- celix_service_tracker_instance_t *instance = NULL;
- celix_status_t status = CELIX_SUCCESS;
+ celixThreadMutex_lock(&tracker->mutex);
+ bool alreadyOpen = tracker->open;
+ tracker->open = true;
+ celixThreadMutex_unlock(&tracker->mutex);
- bool addListener = false;
-
- celixThreadRwlock_writeLock(&tracker->instanceLock);
- if (tracker->instance == NULL) {
- instance = calloc(1, sizeof(*instance));
- instance->context = tracker->context;
-
- instance->closing = false;
- instance->activeServiceChangeCalls = 0;
- celixThreadMutex_create(&instance->closingLock, NULL);
- celixThreadCondition_init(&instance->activeServiceChangeCallsCond, NULL);
-
-
- celixThreadRwlock_create(&instance->lock, NULL);
- instance->trackedServices = celix_arrayList_create();
-
- celixThreadMutex_create(&instance->mutex, NULL);
- instance->currentHighestServiceId = -1;
-
- instance->listener.handle = instance;
- instance->listener.serviceChanged = (void *) serviceTracker_serviceChanged;
- listener = &instance->listener;
-
- instance->callbackHandle = tracker->callbackHandle;
- instance->filter = strdup(tracker->filter);
- if (tracker->customizer != NULL) {
- memcpy(&instance->customizer, tracker->customizer, sizeof(instance->customizer));
- }
- instance->add = tracker->add;
- instance->addWithProperties = tracker->addWithProperties;
- instance->addWithOwner = tracker->addWithOwner;
- instance->set = tracker->set;
- instance->setWithProperties = tracker->setWithProperties;
- instance->setWithOwner = tracker->setWithOwner;
- instance->remove = tracker->remove;
- instance->removeWithProperties = tracker->removeWithProperties;
- instance->removeWithOwner = tracker->removeWithOwner;
-
- tracker->instance = instance;
-
- addListener = true;
- } else {
- //already open
- framework_logIfError(tracker->context->framework->logger, status, NULL, "Tracker already open");
-
+ if (!alreadyOpen) {
+ bundleContext_addServiceListener(tracker->context, &tracker->listener, tracker->filter);
}
- celixThreadRwlock_unlock(&tracker->instanceLock);
-
- if (addListener) {
- bundleContext_addServiceListener(tracker->context, listener, tracker->filter);
- }
return CELIX_SUCCESS;
}
-celix_status_t serviceTracker_close(service_tracker_pt tracker) {
+celix_status_t serviceTracker_close(service_tracker_t* tracker) {
//put all tracked entries in tmp array list, so that the untrack (etc) calls are not blocked.
//set state to close to prevent service listener events
- celixThreadRwlock_writeLock(&tracker->instanceLock);
- celix_service_tracker_instance_t *instance = tracker->instance;
- tracker->instance = NULL;
- if (instance != NULL) {
- celixThreadMutex_lock(&instance->closingLock);
- //prevent service listener events
- instance->closing = true;
- celixThreadMutex_unlock(&instance->closingLock);
+ celixThreadMutex_lock(&tracker->mutex);
+ bool open = tracker->open;
+ tracker->open = false;
+ celixThreadMutex_unlock(&tracker->mutex);
+
+ if (!open) {
+ return CELIX_SUCCESS;
}
- celixThreadRwlock_unlock(&tracker->instanceLock);
- if (instance != NULL) {
- celixThreadRwlock_writeLock(&instance->lock);
- unsigned int size = celix_arrayList_size(instance->trackedServices);
- if(size > 0) {
- celix_tracked_entry_t *trackedEntries[size];
- for (unsigned int i = 0u; i < size; i++) {
- trackedEntries[i] = (celix_tracked_entry_t *) arrayList_get(instance->trackedServices, i);
- }
- arrayList_clear(instance->trackedServices);
- celixThreadRwlock_unlock(&instance->lock);
- //loop trough tracked entries an untrack
- for (unsigned int i = 0u; i < size; i++) {
- serviceTracker_untrackTracked(instance, trackedEntries[i]);
+ //indicate that the service tracking is closing and wait for the still pending service registration events.
+ celixThreadMutex_lock(&tracker->closeSync.mutex);
+ tracker->closeSync.closing = true;
+ while (tracker->closeSync.activeCalls > 0) {
+ celixThreadCondition_wait(&tracker->closeSync.cond, &tracker->closeSync.mutex);
+ }
+ celixThreadMutex_unlock(&tracker->closeSync.mutex);
+
+ int nrOfTrackedEntries;
+ do {
+ celixThreadMutex_lock(&tracker->mutex);
+ celix_tracked_entry_t* tracked = NULL;
+ nrOfTrackedEntries = celix_arrayList_size(tracker->trackedServices);
+ if (nrOfTrackedEntries > 0) {
+ tracked = celix_arrayList_get(tracker->trackedServices, 0);
+ celix_arrayList_removeAt(tracker->trackedServices, 0);
+ celix_arrayList_add(tracker->untrackingServices, tracked);
+ }
+ celixThreadMutex_unlock(&tracker->mutex);
+
+ if (tracked != NULL) {
+ int currentSize = nrOfTrackedEntries - 1;
+ if (currentSize == 0) {
+ serviceTracker_checkAndInvokeSetService(tracker, NULL, NULL, NULL);
+ } else {
+ celix_serviceTracker_useHighestRankingService(tracker, tracked->serviceName, tracker, NULL, NULL, serviceTracker_checkAndInvokeSetService);
}
- } else {
- celixThreadRwlock_unlock(&instance->lock);
+
+ serviceTracker_untrackTracked(tracker, tracked);
+ celixThreadMutex_lock(&tracker->mutex);
+ celix_arrayList_remove(tracker->untrackingServices, tracked);
+ celixThreadCondition_broadcast(&tracker->cond);
+ celixThreadMutex_unlock(&tracker->mutex);
}
- celixThreadMutex_lock(&instance->closingLock);
- while (instance->activeServiceChangeCalls > 0) {
- celixThreadCondition_wait(&instance->activeServiceChangeCallsCond, &instance->closingLock);
- }
- celixThreadMutex_unlock(&instance->closingLock);
+
+ celixThreadMutex_lock(&tracker->mutex);
+ nrOfTrackedEntries = celix_arrayList_size(tracker->trackedServices);
+ celixThreadMutex_unlock(&tracker->mutex);
+ } while (nrOfTrackedEntries > 0);
-#ifdef CELIX_SERVICE_TRACKER_USE_SHUTDOWN_THREAD
- //NOTE Separate thread is needed to prevent deadlock where closing is triggered from a serviceChange event and the
- // untrack -> removeServiceListener will try to remove a service listener which is being invoked and is the
- // actual thread calling the removeServiceListener.
- //
- // This can be detached -> because service listener events are ignored (closing=true) and so no callbacks
- // are made back to the celix framework / tracker owner.
- serviceTracker_addInstanceFromShutdownList(instance);
- celix_thread_t localThread;
- celixThread_create(&localThread, NULL, shutdownServiceTrackerInstanceHandler, instance);
- celixThread_detach(localThread);
-#else
- fw_removeServiceListener(instance->context->framework, instance->context->bundle, &instance->listener);
-
- celixThreadMutex_destroy(&instance->closingLock);
- celixThreadCondition_destroy(&instance->activeServiceChangeCallsCond);
- celixThreadMutex_destroy(&instance->mutex);
- celixThreadRwlock_destroy(&instance->lock);
- celix_arrayList_destroy(instance->trackedServices);
- free(instance->filter);
- free(instance);
-#endif
- }
+ fw_removeServiceListener(tracker->context->framework, tracker->context->bundle, &tracker->listener);
return CELIX_SUCCESS;
}
-service_reference_pt serviceTracker_getServiceReference(service_tracker_pt tracker) {
+service_reference_pt serviceTracker_getServiceReference(service_tracker_t* tracker) {
//TODO deprecated warning -> not locked
- celix_tracked_entry_t *tracked;
- service_reference_pt result = NULL;
- unsigned int i;
- celixThreadRwlock_readLock(&tracker->instanceLock);
- celix_service_tracker_instance_t *instance = tracker->instance;
- if (instance != NULL) {
- celixThreadRwlock_readLock(&instance->lock);
- for (i = 0; i < arrayList_size(instance->trackedServices); ++i) {
- tracked = (celix_tracked_entry_t *) arrayList_get(instance->trackedServices, i);
- result = tracked->reference;
- break;
- }
- celixThreadRwlock_unlock(&instance->lock);
+ service_reference_pt result = NULL;
+
+ 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);
+ result = tracked->reference;
+ break;
}
- celixThreadRwlock_unlock(&tracker->instanceLock);
+ celixThreadMutex_unlock(&tracker->mutex);
return result;
}
-array_list_pt serviceTracker_getServiceReferences(service_tracker_pt tracker) {
+array_list_pt serviceTracker_getServiceReferences(service_tracker_t* tracker) {
//TODO deprecated warning -> not locked
- celix_tracked_entry_t *tracked;
- unsigned int i;
array_list_pt references = NULL;
arrayList_create(&references);
- celixThreadRwlock_readLock(&tracker->instanceLock);
- celix_service_tracker_instance_t *instance = tracker->instance;
- if (instance != NULL) {
- celixThreadRwlock_readLock(&instance->lock);
- for (i = 0; i < arrayList_size(instance->trackedServices); i++) {
- tracked = (celix_tracked_entry_t*) arrayList_get(instance->trackedServices, i);
- arrayList_add(references, tracked->reference);
- }
- celixThreadRwlock_unlock(&instance->lock);
+ 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);
+ arrayList_add(references, tracked->reference);
}
- celixThreadRwlock_unlock(&tracker->instanceLock);
+ celixThreadMutex_unlock(&tracker->mutex);
return references;
}
-void *serviceTracker_getService(service_tracker_pt tracker) {
+void *serviceTracker_getService(service_tracker_t* tracker) {
//TODO deprecated warning -> not locked
- celix_tracked_entry_t* tracked;
void *service = NULL;
- unsigned int i;
- celixThreadRwlock_readLock(&tracker->instanceLock);
- celix_service_tracker_instance_t *instance = tracker->instance;
- if (instance != NULL) {
- celixThreadRwlock_readLock(&instance->lock);
- for (i = 0; i < arrayList_size(instance->trackedServices); i++) {
- tracked = (celix_tracked_entry_t*) arrayList_get(instance->trackedServices, i);
- service = tracked->service;
- break;
- }
- celixThreadRwlock_unlock(&instance->lock);
+ 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);
+ service = tracked->service;
+ break;
}
- celixThreadRwlock_unlock(&tracker->instanceLock);
+ celixThreadMutex_unlock(&tracker->mutex);
return service;
}
-array_list_pt serviceTracker_getServices(service_tracker_pt tracker) {
+array_list_pt serviceTracker_getServices(service_tracker_t* tracker) {
//TODO deprecated warning -> not locked, also make locked variant
- celix_tracked_entry_t *tracked;
- unsigned int i;
array_list_pt references = NULL;
arrayList_create(&references);
- celixThreadRwlock_readLock(&tracker->instanceLock);
- celix_service_tracker_instance_t *instance = tracker->instance;
- if (instance != NULL) {
- celixThreadRwlock_readLock(&instance->lock);
- for (i = 0; i < arrayList_size(instance->trackedServices); i++) {
- tracked = (celix_tracked_entry_t *) arrayList_get(instance->trackedServices, i);
- arrayList_add(references, tracked->service);
- }
- celixThreadRwlock_unlock(&instance->lock);
+ 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);
+ arrayList_add(references, tracked->service);
}
- celixThreadRwlock_unlock(&tracker->instanceLock);
+ celixThreadMutex_unlock(&tracker->mutex);
return references;
}
void *serviceTracker_getServiceByReference(service_tracker_pt tracker, service_reference_pt reference) {
//TODO deprecated warning -> not locked
- celix_tracked_entry_t *tracked;
void *service = NULL;
- unsigned int i;
- celixThreadRwlock_readLock(&tracker->instanceLock);
- celix_service_tracker_instance_t *instance = tracker->instance;
- if (instance != NULL) {
- celixThreadRwlock_readLock(&instance->lock);
- for (i = 0; i < arrayList_size(instance->trackedServices); i++) {
- bool equals = false;
- tracked = (celix_tracked_entry_t *) arrayList_get(instance->trackedServices, i);
- serviceReference_equals(reference, tracked->reference, &equals);
- if (equals) {
- service = tracked->service;
- break;
- }
+ celixThreadMutex_lock(&tracker->mutex);
+ for (int i = 0; i < celix_arrayList_size(tracker->trackedServices); i++) {
+ bool equals = false;
+ celix_tracked_entry_t *tracked = celix_arrayList_get(tracker->trackedServices, i);
+ serviceReference_equals(reference, tracked->reference, &equals);
+ if (equals) {
+ service = tracked->service;
+ break;
}
- celixThreadRwlock_unlock(&instance->lock);
}
- celixThreadRwlock_unlock(&tracker->instanceLock);
+ celixThreadMutex_unlock(&tracker->mutex);
return service;
}
static void serviceTracker_serviceChanged(void *handle, celix_service_event_t *event) {
- celix_service_tracker_instance_t *instance = handle;
+ service_tracker_t *tracker = handle;
- celixThreadMutex_lock(&instance->closingLock);
- bool closing = instance->closing;
+ celixThreadMutex_lock(&tracker->closeSync.mutex);
+ bool closing = tracker->closeSync.closing;
if (!closing) {
- instance->activeServiceChangeCalls += 1;
+ tracker->closeSync.activeCalls += 1;
}
- celixThreadMutex_unlock(&instance->closingLock);
+ celixThreadMutex_unlock(&tracker->closeSync.mutex);
if (!closing) {
switch (event->type) {
case OSGI_FRAMEWORK_SERVICE_EVENT_REGISTERED:
- serviceTracker_track(instance, event->reference, event);
+ serviceTracker_track(tracker, event->reference, event);
break;
case OSGI_FRAMEWORK_SERVICE_EVENT_MODIFIED:
- serviceTracker_track(instance, event->reference, event);
+ serviceTracker_track(tracker, event->reference, event);
break;
case OSGI_FRAMEWORK_SERVICE_EVENT_UNREGISTERING:
- serviceTracker_untrack(instance, event->reference, event);
+ serviceTracker_untrack(tracker, event->reference);
break;
- case OSGI_FRAMEWORK_SERVICE_EVENT_MODIFIED_ENDMATCH:
- //TODO
+ default:
+ //nop
break;
}
- celixThreadMutex_lock(&instance->closingLock);
- assert(instance->activeServiceChangeCalls > 0);
- instance->activeServiceChangeCalls -= 1;
- if (instance->activeServiceChangeCalls == 0) {
- celixThreadCondition_broadcast(&instance->activeServiceChangeCallsCond);
+ } else {
+ switch (event->type) {
+ case OSGI_FRAMEWORK_SERVICE_EVENT_UNREGISTERING:
+ //untrack the service reference, because after this call the registration can be gone
+ serviceTracker_untrack(tracker, event->reference);
+ break;
+ default:
+ //nop
+ break;
}
- celixThreadMutex_unlock(&instance->closingLock);
+ }
+
+ if (!closing) {
+ celixThreadMutex_lock(&tracker->closeSync.mutex);
+ tracker->closeSync.activeCalls -= 1;
+ celixThreadCondition_broadcast(&tracker->closeSync.cond);
+ celixThreadMutex_unlock(&tracker->closeSync.mutex);
}
}
size_t serviceTracker_nrOfTrackedServices(service_tracker_t *tracker) {
- size_t result = 0;
- celixThreadRwlock_readLock(&tracker->instanceLock);
- celixThreadRwlock_readLock(&tracker->instance->lock);
- result = (size_t) arrayList_size(tracker->instance->trackedServices);
- celixThreadRwlock_unlock(&tracker->instance->lock);
- celixThreadRwlock_unlock(&tracker->instanceLock);
+ celixThreadMutex_lock(&tracker->mutex);
+ size_t result = (size_t) arrayList_size(tracker->trackedServices);
+ celixThreadMutex_unlock(&tracker->mutex);
return result;
}
-static celix_status_t serviceTracker_track(celix_service_tracker_instance_t *instance, service_reference_pt reference, celix_service_event_t *event) {
+static celix_status_t serviceTracker_track(service_tracker_t* tracker, service_reference_pt reference, celix_service_event_t *event) {
celix_status_t status = CELIX_SUCCESS;
celix_tracked_entry_t *found = NULL;
- unsigned int i;
-
- bundleContext_retainServiceReference(instance->context, reference);
- celixThreadRwlock_readLock(&instance->lock);
- for (i = 0; i < arrayList_size(instance->trackedServices); i++) {
+ bundleContext_retainServiceReference(tracker->context, reference);
+
+ celixThreadMutex_lock(&tracker->mutex);
+ for (int i = 0; i < celix_arrayList_size(tracker->trackedServices); i++) {
bool equals = false;
- celix_tracked_entry_t *visit = (celix_tracked_entry_t*) arrayList_get(instance->trackedServices, i);
+ celix_tracked_entry_t *visit = (celix_tracked_entry_t*) arrayList_get(tracker->trackedServices, i);
serviceReference_equals(reference, visit->reference, &equals);
if (equals) {
//NOTE it is possible to get two REGISTERED events, second one can be ignored.
@@ -461,12 +384,12 @@
break;
}
}
- celixThreadRwlock_unlock(&instance->lock);
+ celixThreadMutex_unlock(&tracker->mutex);
if (found == NULL) {
//NEW entry
void *service = NULL;
- status = serviceTracker_invokeAddingService(instance, reference, &service);
+ status = serviceTracker_invokeAddingService(tracker, reference, &service);
if (status == CELIX_SUCCESS && service != NULL) {
assert(reference != NULL);
@@ -482,22 +405,24 @@
celix_tracked_entry_t *tracked = tracked_create(reference, service, props, bnd); //use count 1
- celixThreadRwlock_writeLock(&instance->lock);
- arrayList_add(instance->trackedServices, tracked);
- celixThreadRwlock_unlock(&instance->lock);
+ celixThreadMutex_lock(&tracker->mutex);
+ arrayList_add(tracker->trackedServices, tracked);
+ celixThreadMutex_unlock(&tracker->mutex);
- serviceTracker_invokeAddService(instance, tracked);
- serviceTracker_useHighestRankingServiceInternal(instance, tracked->serviceName, instance, NULL, NULL, serviceTracker_checkAndInvokeSetService);
+ serviceTracker_invokeAddService(tracker, tracked);
+ celix_serviceTracker_useHighestRankingService(tracker, tracked->serviceName, tracker, NULL, NULL, serviceTracker_checkAndInvokeSetService);
}
+ } else {
+ bundleContext_ungetServiceReference(tracker->context, reference);
}
- framework_logIfError(instance->context->framework->logger, status, NULL, "Cannot track reference");
+ framework_logIfError(tracker->context->framework->logger, status, NULL, "Cannot track reference");
return status;
}
static void serviceTracker_checkAndInvokeSetService(void *handle, void *highestSvc, const celix_properties_t *props, const celix_bundle_t *bnd) {
- celix_service_tracker_instance_t *instance = handle;
+ service_tracker_t *tracker = handle;
bool update = false;
long svcId = -1;
if (highestSvc == NULL) {
@@ -507,117 +432,131 @@
svcId = celix_properties_getAsLong(props, OSGI_FRAMEWORK_SERVICE_ID, -1);
}
if (svcId >= 0) {
- celixThreadMutex_lock(&instance->mutex);
- if (instance->currentHighestServiceId != svcId) {
- instance->currentHighestServiceId = svcId;
+ celixThreadMutex_lock(&tracker->mutex);
+ if (tracker->currentHighestServiceId != svcId) {
+ tracker->currentHighestServiceId = svcId;
update = true;
//update
}
- celixThreadMutex_unlock(&instance->mutex);
+ celixThreadMutex_unlock(&tracker->mutex);
}
if (update) {
- void *h = instance->callbackHandle;
- if (instance->set != NULL) {
- instance->set(h, highestSvc);
+ void *h = tracker->callbackHandle;
+ if (tracker->set != NULL) {
+ tracker->set(h, highestSvc);
}
- if (instance->setWithProperties != NULL) {
- instance->setWithProperties(h, highestSvc, props);
+ if (tracker->setWithProperties != NULL) {
+ tracker->setWithProperties(h, highestSvc, props);
}
- if (instance->setWithOwner != NULL) {
- instance->setWithOwner(h, highestSvc, props, bnd);
+ if (tracker->setWithOwner != NULL) {
+ tracker->setWithOwner(h, highestSvc, props, bnd);
}
}
}
-static celix_status_t serviceTracker_invokeAddService(celix_service_tracker_instance_t *instance, celix_tracked_entry_t *tracked) {
+static celix_status_t serviceTracker_invokeAddService(service_tracker_t *tracker, celix_tracked_entry_t *tracked) {
celix_status_t status = CELIX_SUCCESS;
void *customizerHandle = NULL;
added_callback_pt function = NULL;
- serviceTrackerCustomizer_getHandle(&instance->customizer, &customizerHandle);
- serviceTrackerCustomizer_getAddedFunction(&instance->customizer, &function);
+ serviceTrackerCustomizer_getHandle(&tracker->customizer, &customizerHandle);
+ serviceTrackerCustomizer_getAddedFunction(&tracker->customizer, &function);
if (function != NULL) {
function(customizerHandle, tracked->reference, tracked->service);
}
- void *handle = instance->callbackHandle;
- if (instance->add != NULL) {
- instance->add(handle, tracked->service);
+ void *handle = tracker->callbackHandle;
+ if (tracker->add != NULL) {
+ tracker->add(handle, tracked->service);
}
- if (instance->addWithProperties != NULL) {
- instance->addWithProperties(handle, tracked->service, tracked->properties);
+ if (tracker->addWithProperties != NULL) {
+ tracker->addWithProperties(handle, tracked->service, tracked->properties);
}
- if (instance->addWithOwner != NULL) {
- instance->addWithOwner(handle, tracked->service, tracked->properties, tracked->serviceOwner);
+ if (tracker->addWithOwner != NULL) {
+ tracker->addWithOwner(handle, tracked->service, tracked->properties, tracked->serviceOwner);
}
return status;
}
-static celix_status_t serviceTracker_invokeAddingService(celix_service_tracker_instance_t *instance, service_reference_pt ref, void **svcOut) {
+static celix_status_t serviceTracker_invokeAddingService(service_tracker_t *tracker, service_reference_pt ref, void **svcOut) {
celix_status_t status = CELIX_SUCCESS;
void *handle = NULL;
adding_callback_pt function = NULL;
- status = serviceTrackerCustomizer_getHandle(&instance->customizer, &handle);
+ status = serviceTrackerCustomizer_getHandle(&tracker->customizer, &handle);
if (status == CELIX_SUCCESS) {
- status = serviceTrackerCustomizer_getAddingFunction(&instance->customizer, &function);
+ status = serviceTrackerCustomizer_getAddingFunction(&tracker->customizer, &function);
}
if (status == CELIX_SUCCESS && function != NULL) {
status = function(handle, ref, svcOut);
} else if (status == CELIX_SUCCESS) {
- status = bundleContext_getService(instance->context, ref, svcOut);
+ status = bundleContext_getService(tracker->context, ref, svcOut);
}
- framework_logIfError(instance->context->framework->logger, status, NULL, "Cannot handle addingService");
+ framework_logIfError(tracker->context->framework->logger, status, NULL, "Cannot handle addingService");
return status;
}
-static celix_status_t serviceTracker_untrack(celix_service_tracker_instance_t* instance, service_reference_pt reference, celix_service_event_t *event) {
+static celix_status_t serviceTracker_untrack(service_tracker_t* tracker, service_reference_pt reference) {
celix_status_t status = CELIX_SUCCESS;
celix_tracked_entry_t *remove = NULL;
- unsigned int i;
- unsigned int size;
const char *serviceName = NULL;
- celixThreadRwlock_writeLock(&instance->lock);
- size = arrayList_size(instance->trackedServices);
- for (i = 0; i < size; i++) {
+ celixThreadMutex_lock(&tracker->mutex);
+ for (int i = 0; i < celix_arrayList_size(tracker->trackedServices); i++) {
bool equals;
- celix_tracked_entry_t *tracked = (celix_tracked_entry_t*) arrayList_get(instance->trackedServices, i);
+ celix_tracked_entry_t *tracked = (celix_tracked_entry_t*) arrayList_get(tracker->trackedServices, i);
serviceName = tracked->serviceName;
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
- arrayList_remove(instance->trackedServices, i);
+ celix_arrayList_removeAt(tracker->trackedServices, i);
+ celix_arrayList_add(tracker->untrackingServices, remove);
break;
}
}
- size = arrayList_size(instance->trackedServices); //updated size
- celixThreadRwlock_unlock(&instance->lock);
+ int size = celix_arrayList_size(tracker->trackedServices); //updated size
+ celixThreadMutex_unlock(&tracker->mutex);
- if (size == 0) {
- serviceTracker_checkAndInvokeSetService(instance, NULL, NULL, NULL);
- } else {
- serviceTracker_useHighestRankingServiceInternal(instance, serviceName, instance, NULL, NULL, serviceTracker_checkAndInvokeSetService);
+ if (remove != NULL) {
+ if (size == 0) {
+ serviceTracker_checkAndInvokeSetService(tracker, NULL, NULL, NULL);
+ } else {
+ celix_serviceTracker_useHighestRankingService(tracker, serviceName, tracker, NULL, NULL, serviceTracker_checkAndInvokeSetService);
+ }
}
- serviceTracker_untrackTracked(instance, remove);
+ //note also syncing on untracking entries, to ensure no untrack is parallel in progress
+ if (remove != NULL) {
+ serviceTracker_untrackTracked(tracker, remove);
+ celixThreadMutex_lock(&tracker->mutex);
+ celix_arrayList_remove(tracker->untrackingServices, remove);
+ celixThreadMutex_unlock(&tracker->mutex);
+ } else {
+ //ensure no untrack is still happening (to ensure it safe to unregister service)
+ celixThreadMutex_lock(&tracker->mutex);
+ while (celix_arrayList_size(tracker->untrackingServices) > 0) {
+ celixThreadCondition_wait(&tracker->cond, &tracker->mutex);
+ }
+ celixThreadMutex_unlock(&tracker->mutex);
+ }
- framework_logIfError(instance->context->framework->logger, status, NULL, "Cannot untrack reference");
+ framework_logIfError(tracker->context->framework->logger, status, NULL, "Cannot untrack reference");
return status;
}
-static void serviceTracker_untrackTracked(celix_service_tracker_instance_t *instance, celix_tracked_entry_t *tracked) {
+static void serviceTracker_untrackTracked(service_tracker_t *tracker, celix_tracked_entry_t *tracked) {
if (tracked != NULL) {
- serviceTracker_invokeRemovingService(instance, tracked);
- bundleContext_ungetServiceReference(instance->context, tracked->reference);
+ serviceTracker_invokeRemovingService(tracker, tracked);
+
+ bundleContext_ungetServiceReference(tracker->context, tracked->reference);
tracked_release(tracked);
//Wait till the useCount is 0, because the untrack should only return if the service is not used anymore.
@@ -625,37 +564,37 @@
}
}
-static celix_status_t serviceTracker_invokeRemovingService(celix_service_tracker_instance_t *instance, celix_tracked_entry_t *tracked) {
+static celix_status_t serviceTracker_invokeRemovingService(service_tracker_t *tracker, celix_tracked_entry_t *tracked) {
celix_status_t status = CELIX_SUCCESS;
bool ungetSuccess = true;
void *customizerHandle = NULL;
removed_callback_pt function = NULL;
- serviceTrackerCustomizer_getHandle(&instance->customizer, &customizerHandle);
- serviceTrackerCustomizer_getRemovedFunction(&instance->customizer, &function);
+ serviceTrackerCustomizer_getHandle(&tracker->customizer, &customizerHandle);
+ serviceTrackerCustomizer_getRemovedFunction(&tracker->customizer, &function);
if (function != NULL) {
status = function(customizerHandle, tracked->reference, tracked->service);
}
- void *handle = instance->callbackHandle;
- if (instance->remove != NULL) {
- instance->remove(handle, tracked->service);
+ void *handle = tracker->callbackHandle;
+ if (tracker->remove != NULL) {
+ tracker->remove(handle, tracked->service);
}
- if (instance->addWithProperties != NULL) {
- instance->removeWithProperties(handle, tracked->service, tracked->properties);
+ if (tracker->addWithProperties != NULL) {
+ tracker->removeWithProperties(handle, tracked->service, tracked->properties);
}
- if (instance->removeWithOwner != NULL) {
- instance->removeWithOwner(handle, tracked->service, tracked->properties, tracked->serviceOwner);
+ if (tracker->removeWithOwner != NULL) {
+ tracker->removeWithOwner(handle, tracked->service, tracked->properties, tracked->serviceOwner);
}
if (status == CELIX_SUCCESS) {
- status = bundleContext_ungetService(instance->context, tracked->reference, &ungetSuccess);
+ status = bundleContext_ungetService(tracker->context, tracked->reference, &ungetSuccess);
}
if (!ungetSuccess) {
- framework_log(instance->context->framework->logger, CELIX_LOG_LEVEL_ERROR, __FUNCTION__, __BASE_FILE__, __LINE__, "Error ungetting service");
+ framework_log(tracker->context->framework->logger, CELIX_LOG_LEVEL_ERROR, __FUNCTION__, __BASE_FILE__, __LINE__, "Error ungetting service");
status = CELIX_BUNDLE_EXCEPTION;
}
@@ -687,11 +626,12 @@
const celix_service_tracking_options_t *opts
) {
celix_service_tracker_t *tracker = NULL;
- if (ctx != NULL && opts != NULL && opts->filter.serviceName != NULL) {
+ const char* serviceName = opts->filter.serviceName == NULL ? "*" : opts->filter.serviceName;
+ if (ctx != NULL && opts != NULL) {
tracker = calloc(1, sizeof(*tracker));
if (tracker != NULL) {
tracker->context = ctx;
- tracker->serviceName = celix_utils_strdup(opts->filter.serviceName);
+ tracker->serviceName = celix_utils_strdup(serviceName);
//setting callbacks
tracker->callbackHandle = opts->callbackHandle;
@@ -705,72 +645,33 @@
tracker->addWithOwner = opts->addWithOwner;
tracker->removeWithOwner = opts->removeWithOwner;
- celixThreadRwlock_create(&tracker->instanceLock, NULL);
+ celixThreadMutex_create(&tracker->closeSync.mutex, NULL);
+ celixThreadCondition_init(&tracker->closeSync.cond, NULL);
- //setting lang
- const char *lang = opts->filter.serviceLanguage;
- if (lang == NULL || strncmp("", lang, 1) == 0) {
- lang = CELIX_FRAMEWORK_SERVICE_C_LANGUAGE;
- }
+ celixThreadMutex_create(&tracker->mutex, NULL);
+ celixThreadCondition_init(&tracker->cond, NULL);
+ tracker->trackedServices = celix_arrayList_create();
+ tracker->untrackingServices = celix_arrayList_create();
- char* versionRange = NULL;
- if(opts->filter.versionRange != NULL) {
- version_range_pt range;
- celix_status_t status = versionRange_parse(opts->filter.versionRange, &range);
- if(status != CELIX_SUCCESS) {
- framework_log(tracker->context->framework->logger, CELIX_LOG_LEVEL_ERROR, __FUNCTION__, __BASE_FILE__, __LINE__,
- "Error incorrect version range.");
- celixThreadRwlock_destroy(&tracker->instanceLock);
- free(tracker);
- return NULL;
- }
- versionRange = versionRange_createLDAPFilter(range, CELIX_FRAMEWORK_SERVICE_VERSION);
- versionRange_destroy(range);
- if(versionRange == NULL) {
- framework_log(tracker->context->framework->logger, CELIX_LOG_LEVEL_ERROR, __FUNCTION__, __BASE_FILE__, __LINE__,
- "Error creating LDAP filter.");
- celixThreadRwlock_destroy(&tracker->instanceLock);
- free(tracker);
- return NULL;
- }
- }
+ celixThreadMutex_create(&tracker->mutex, NULL);
+ tracker->currentHighestServiceId = -1;
- //setting filter
- if (opts->filter.ignoreServiceLanguage) {
- if (opts->filter.filter != NULL && versionRange != NULL) {
- asprintf(&tracker->filter, "(&(%s=%s)%s%s)", OSGI_FRAMEWORK_OBJECTCLASS, opts->filter.serviceName, versionRange, opts->filter.filter);
- } else if (versionRange != NULL) {
- asprintf(&tracker->filter, "(&(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, opts->filter.serviceName, versionRange);
- } else if (opts->filter.filter != NULL) {
- asprintf(&tracker->filter, "(&(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, opts->filter.serviceName, opts->filter.filter);
- } else {
- asprintf(&tracker->filter, "(&(%s=%s))", OSGI_FRAMEWORK_OBJECTCLASS, opts->filter.serviceName);
- }
- } else {
- if (opts->filter.filter != NULL && versionRange != NULL) {
- asprintf(&tracker->filter, "(&(%s=%s)(%s=%s)%s%s)", OSGI_FRAMEWORK_OBJECTCLASS, opts->filter.serviceName, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang, versionRange, opts->filter.filter);
- } else if (versionRange != NULL) {
- asprintf(&tracker->filter, "(&(%s=%s)(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, opts->filter.serviceName, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang, versionRange);
- } else if (opts->filter.filter != NULL) {
- asprintf(&tracker->filter, "(&(%s=%s)(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, opts->filter.serviceName, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang, opts->filter.filter);
- } else {
- asprintf(&tracker->filter, "(&(%s=%s)(%s=%s))", OSGI_FRAMEWORK_OBJECTCLASS, opts->filter.serviceName, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang);
- }
- }
+ tracker->listener.handle = tracker;
+ tracker->listener.serviceChanged = (void *) serviceTracker_serviceChanged;
+ tracker->filter = celix_serviceRegistry_createFilterFor(ctx->framework->registry, opts->filter.serviceName, opts->filter.versionRange, opts->filter.filter, opts->filter.serviceLanguage, opts->filter.ignoreServiceLanguage);
- if(versionRange != NULL){
- free(versionRange);
+ if (tracker->filter == NULL) {
+ framework_log(tracker->context->framework->logger, CELIX_LOG_LEVEL_ERROR, __FUNCTION__, __BASE_FILE__, __LINE__,
+ "Error cannot create filter.");
+ free(tracker->serviceName);
+ free(tracker);
+ return NULL;
}
serviceTracker_open(tracker);
}
} else {
- if (ctx != NULL && opts != NULL && opts->filter.serviceName == NULL) {
- framework_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, __FUNCTION__, __BASE_FILE__, __LINE__,
- "Error incorrect arguments. Missing service name.");
- } else if (ctx != NULL) {
- framework_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, __FUNCTION__, __BASE_FILE__, __LINE__, "Error incorrect arguments. Required context (%p) or opts (%p) is NULL", ctx, opts);
- }
+ framework_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, __FUNCTION__, __BASE_FILE__, __LINE__, "Error incorrect arguments. Required context (%p) or opts (%p) is NULL", ctx, opts);
}
return tracker;
}
@@ -782,7 +683,7 @@
}
}
-static bool serviceTracker_useHighestRankingServiceInternal(celix_service_tracker_instance_t *instance,
+bool celix_serviceTracker_useHighestRankingService(service_tracker_t *tracker,
const char *serviceName /*sanity*/,
void *callbackHandle,
void (*use)(void *handle, void *svc),
@@ -795,11 +696,11 @@
unsigned int i;
//first lock tracker and get highest tracked entry
- celixThreadRwlock_readLock(&instance->lock);
- unsigned int size = arrayList_size(instance->trackedServices);
+ celixThreadMutex_lock(&tracker->mutex);
+ unsigned int size = arrayList_size(tracker->trackedServices);
for (i = 0; i < size; i++) {
- tracked = (celix_tracked_entry_t *) arrayList_get(instance->trackedServices, i);
+ tracked = (celix_tracked_entry_t *) arrayList_get(tracker->trackedServices, i);
if (serviceName != NULL && tracked->serviceName != NULL && strncmp(tracked->serviceName, serviceName, 10*1024) == 0) {
const char *val = properties_getWithDefault(tracked->properties, OSGI_FRAMEWORK_SERVICE_RANKING, "0");
long rank = strtol(val, NULL, 10);
@@ -813,7 +714,7 @@
tracked_retain(highest);
}
//unlock tracker so that the tracked entry can be removed from the trackedServices list if unregistered.
- celixThreadRwlock_unlock(&instance->lock);
+ celixThreadMutex_unlock(&tracker->mutex);
if (highest != NULL) {
//got service, call, decrease use count an signal useCond after.
@@ -833,40 +734,6 @@
return called;
}
-
-bool celix_serviceTracker_useHighestRankingService(
- celix_service_tracker_t *tracker,
- const char *serviceName /*sanity*/,
- double waitTimeoutInSeconds,
- void *callbackHandle,
- void (*use)(void *handle, void *svc),
- 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)) {
-
- bool called = false;
- struct timespec start, now;
- clock_gettime(CLOCK_MONOTONIC, &start);
- do {
- celixThreadRwlock_readLock(&tracker->instanceLock);
- if (tracker->instance != NULL) {
- called = serviceTracker_useHighestRankingServiceInternal(tracker->instance, serviceName, callbackHandle, use, useWithProperties, useWithOwner);
- }
- celixThreadRwlock_unlock(&tracker->instanceLock);
-
- if (waitTimeoutInSeconds <= 0) {
- break;
- } else if (!called) {
- clock_gettime(CLOCK_MONOTONIC, &now);
- double diff = celix_difftime(&start, &now);
- if (diff > waitTimeoutInSeconds) {
- break;
- }
- usleep(10);
- }
- } while (!called);
- return called;
-}
-
size_t celix_serviceTracker_useServices(
service_tracker_t *tracker,
const char* serviceName /*sanity*/,
@@ -875,141 +742,34 @@
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)) {
size_t count = 0;
- celixThreadRwlock_readLock(&tracker->instanceLock);
- celix_service_tracker_instance_t *instance = tracker->instance;
- if (instance != NULL) {
- //first lock tracker, get tracked entries and increase use count
- celixThreadRwlock_readLock(&instance->lock);
- int size = celix_arrayList_size(instance->trackedServices);
- count = (size_t)size;
- celix_tracked_entry_t *entries[size];
- for (int i = 0; i < size; i++) {
- celix_tracked_entry_t *tracked = (celix_tracked_entry_t *) arrayList_get(instance->trackedServices, i);
- tracked_retain(tracked);
- entries[i] = tracked;
- }
- //unlock tracker so that the tracked entry can be removed from the trackedServices list if unregistered.
- celixThreadRwlock_unlock(&instance->lock);
-
- //then use entries and decrease use count
- for (int i = 0; i < size; i++) {
- celix_tracked_entry_t *entry = entries[i];
- //got service, call, decrease use count an signal useCond after.
- if (use != NULL) {
- use(callbackHandle, entry->service);
- }
- if (useWithProperties != NULL) {
- useWithProperties(callbackHandle, entry->service, entry->properties);
- }
- if (useWithOwner != NULL) {
- useWithOwner(callbackHandle, entry->service, entry->properties, entry->serviceOwner);
- }
-
- tracked_release(entry);
- }
+ //first lock tracker, get tracked entries and increase use count
+ celixThreadMutex_lock(&tracker->mutex);
+ int size = celix_arrayList_size(tracker->trackedServices);
+ count = (size_t)size;
+ celix_tracked_entry_t *entries[size];
+ for (int i = 0; i < size; i++) {
+ celix_tracked_entry_t *tracked = (celix_tracked_entry_t *) arrayList_get(tracker->trackedServices, i);
+ tracked_retain(tracked);
+ entries[i] = tracked;
}
- celixThreadRwlock_unlock(&tracker->instanceLock);
+ //unlock tracker so that the tracked entry can be removed from the trackedServices list if unregistered.
+ celixThreadMutex_unlock(&tracker->mutex);
+
+ //then use entries and decrease use count
+ for (int i = 0; i < size; i++) {
+ celix_tracked_entry_t *entry = entries[i];
+ //got service, call, decrease use count an signal useCond after.
+ if (use != NULL) {
+ use(callbackHandle, entry->service);
+ }
+ if (useWithProperties != NULL) {
+ useWithProperties(callbackHandle, entry->service, entry->properties);
+ }
+ if (useWithOwner != NULL) {
+ useWithOwner(callbackHandle, entry->service, entry->properties, entry->serviceOwner);
+ }
+
+ tracked_release(entry);
+ }
return count;
-}
-
-void celix_serviceTracker_syncForFramework(void *fw) {
- celixThread_once(&g_once, serviceTracker_once);
- celixThreadMutex_lock(&g_shutdownMutex);
- size_t count = 0;
- do {
- count = 0;
- if (g_shutdownInstances != NULL) {
- for (int i = 0; i < celix_arrayList_size(g_shutdownInstances); ++i) {
- celix_service_tracker_instance_t *instance = celix_arrayList_get(g_shutdownInstances, i);
- if (instance->context->framework == fw) {
- count += 1;
- }
- }
- }
- if (count > 0) {
- pthread_cond_wait(&g_shutdownCond, &g_shutdownMutex);
- }
- } while (count > 0);
-
- if (g_shutdownInstances != NULL && celix_arrayList_size(g_shutdownInstances) == 0) {
- celix_arrayList_destroy(g_shutdownInstances);
- g_shutdownInstances = NULL;
- }
- celixThreadMutex_unlock(&g_shutdownMutex);
-}
-
-void celix_serviceTracker_syncForContext(void *ctx) {
- celixThread_once(&g_once, serviceTracker_once);
- celixThreadMutex_lock(&g_shutdownMutex);
- size_t count;
- do {
- count = 0;
- if (g_shutdownInstances != NULL) {
- for (int i = 0; i < celix_arrayList_size(g_shutdownInstances); ++i) {
- celix_service_tracker_instance_t *instance = celix_arrayList_get(g_shutdownInstances, i);
- if (instance->context == ctx) {
- count += 1;
- }
- }
- }
- if (count > 0) {
- pthread_cond_wait(&g_shutdownCond, &g_shutdownMutex);
- }
- } while (count > 0);
-
- if (g_shutdownInstances != NULL && celix_arrayList_size(g_shutdownInstances) == 0) {
- celix_arrayList_destroy(g_shutdownInstances);
- g_shutdownInstances = NULL;
- }
- celixThreadMutex_unlock(&g_shutdownMutex);
-}
-
-#ifdef CELIX_SERVICE_TRACKER_USE_SHUTDOWN_THREAD
-static void serviceTracker_addInstanceFromShutdownList(celix_service_tracker_instance_t *instance) {
- celixThread_once(&g_once, serviceTracker_once);
- celixThreadMutex_lock(&g_shutdownMutex);
- if (g_shutdownInstances == NULL) {
- g_shutdownInstances = celix_arrayList_create();
- }
- celix_arrayList_add(g_shutdownInstances, instance);
- celixThreadMutex_unlock(&g_shutdownMutex);
-}
-
-static void serviceTracker_remInstanceFromShutdownList(celix_service_tracker_instance_t *instance) {
- celixThread_once(&g_once, serviceTracker_once);
- celixThreadMutex_lock(&g_shutdownMutex);
- if (g_shutdownInstances != NULL) {
- size_t size = celix_arrayList_size(g_shutdownInstances);
- for (size_t i = 0; i < size; ++i) {
- celix_array_list_entry_t entry;
- memset(&entry, 0, sizeof(entry));
- entry.voidPtrVal = instance;
- celix_arrayList_removeEntry(g_shutdownInstances, entry);
- }
- if (celix_arrayList_size(g_shutdownInstances) == 0) {
- celix_arrayList_destroy(g_shutdownInstances);
- g_shutdownInstances = NULL;
- }
- celixThreadCondition_broadcast(&g_shutdownCond);
- }
- celixThreadMutex_unlock(&g_shutdownMutex);
-}
-
-static void* shutdownServiceTrackerInstanceHandler(void *data) {
- celix_service_tracker_instance_t *instance = data;
-
- fw_removeServiceListener(instance->context->framework, instance->context->bundle, &instance->listener);
-
- celixThreadMutex_destroy(&instance->closingLock);
- celixThreadCondition_destroy(&instance->activeServiceChangeCallsCond);
- celixThreadMutex_destroy(&instance->mutex);
- celixThreadRwlock_destroy(&instance->lock);
- celix_arrayList_destroy(instance->trackedServices);
- free(instance->filter);
-
- serviceTracker_remInstanceFromShutdownList(instance);
- free(instance);
-
- return NULL;
-}
-#endif
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/libs/framework/src/service_tracker_private.h b/libs/framework/src/service_tracker_private.h
index 47b8fa7..4bc1b27 100644
--- a/libs/framework/src/service_tracker_private.h
+++ b/libs/framework/src/service_tracker_private.h
@@ -23,51 +23,14 @@
#include "service_tracker.h"
#include "celix_types.h"
-//instance for an active per open statement and removed per close statement
-typedef struct celix_service_tracker_instance {
- celix_thread_mutex_t closingLock; //projects closing and activeServiceChangeCalls
- bool closing; //when true the service tracker instance is being closed and all calls from the service listener are ignored
- size_t activeServiceChangeCalls;
- celix_thread_cond_t activeServiceChangeCallsCond;
-
- bundle_context_t *context;
- char * filter;
-
- service_tracker_customizer_t customizer;
- celix_service_listener_t listener;
-
- 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 (*setWithProperties)(void *handle, void *svc, const properties_t *props); //highest ranking
- void (*addWithProperties)(void *handle, void *svc, const properties_t *props);
- void (*removeWithProperties)(void *handle, void *svc, const properties_t *props);
- void (*modifiedWithProperties)(void *handle, void *svc, const properties_t *props);
-
- void (*setWithOwner)(void *handle, void *svc, const properties_t *props, const bundle_t *owner); //highest ranking
- void (*addWithOwner)(void *handle, void *svc, const properties_t *props, const bundle_t *owner);
- void (*removeWithOwner)(void *handle, void *svc, const properties_t *props, const bundle_t *owner);
- void (*modifiedWithOwner)(void *handle, void *svc, const properties_t *props, const bundle_t *owner);
-
- celix_thread_rwlock_t lock; //projects trackedServices
- array_list_t *trackedServices;
-
- celix_thread_mutex_t mutex; //protect current highest service id
- long currentHighestServiceId;
-
- celix_thread_t shutdownThread; //will be created when this instance is shutdown
-} celix_service_tracker_instance_t;
-
struct celix_serviceTracker {
bundle_context_t *context;
char* serviceName;
char* filter;
- service_tracker_customizer_t *customizer;
+
+ service_tracker_customizer_t customizer;
+ celix_service_listener_t listener;
void *callbackHandle;
@@ -86,9 +49,19 @@
void (*removeWithOwner)(void *handle, void *svc, const properties_t *props, const bundle_t *owner);
void (*modifiedWithOwner)(void *handle, void *svc, const properties_t *props, const bundle_t *owner);
- celix_thread_rwlock_t instanceLock;
- celix_service_tracker_instance_t *instance; /*NULL -> close, !NULL->open*/
+ struct {
+ 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
+ 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 cond;
+ celix_array_list_t *trackedServices;
+ celix_array_list_t *untrackingServices;
+ bool open;
+ long currentHighestServiceId;
};
typedef struct celix_tracked_entry {
diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt
index 697a3b0..d00b14b 100644
--- a/libs/utils/gtest/CMakeLists.txt
+++ b/libs/utils/gtest/CMakeLists.txt
@@ -18,6 +18,7 @@
add_executable(test_utils
src/LogUtilsTestSuite.cc
+ src/TimeUtilsTestSuite.cc
)
target_link_libraries(test_utils PRIVATE Celix::utils GTest::gtest GTest::gtest_main)
diff --git a/libs/utils/gtest/src/TimeUtilsTestSuite.cc b/libs/utils/gtest/src/TimeUtilsTestSuite.cc
new file mode 100644
index 0000000..ccbca69
--- /dev/null
+++ b/libs/utils/gtest/src/TimeUtilsTestSuite.cc
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include <thread>
+#include <chrono>
+
+#include "celix_utils.h"
+
+class TimeUtilsTestSuite : public ::testing::Test {};
+
+TEST_F(TimeUtilsTestSuite, GetTimeTest) {
+ timespec t1;
+ timespec t3;
+ clock_gettime(CLOCK_MONOTONIC, &t1);
+ std::this_thread::sleep_for(std::chrono::seconds {1} );
+ auto t2 = celix_gettime(CLOCK_MONOTONIC);
+ std::this_thread::sleep_for(std::chrono::seconds {1} );
+ clock_gettime(CLOCK_MONOTONIC, &t3);
+
+ EXPECT_GT(t2.tv_sec, t1.tv_sec);
+ EXPECT_GT(t3.tv_sec, t2.tv_sec);
+}
+
+
+TEST_F(TimeUtilsTestSuite, DiffTimeTest) {
+ auto t1 = celix_gettime(CLOCK_MONOTONIC);
+ std::this_thread::sleep_for(std::chrono::microseconds {10} );
+ auto t2 = celix_gettime(CLOCK_MONOTONIC);
+ auto diff = celix_difftime(&t1, &t2);
+ EXPECT_GE(diff, 0.00001 /*10 us*/);
+ EXPECT_LT(diff, 0.01 /*1 ms*/);
+}
+
+TEST_F(TimeUtilsTestSuite, ElapsedTimeTest) {
+ auto t1 = celix_gettime(CLOCK_MONOTONIC);
+ std::this_thread::sleep_for(std::chrono::microseconds {10} );
+ auto diff = celix_elapsedtime(CLOCK_MONOTONIC, t1);
+ EXPECT_GE(diff, 0.00001 /*10 us*/);
+ EXPECT_LT(diff, 0.1 /*1/10 s, note do want to rely on accuracy of sleep_for*/);
+}
\ No newline at end of file
diff --git a/libs/utils/include/celix_log_utils.h b/libs/utils/include/celix_log_utils.h
index 43ea714..b90cb4d 100644
--- a/libs/utils/include/celix_log_utils.h
+++ b/libs/utils/include/celix_log_utils.h
@@ -53,7 +53,6 @@
* Logs a message to stdout/stderr using the provided logName and log level.
* If the provided log level is higher than info, stderr will be used.
*
- * function is not thread safe (multiple printf's are used).
*/
void celix_logUtils_logToStdout(const char *logName, celix_log_level_e level, const char *format, ...);
@@ -66,7 +65,6 @@
*
* If the argument file or function is NULL, the arguments file, function and line are not used.
*
- * function is not thread safe (multiple printf's are used).
*/
void celix_logUtils_logToStdoutDetails(const char *logName, celix_log_level_e level, const char* file, const char* function, int line, const char *format, ...);
@@ -75,7 +73,6 @@
* Logs a message to stdout/stderr using the provided logName and log level.
* If the provided log level is higher than info, stderr will be used.
*
- * function is not thread safe (multiple printf's are used).
*/
void celix_logUtils_vLogToStdout(const char *logName, celix_log_level_e level, const char *format, va_list formatArgs);
@@ -88,14 +85,12 @@
*
* If the argument file or function is NULL, the arguments file, function and line are not used.
*
- * function is not thread safe (multiple printf's are used).
*/
void celix_logUtils_vLogToStdoutDetails(const char *logName, celix_log_level_e level, const char* file, const char* function, int line, const char *format, va_list formatArgs);
/**
* Prints a backtrace to the provided output stream.
*
- * function is not thread safe (multiple printf's are used).
*/
void celix_logUtils_printBacktrace(FILE* stream);
diff --git a/libs/utils/include/celix_utils.h b/libs/utils/include/celix_utils.h
index 2a7ba35..d39e8e2 100644
--- a/libs/utils/include/celix_utils.h
+++ b/libs/utils/include/celix_utils.h
@@ -71,6 +71,17 @@
*/
double celix_difftime(const struct timespec *tBegin, const struct timespec *tEnd);
+/**
+ * Returns the current time as struct timespec
+ * @param clockId The clock to use (see time.h)
+ */
+struct timespec celix_gettime(clockid_t clockId);
+
+/**
+ * Returns the elapsed time - in seconds - relative to the startTime
+ * using the clock for the provided clockid.
+ */
+double celix_elapsedtime(clockid_t clockId, struct timespec startTime);
/**
* Creates a hash from a string
diff --git a/libs/utils/private/test/utils_test.cpp b/libs/utils/private/test/utils_test.cpp
index dfd9066..0954b38 100644
--- a/libs/utils/private/test/utils_test.cpp
+++ b/libs/utils/private/test/utils_test.cpp
@@ -271,21 +271,21 @@
TEST(utils, compareServiceIdsAndRanking){
int ret;
- //service 1 is higher ranked and has a irrelevant ID
+ //service 1 is higher ranked and has a irrelevant ID, so result is -1 (smaller -> sorted before service 2)
ret = utils_compareServiceIdsAndRanking(2,2,1,1);
- LONGS_EQUAL(1, ret);
+ LONGS_EQUAL(-1, ret);
- //service 1 is equally ranked and has a lower ID
+ //service 1 is equally ranked and has a lower ID. so result is -1 (smaller -> sorted before service 2)
ret = utils_compareServiceIdsAndRanking(1,1,2,1);
+ LONGS_EQUAL(-1, ret);
+
+ //service 1 is equally ranked and has a higher ID, so result is 1 (larger -> sorted after service 2)
+ ret = utils_compareServiceIdsAndRanking(2,1,1,1);
LONGS_EQUAL(1, ret);
- //service 1 is equally ranked and has a higher ID
- ret = utils_compareServiceIdsAndRanking(2,1,1,1);
- LONGS_EQUAL(-1, ret);
-
- //service 1 is lower ranked and has a irrelevant ID
+ //service 1 is lower ranked and has a irrelevant ID, so result is -1 (larger -> sorted after service 2)
ret = utils_compareServiceIdsAndRanking(1,1,2,2);
- LONGS_EQUAL(-1, ret);
+ LONGS_EQUAL(1, ret);
//service 1 is equal in ID and irrelevantly ranked
ret = utils_compareServiceIdsAndRanking(1,1,1,1);
diff --git a/libs/utils/src/celix_log_utils.c b/libs/utils/src/celix_log_utils.c
index 236d0d9..7826725 100644
--- a/libs/utils/src/celix_log_utils.c
+++ b/libs/utils/src/celix_log_utils.c
@@ -26,6 +26,7 @@
#include <stdio.h>
#include <time.h>
#include <string.h>
+#include <pthread.h>
static const char * const CELIX_STRING_VALUE_DISABLED = "disabled";
static const char * const CELIX_STRING_VALUE_FATAL = "fatal";
@@ -127,6 +128,8 @@
celix_logUtils_vLogToStdoutDetails(logName, level, NULL, NULL, 0, format, formatArgs);
}
+static pthread_mutex_t globalMutex = PTHREAD_MUTEX_INITIALIZER;
+
void celix_logUtils_vLogToStdoutDetails(const char *logName, celix_log_level_e level, const char* file, const char* function, int line, const char *format, va_list formatArgs) {
if (level == CELIX_LOG_LEVEL_DISABLED) {
//silently ignore
@@ -143,22 +146,21 @@
out = stderr;
}
+ pthread_mutex_lock(&globalMutex);
fprintf(out, "[%i-%02i-%02iT%02i:%02i:%02i] ", local.tm_year + 1900, local.tm_mon+1, local.tm_mday, local.tm_hour, local.tm_min, local.tm_sec);
if (file != NULL && function != NULL) {
(void)file; //note not using file
- fprintf(out, "[%s] [%s] [%s:%i] ", celix_logUtils_logLevelToString(level), logName, function, line);
+ fprintf(out, "[%7s] [%s] [%s:%i] ", celix_logUtils_logLevelToString(level), logName, function, line);
} else {
- fprintf(out, "[%s] [%s] ", celix_logUtils_logLevelToString(level), logName);
+ fprintf(out, "[%7s] [%s] ", celix_logUtils_logLevelToString(level), logName);
}
-
vfprintf(out, format, formatArgs);
-
fprintf(out, "\n");
- fflush(out);
if (level >= CELIX_LOG_LEVEL_FATAL) {
fprintf(out, "Backtrace:\n");
- fflush(out);
celix_logUtils_inlinePrintBacktrace(out);
}
+ fflush(out);
+ pthread_mutex_unlock(&globalMutex);
}
\ No newline at end of file
diff --git a/libs/utils/src/utils.c b/libs/utils/src/utils.c
index f82bd26..85be134 100644
--- a/libs/utils/src/utils.c
+++ b/libs/utils/src/utils.c
@@ -150,9 +150,9 @@
if (servId == otherServId) {
result = 0;
} else if (servRank != otherServRank) {
- result = servRank < otherServRank ? -1 : 1;
+ result = servRank < otherServRank ? 1 : -1;
} else { //equal service rank, compare service ids
- result = servId < otherServId ? 1 : -1;
+ result = servId < otherServId ? -1 : 1;
}
return result;
@@ -171,6 +171,21 @@
return ((double)diff.tv_sec) + diff.tv_nsec / 1000000000.0;
}
+struct timespec celix_gettime(clockid_t clockId) {
+ struct timespec t;
+ errno = 0;
+ int rc = clock_gettime(clockId, &t);
+ if (rc != 0) {
+ fprintf(stderr, "Cannot get time from clock_gettime. Error is %s\n", strerror(errno));
+ }
+ return t;
+}
+
+double celix_elapsedtime(clockid_t clockId, struct timespec startTime) {
+ struct timespec now = celix_gettime(clockId);
+ return celix_difftime(&startTime, &now);
+}
+
char* celix_utils_strdup(const char *str) {
if (str != NULL) {
return strndup(str, CELIX_UTILS_MAX_STRLEN);