Repository: celix Updated Branches: refs/heads/develop 02ee6c4e5 -> bcda87f54
CELIX-452: Changes the behaviour of the serviceTracker_close, so that removing the service listener is done on a separate thread. This is needed because the previous commits added sync behaviour to the serviceChanged handling. As result a serviceChange event will ensure that removeServiceListener call will only returned if it is not used anymore. This can introduces a deadlock when removing a service listener from a service change event callback. This is specifically the case with service trackers, because they remove a service listeners in the sync close call. The removing of the service listener is now done on a detached thread. During bundle stop and framework stop a sync call is used to ensure all detached "service tracker shutdown" threads are done. Because the current api support stopping and restarted a service tracker, some considerable refactoring was needed in the general service tracker internals. Project: http://git-wip-us.apache.org/repos/asf/celix/repo Commit: http://git-wip-us.apache.org/repos/asf/celix/commit/bcda87f5 Tree: http://git-wip-us.apache.org/repos/asf/celix/tree/bcda87f5 Diff: http://git-wip-us.apache.org/repos/asf/celix/diff/bcda87f5 Branch: refs/heads/develop Commit: bcda87f5431bbb8eab570a75b350971e3753f18a Parents: 02ee6c4 Author: Pepijn Noltes <[email protected]> Authored: Tue Jul 17 21:25:26 2018 +0200 Committer: Pepijn Noltes <[email protected]> Committed: Tue Jul 17 21:25:26 2018 +0200 ---------------------------------------------------------------------- libs/framework/include/celix_framework.h | 1 + libs/framework/include/service_tracker.h | 11 + .../include/service_tracker_customizer.h | 7 + .../test/service_tracker_customizer_test.cpp | 2 +- libs/framework/src/framework.c | 7 +- libs/framework/src/service_tracker.c | 743 ++++++++++++------- libs/framework/src/service_tracker_customizer.c | 2 +- .../src/service_tracker_customizer_private.h | 49 -- libs/framework/src/service_tracker_private.h | 48 +- libs/framework/tst/CMakeLists.txt | 1 + .../tst/bundle_context_services_test.cpp | 3 + 11 files changed, 552 insertions(+), 322 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/celix/blob/bcda87f5/libs/framework/include/celix_framework.h ---------------------------------------------------------------------- diff --git a/libs/framework/include/celix_framework.h b/libs/framework/include/celix_framework.h index ac3193e..39116f6 100644 --- a/libs/framework/include/celix_framework.h +++ b/libs/framework/include/celix_framework.h @@ -21,6 +21,7 @@ typedef struct framework celix_framework_t; + #include "celix_types.h" #include "celix_properties.h" http://git-wip-us.apache.org/repos/asf/celix/blob/bcda87f5/libs/framework/include/service_tracker.h ---------------------------------------------------------------------- diff --git a/libs/framework/include/service_tracker.h b/libs/framework/include/service_tracker.h index 7fb5f54..40559db 100644 --- a/libs/framework/include/service_tracker.h +++ b/libs/framework/include/service_tracker.h @@ -135,6 +135,17 @@ void celix_serviceTracker_useServices( 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 } #endif http://git-wip-us.apache.org/repos/asf/celix/blob/bcda87f5/libs/framework/include/service_tracker_customizer.h ---------------------------------------------------------------------- diff --git a/libs/framework/include/service_tracker_customizer.h b/libs/framework/include/service_tracker_customizer.h index 19672d8..2ddd437 100644 --- a/libs/framework/include/service_tracker_customizer.h +++ b/libs/framework/include/service_tracker_customizer.h @@ -46,6 +46,13 @@ typedef celix_status_t (*removed_callback_pt)(void *handle, service_reference_pt typedef struct serviceTrackerCustomizer *service_tracker_customizer_pt; typedef struct serviceTrackerCustomizer service_tracker_customizer_t; +struct serviceTrackerCustomizer { + void * handle; + celix_status_t (*addingService)(void * handle, service_reference_pt reference, void **service); + celix_status_t (*addedService)(void * handle, service_reference_pt reference, void * service); + celix_status_t (*modifiedService)(void * handle, service_reference_pt reference, void * service); + celix_status_t (*removedService)(void * handle, service_reference_pt reference, void * service); +}; FRAMEWORK_EXPORT celix_status_t serviceTrackerCustomizer_create(void *handle, adding_callback_pt addingFunction, http://git-wip-us.apache.org/repos/asf/celix/blob/bcda87f5/libs/framework/private/test/service_tracker_customizer_test.cpp ---------------------------------------------------------------------- diff --git a/libs/framework/private/test/service_tracker_customizer_test.cpp b/libs/framework/private/test/service_tracker_customizer_test.cpp index a80cd29..ced8f3c 100644 --- a/libs/framework/private/test/service_tracker_customizer_test.cpp +++ b/libs/framework/private/test/service_tracker_customizer_test.cpp @@ -34,7 +34,7 @@ extern "C" { -#include "service_tracker_customizer_private.h" +#include "service_tracker_customizer.h" #include "service_reference.h" #include "celix_log.h" http://git-wip-us.apache.org/repos/asf/celix/blob/bcda87f5/libs/framework/src/framework.c ---------------------------------------------------------------------- diff --git a/libs/framework/src/framework.c b/libs/framework/src/framework.c index fcfe07f..2bd5d84 100644 --- a/libs/framework/src/framework.c +++ b/libs/framework/src/framework.c @@ -44,6 +44,7 @@ #include "bundle_private.h" #include "celix_bundle_context.h" #include "bundle_context_private.h" +#include "service_tracker.h" typedef celix_status_t (*create_function_fp)(bundle_context_t *context, void **userData); typedef celix_status_t (*start_function_fp)(void *userData, bundle_context_t *context); @@ -184,7 +185,7 @@ static inline void listener_release(celix_fw_service_listener_entry_t *entry) { assert(entry->useCount > 0); entry->useCount -= 1; if (entry->useCount == 0) { - celixThreadCondition_signal(&entry->useCond); + celixThreadCondition_broadcast(&entry->useCond); } celixThreadMutex_unlock(&entry->mutex); } @@ -389,6 +390,7 @@ celix_status_t framework_destroy(framework_pt framework) { int i; for (i = 0; i < arrayList_size(framework->requests); i++) { request_pt request = arrayList_get(framework->requests, i); + free(request->bundleSymbolicName); free(request); } arrayList_destroy(framework->requests); @@ -1057,6 +1059,7 @@ celix_status_t fw_stopBundle(framework_pt framework, bundle_pt bundle, bool reco } if (id != 0) { + celix_serviceTracker_syncForContext(bundle->context); status = CELIX_DO_IF(status, serviceRegistry_clearServiceRegistrations(framework->registry, bundle)); if (status == CELIX_SUCCESS) { module_pt module = NULL; @@ -1103,6 +1106,8 @@ celix_status_t fw_stopBundle(framework_pt framework, bundle_pt bundle, bool reco fw_fireBundleEvent(framework, OSGI_FRAMEWORK_BUNDLE_EVENT_STOPPED, bundle); } + celix_serviceTracker_syncForFramework(framework); + return status; } http://git-wip-us.apache.org/repos/asf/celix/blob/bcda87f5/libs/framework/src/service_tracker.c ---------------------------------------------------------------------- diff --git a/libs/framework/src/service_tracker.c b/libs/framework/src/service_tracker.c index 4e13275..87dff42 100644 --- a/libs/framework/src/service_tracker.c +++ b/libs/framework/src/service_tracker.c @@ -28,22 +28,39 @@ #include "constants.h" #include "service_reference.h" #include "celix_log.h" -#include "service_tracker_customizer_private.h" #include "bundle_context_private.h" #include "celix_array_list.h" -static celix_status_t serviceTracker_track(service_tracker_pt tracker, service_reference_pt reference, celix_service_event_t *event); -static celix_status_t serviceTracker_untrack(service_tracker_pt tracker, service_reference_pt reference, celix_service_event_t *event); -static void serviceTracker_untrackTracked(celix_service_tracker_t *tracker, celix_tracked_entry_t *tracked); -static celix_status_t serviceTracker_invokeAddingService(celix_service_tracker_t *tracker, service_reference_pt ref, void **svcOut); -static celix_status_t serviceTracker_invokeAddService(celix_service_tracker_t *tracker, celix_tracked_entry_t *tracked); -static celix_status_t serviceTracker_invokeModifiedService(celix_service_tracker_t *tracker, celix_tracked_entry_t *tracked); -static celix_status_t serviceTracker_invokeRemovingService(celix_service_tracker_t *tracker, celix_tracked_entry_t *tracked); +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_invokeModifiedService(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 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)); + +static void serviceTracker_addInstanceFromShutdownList(celix_service_tracker_instance_t *instance); +static void serviceTracker_remInstanceFromShutdownList(celix_service_tracker_instance_t *instance); + +static celix_thread_once_t g_once = CELIX_THREAD_ONCE_INIT; +static celix_thread_mutex_t g_mutex; +static celix_thread_cond_t g_cond; +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_mutex, NULL); + celixThreadCondition_init(&g_cond, 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)); - tracked->reference = ref; tracked->service = svc; tracked->properties = props; @@ -67,7 +84,7 @@ static inline void tracked_release(celix_tracked_entry_t *tracked) { assert(tracked->useCount > 0); tracked->useCount -= 1; if (tracked->useCount == 0) { - celixThreadCondition_signal(&tracked->useCond); + celixThreadCondition_broadcast(&tracked->useCond); } celixThreadMutex_unlock(&tracked->mutex); } @@ -111,15 +128,7 @@ celix_status_t serviceTracker_createWithFilter(bundle_context_pt context, const } else { (*tracker)->context = context; (*tracker)->filter = strdup(filter); - - celixThreadRwlock_create(&(*tracker)->lock, NULL); - (*tracker)->trackedServices = NULL; - arrayList_create(&(*tracker)->trackedServices); (*tracker)->customizer = customizer; - (*tracker)->listener = NULL; - - celixThreadMutex_create(&(*tracker)->mutex, NULL); - (*tracker)->currentHighestServiceId = -1; } framework_logIfError(logger, status, NULL, "Cannot create service tracker [filter=%s]", filter); @@ -128,24 +137,10 @@ celix_status_t serviceTracker_createWithFilter(bundle_context_pt context, const } celix_status_t serviceTracker_destroy(service_tracker_pt tracker) { - if (tracker->listener != NULL) { - bundleContext_removeServiceListener(tracker->context, tracker->listener); - } - if (tracker->customizer != NULL) { + if (tracker->customizer != NULL) { serviceTrackerCustomizer_destroy(tracker->customizer); } - celixThreadRwlock_writeLock(&tracker->lock); - arrayList_destroy(tracker->trackedServices); - celixThreadRwlock_unlock(&tracker->lock); - - - if (tracker->listener != NULL) { - free (tracker->listener); - } - - celixThreadRwlock_destroy(&tracker->lock); - free(tracker->filter); free(tracker); @@ -153,35 +148,71 @@ celix_status_t serviceTracker_destroy(service_tracker_pt tracker) { } celix_status_t serviceTracker_open(service_tracker_pt tracker) { - celix_service_listener_t *listener; - array_list_pt initial = NULL; - celix_status_t status = CELIX_SUCCESS; - listener = (celix_service_listener_t *) malloc(sizeof(*listener)); - - status = bundleContext_getServiceReferences(tracker->context, NULL, tracker->filter, &initial); //REF COUNT to 1 - if (status == CELIX_SUCCESS && listener != NULL) { - service_reference_pt initial_reference; - unsigned int i; - - listener->handle = tracker; - listener->serviceChanged = (void *) serviceTracker_serviceChanged; - status = bundleContext_addServiceListener(tracker->context, listener, tracker->filter); - if (status == CELIX_SUCCESS) { - tracker->listener = listener; + celix_service_listener_t *listener = NULL; + celix_service_tracker_instance_t *instance = NULL; + array_list_pt initial = NULL; + celix_status_t status = CELIX_SUCCESS; - for (i = 0; i < arrayList_size(initial); i++) { - initial_reference = (service_reference_pt) arrayList_get(initial, i); - serviceTracker_track(tracker, initial_reference, NULL); //REF COUNT to 2 - bundleContext_ungetServiceReference(tracker->context, initial_reference); //REF COUNT to 1 - } + celixThreadRwlock_writeLock(&tracker->instanceLock); + if (tracker->instance == NULL) { + instance = calloc(1, sizeof(*instance)); + instance->context = tracker->context; - arrayList_destroy(initial); + instance->closing = false; + instance->activeServiceChangeCalls = 0; + celixThreadMutex_create(&instance->closingLock, NULL); + celixThreadCondition_init(&instance->activeServiceChangeCallsCond, NULL); - initial = NULL; - } - } - if(status != CELIX_SUCCESS && listener != 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; + + status = bundleContext_getServiceReferences(tracker->context, NULL, tracker->filter, &initial); //REF COUNT to 1 + + tracker->instance = instance; + } else { + //already open + } + celixThreadRwlock_unlock(&tracker->instanceLock); + + //TODO add fw call which adds a service listener and return the then valid service references. + if (status == CELIX_SUCCESS && listener != NULL) { //register service listener + status = bundleContext_addServiceListener(tracker->context, listener, tracker->filter); + } + if (status == CELIX_SUCCESS && initial != NULL) { + service_reference_pt initial_reference; + unsigned int i; + for (i = 0; i < arrayList_size(initial); i++) { + initial_reference = (service_reference_pt) arrayList_get(initial, i); + serviceTracker_track(instance, initial_reference, NULL); //REF COUNT to 2 + bundleContext_ungetServiceReference(tracker->context, initial_reference); //REF COUNT to 1 + } + arrayList_destroy(initial); + } + + if (status != CELIX_SUCCESS && listener != NULL){ free(listener); } @@ -190,27 +221,75 @@ celix_status_t serviceTracker_open(service_tracker_pt tracker) { return status; } -celix_status_t serviceTracker_close(service_tracker_pt tracker) { +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; +} + +celix_status_t serviceTracker_close(service_tracker_pt tracker) { //put all tracked entries in tmp array list, so that the untrack (etc) calls are not blocked. - int i; - celixThreadRwlock_writeLock(&tracker->lock); - fw_removeServiceListener(tracker->context->framework, tracker->context->bundle, tracker->listener); //remove in lock, to ensure no new tracked entry is added - size_t size = celix_arrayList_size(tracker->trackedServices); - celix_tracked_entry_t* trackedEntries[size]; - for (i = 0; i < arrayList_size(tracker->trackedServices); i++) { - trackedEntries[i] = (celix_tracked_entry_t*)arrayList_get(tracker->trackedServices, i); - } - arrayList_clear(tracker->trackedServices); - celixThreadRwlock_unlock(&tracker->lock); - - //loop trough tracked entries an untrack - for (i = 0; i < size; i++) { - serviceTracker_untrackTracked(tracker, trackedEntries[i]); - } + //set state to close to prevent service listener events + + celixThreadRwlock_writeLock(&tracker->instanceLock); + celix_service_tracker_instance_t *instance = tracker->instance; + tracker->instance = NULL; + celixThreadRwlock_unlock(&tracker->instanceLock); + + if (instance != NULL) { + + //prevent service listener events + celixThreadMutex_lock(&instance->closingLock); + instance->closing = true; + celixThreadMutex_unlock(&instance->closingLock); + + int i; + celixThreadRwlock_writeLock(&instance->lock); + size_t size = celix_arrayList_size(instance->trackedServices); + celix_tracked_entry_t *trackedEntries[size]; + for (i = 0; i < arrayList_size(instance->trackedServices); i++) { + trackedEntries[i] = (celix_tracked_entry_t *) arrayList_get(instance->trackedServices, i); + } + arrayList_clear(instance->trackedServices); + celixThreadRwlock_unlock(&instance->lock); - free(tracker->listener); - tracker->listener = NULL; + //loop trough tracked entries an untrack + for (i = 0; i < size; i++) { + serviceTracker_untrackTracked(instance, trackedEntries[i]); + } + + //sync til all pending serviceChanged event are handled.. (TODO again a possible deadlock??) + celixThreadMutex_lock(&instance->closingLock); + while(instance->activeServiceChangeCalls > 0) { + celixThreadCondition_wait(&instance->activeServiceChangeCallsCond, &instance->closingLock); + } + celixThreadMutex_unlock(&instance->closingLock); + + + + //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); + } return CELIX_SUCCESS; } @@ -221,13 +300,18 @@ service_reference_pt serviceTracker_getServiceReference(service_tracker_pt track service_reference_pt result = NULL; unsigned int i; - celixThreadRwlock_readLock(&tracker->lock); - for (i = 0; i < arrayList_size(tracker->trackedServices); i++) { - tracked = (celix_tracked_entry_t*) arrayList_get(tracker->trackedServices, i); - result = tracked->reference; - break; - } - celixThreadRwlock_unlock(&tracker->lock); + 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); + } + celixThreadRwlock_unlock(&tracker->instanceLock); return result; } @@ -239,12 +323,17 @@ array_list_pt serviceTracker_getServiceReferences(service_tracker_pt tracker) { array_list_pt references = NULL; arrayList_create(&references); - celixThreadRwlock_readLock(&tracker->lock); - for (i = 0; i < arrayList_size(tracker->trackedServices); i++) { - tracked = (celix_tracked_entry_t*) arrayList_get(tracker->trackedServices, i); - arrayList_add(references, tracked->reference); - } - celixThreadRwlock_unlock(&tracker->lock); + 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); + } + celixThreadRwlock_unlock(&tracker->instanceLock); return references; } @@ -255,13 +344,18 @@ void *serviceTracker_getService(service_tracker_pt tracker) { void *service = NULL; unsigned int i; - celixThreadRwlock_readLock(&tracker->lock); - for (i = 0; i < arrayList_size(tracker->trackedServices); i++) { - tracked = (celix_tracked_entry_t*) arrayList_get(tracker->trackedServices, i); - service = tracked->service; - break; - } - celixThreadRwlock_unlock(&tracker->lock); + 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); + } + celixThreadRwlock_unlock(&tracker->instanceLock); return service; } @@ -273,12 +367,17 @@ array_list_pt serviceTracker_getServices(service_tracker_pt tracker) { array_list_pt references = NULL; arrayList_create(&references); - celixThreadRwlock_readLock(&tracker->lock); - for (i = 0; i < arrayList_size(tracker->trackedServices); i++) { - tracked = (celix_tracked_entry_t*) arrayList_get(tracker->trackedServices, i); - arrayList_add(references, tracked->service); - } - celixThreadRwlock_unlock(&tracker->lock); + 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); + } + celixThreadRwlock_unlock(&tracker->instanceLock); return references; } @@ -289,51 +388,73 @@ void *serviceTracker_getServiceByReference(service_tracker_pt tracker, service_r void *service = NULL; unsigned int i; - celixThreadRwlock_readLock(&tracker->lock); - for (i = 0; i < arrayList_size(tracker->trackedServices); i++) { - bool equals = false; - tracked = (celix_tracked_entry_t*) arrayList_get(tracker->trackedServices, i); - serviceReference_equals(reference, tracked->reference, &equals); - if (equals) { - service = tracked->service; - break; - } - } - celixThreadRwlock_unlock(&tracker->lock); + 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; + } + } + celixThreadRwlock_unlock(&instance->lock); + } + celixThreadRwlock_unlock(&tracker->instanceLock); return service; } void serviceTracker_serviceChanged(celix_service_listener_t *listener, celix_service_event_t *event) { - service_tracker_pt tracker = listener->handle; - switch (event->type) { - case OSGI_FRAMEWORK_SERVICE_EVENT_REGISTERED: - serviceTracker_track(tracker, event->reference, event); - break; - case OSGI_FRAMEWORK_SERVICE_EVENT_MODIFIED: - serviceTracker_track(tracker, event->reference, event); - break; - case OSGI_FRAMEWORK_SERVICE_EVENT_UNREGISTERING: - serviceTracker_untrack(tracker, event->reference, event); - break; - case OSGI_FRAMEWORK_SERVICE_EVENT_MODIFIED_ENDMATCH: - //TODO - break; - } + celix_service_tracker_instance_t *instance = listener->handle; + + celixThreadMutex_lock(&instance->closingLock); + bool closing = instance->closing; + if (!closing) { + instance->activeServiceChangeCalls += 1; + } + celixThreadMutex_unlock(&instance->closingLock); + + if (!closing) { + switch (event->type) { + case OSGI_FRAMEWORK_SERVICE_EVENT_REGISTERED: + serviceTracker_track(instance, event->reference, event); + break; + case OSGI_FRAMEWORK_SERVICE_EVENT_MODIFIED: + serviceTracker_track(instance, event->reference, event); + break; + case OSGI_FRAMEWORK_SERVICE_EVENT_UNREGISTERING: + serviceTracker_untrack(instance, event->reference, event); + break; + case OSGI_FRAMEWORK_SERVICE_EVENT_MODIFIED_ENDMATCH: + //TODO + break; + } + celixThreadMutex_lock(&instance->closingLock); + assert(instance->activeServiceChangeCalls > 0); + instance->activeServiceChangeCalls -= 1; + if (instance->activeServiceChangeCalls == 0) { + celixThreadCondition_broadcast(&instance->activeServiceChangeCallsCond); + } + celixThreadMutex_unlock(&instance->closingLock); + } } -static celix_status_t serviceTracker_track(service_tracker_pt tracker, service_reference_pt reference, celix_service_event_t *event) { +static celix_status_t serviceTracker_track(celix_service_tracker_instance_t *instance, 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(tracker->context, reference); + bundleContext_retainServiceReference(instance->context, reference); - celixThreadRwlock_readLock(&tracker->lock); - for (i = 0; i < arrayList_size(tracker->trackedServices); i++) { + celixThreadRwlock_readLock(&instance->lock); + for (i = 0; i < arrayList_size(instance->trackedServices); i++) { bool equals = false; - celix_tracked_entry_t *visit = (celix_tracked_entry_t*) arrayList_get(tracker->trackedServices, i); + celix_tracked_entry_t *visit = (celix_tracked_entry_t*) arrayList_get(instance->trackedServices, i); serviceReference_equals(reference, visit->reference, &equals); if (equals) { found = visit; @@ -341,15 +462,15 @@ static celix_status_t serviceTracker_track(service_tracker_pt tracker, service_r break; } } - celixThreadRwlock_unlock(&tracker->lock); + celixThreadRwlock_unlock(&instance->lock); if (found != NULL) { - status = serviceTracker_invokeModifiedService(tracker, found); + status = serviceTracker_invokeModifiedService(instance, found); tracked_retain(found); } else if (status == CELIX_SUCCESS && found == NULL) { //NEW entry void *service = NULL; - status = serviceTracker_invokeAddingService(tracker, reference, &service); + status = serviceTracker_invokeAddingService(instance, reference, &service); if (status == CELIX_SUCCESS && service != NULL) { assert(reference != NULL); @@ -365,12 +486,12 @@ static celix_status_t serviceTracker_track(service_tracker_pt tracker, service_r celix_tracked_entry_t *tracked = tracked_create(reference, service, props, bnd); - celixThreadRwlock_writeLock(&tracker->lock); - arrayList_add(tracker->trackedServices, tracked); - celixThreadRwlock_unlock(&tracker->lock); + celixThreadRwlock_writeLock(&instance->lock); + arrayList_add(instance->trackedServices, tracked); + celixThreadRwlock_unlock(&instance->lock); - serviceTracker_invokeAddService(tracker, tracked); - celix_serviceTracker_useHighestRankingService(tracker, tracked->serviceName, tracker, NULL, NULL, serviceTracker_checkAndInvokeSetService); + serviceTracker_invokeAddService(instance, tracked); + serviceTracker_useHighestRankingServiceInternal(instance, tracked->serviceName, instance, NULL, NULL, serviceTracker_checkAndInvokeSetService); } } @@ -380,7 +501,7 @@ static celix_status_t serviceTracker_track(service_tracker_pt tracker, service_r } static void serviceTracker_checkAndInvokeSetService(void *handle, void *highestSvc, const celix_properties_t *props, const celix_bundle_t *bnd) { - celix_service_tracker_t *tracker = handle; + celix_service_tracker_instance_t *instance = handle; bool update = false; long svcId = -1; if (highestSvc == NULL) { @@ -390,100 +511,96 @@ static void serviceTracker_checkAndInvokeSetService(void *handle, void *highestS svcId = celix_properties_getAsLong(props, OSGI_FRAMEWORK_SERVICE_ID, -1); } if (svcId > 0) { - celixThreadMutex_lock(&tracker->mutex); - if (tracker->currentHighestServiceId != svcId) { - tracker->currentHighestServiceId = svcId; + celixThreadMutex_lock(&instance->mutex); + if (instance->currentHighestServiceId != svcId) { + instance->currentHighestServiceId = svcId; update = true; //update } - celixThreadMutex_unlock(&tracker->mutex); + celixThreadMutex_unlock(&instance->mutex); } if (update) { - void *h = tracker->callbackHandle; - if (tracker->set != NULL) { - tracker->set(h, highestSvc); + void *h = instance->callbackHandle; + if (instance->set != NULL) { + instance->set(h, highestSvc); } - if (tracker->setWithProperties != NULL) { - tracker->setWithProperties(h, highestSvc, props); + if (instance->setWithProperties != NULL) { + instance->setWithProperties(h, highestSvc, props); } - if (tracker->setWithOwner != NULL) { - tracker->setWithOwner(h, highestSvc, props, bnd); + if (instance->setWithOwner != NULL) { + instance->setWithOwner(h, highestSvc, props, bnd); } } } -static celix_status_t serviceTracker_invokeModifiedService(service_tracker_pt tracker, celix_tracked_entry_t *tracked) { +static celix_status_t serviceTracker_invokeModifiedService(celix_service_tracker_instance_t *instance, celix_tracked_entry_t *tracked) { celix_status_t status = CELIX_SUCCESS; - if (tracker->customizer != NULL) { + if (&instance->customizer != NULL) { void *handle = NULL; modified_callback_pt function = NULL; - serviceTrackerCustomizer_getHandle(tracker->customizer, &handle); - serviceTrackerCustomizer_getModifiedFunction(tracker->customizer, &function); + serviceTrackerCustomizer_getHandle(&instance->customizer, &handle); + serviceTrackerCustomizer_getModifiedFunction(&instance->customizer, &function); if (function != NULL) { function(handle, tracked->reference, tracked->service); } } - void *handle = tracker->callbackHandle; - if (tracker->modified != NULL) { - tracker->modified(handle, tracked->service); + void *handle = instance->callbackHandle; + if (instance->modified != NULL) { + instance->modified(handle, tracked->service); } - if (tracker->modifiedWithProperties != NULL) { - tracker->modifiedWithProperties(handle, tracked->service, tracked->properties); + if (instance->modifiedWithProperties != NULL) { + instance->modifiedWithProperties(handle, tracked->service, tracked->properties); } - if (tracker->modifiedWithOwner != NULL) { - tracker->modifiedWithOwner(handle, tracked->service, tracked->properties, tracked->serviceOwner); + if (instance->modifiedWithOwner != NULL) { + instance->modifiedWithOwner(handle, tracked->service, tracked->properties, tracked->serviceOwner); } return status; } -static celix_status_t serviceTracker_invokeAddService(service_tracker_pt tracker, celix_tracked_entry_t *tracked) { +static celix_status_t serviceTracker_invokeAddService(celix_service_tracker_instance_t *instance, celix_tracked_entry_t *tracked) { celix_status_t status = CELIX_SUCCESS; - if (tracker->customizer != NULL) { - void *handle = NULL; - added_callback_pt function = NULL; - serviceTrackerCustomizer_getHandle(tracker->customizer, &handle); - serviceTrackerCustomizer_getAddedFunction(tracker->customizer, &function); - if (function != NULL) { - function(handle, tracked->reference, tracked->service); - } + void *customizerHandle = NULL; + added_callback_pt function = NULL; + + serviceTrackerCustomizer_getHandle(&instance->customizer, &customizerHandle); + serviceTrackerCustomizer_getAddedFunction(&instance->customizer, &function); + if (function != NULL) { + function(customizerHandle, tracked->reference, tracked->service); } - void *handle = tracker->callbackHandle; - if (tracker->add != NULL) { - tracker->add(handle, tracked->service); + + void *handle = instance->callbackHandle; + if (instance->add != NULL) { + instance->add(handle, tracked->service); } - if (tracker->addWithProperties != NULL) { - tracker->addWithProperties(handle, tracked->service, tracked->properties); + if (instance->addWithProperties != NULL) { + instance->addWithProperties(handle, tracked->service, tracked->properties); } - if (tracker->addWithOwner != NULL) { - tracker->addWithOwner(handle, tracked->service, tracked->properties, tracked->serviceOwner); + if (instance->addWithOwner != NULL) { + instance->addWithOwner(handle, tracked->service, tracked->properties, tracked->serviceOwner); } return status; } -static celix_status_t serviceTracker_invokeAddingService(celix_service_tracker_t *tracker, service_reference_pt ref, void **svcOut) { +static celix_status_t serviceTracker_invokeAddingService(celix_service_tracker_instance_t *instance, service_reference_pt ref, void **svcOut) { celix_status_t status = CELIX_SUCCESS; - if (tracker->customizer != NULL) { - void *handle = NULL; - adding_callback_pt function = NULL; + void *handle = NULL; + adding_callback_pt function = NULL; - status = serviceTrackerCustomizer_getHandle(tracker->customizer, &handle); + status = serviceTrackerCustomizer_getHandle(&instance->customizer, &handle); - if (status == CELIX_SUCCESS) { - status = serviceTrackerCustomizer_getAddingFunction(tracker->customizer, &function); - } + if (status == CELIX_SUCCESS) { + status = serviceTrackerCustomizer_getAddingFunction(&instance->customizer, &function); + } - if (status == CELIX_SUCCESS && function != NULL) { - status = function(handle, ref, svcOut); - } else if (status == CELIX_SUCCESS) { - status = bundleContext_getService(tracker->context, ref, svcOut); - } - } else { - status = bundleContext_getService(tracker->context, ref, svcOut); + if (status == CELIX_SUCCESS && function != NULL) { + status = function(handle, ref, svcOut); + } else if (status == CELIX_SUCCESS) { + status = bundleContext_getService(instance->context, ref, svcOut); } framework_logIfError(logger, status, NULL, "Cannot handle addingService"); @@ -491,47 +608,47 @@ static celix_status_t serviceTracker_invokeAddingService(celix_service_tracker_t return status; } -static celix_status_t serviceTracker_untrack(service_tracker_pt tracker, service_reference_pt reference, celix_service_event_t *event) { +static celix_status_t serviceTracker_untrack(celix_service_tracker_instance_t* instance, service_reference_pt reference, celix_service_event_t *event) { celix_status_t status = CELIX_SUCCESS; celix_tracked_entry_t *remove = NULL; unsigned int i; unsigned int size; const char *serviceName = NULL; - celixThreadRwlock_writeLock(&tracker->lock); - size = arrayList_size(tracker->trackedServices); + celixThreadRwlock_writeLock(&instance->lock); + size = arrayList_size(instance->trackedServices); for (i = 0; i < size; i++) { bool equals; - celix_tracked_entry_t *tracked = (celix_tracked_entry_t*) arrayList_get(tracker->trackedServices, i); + celix_tracked_entry_t *tracked = (celix_tracked_entry_t*) arrayList_get(instance->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(tracker->trackedServices, i); + arrayList_remove(instance->trackedServices, i); break; } } - size = arrayList_size(tracker->trackedServices); //updated size - celixThreadRwlock_unlock(&tracker->lock); + size = arrayList_size(instance->trackedServices); //updated size + celixThreadRwlock_unlock(&instance->lock); if (size == 0) { - serviceTracker_checkAndInvokeSetService(tracker, NULL, NULL, NULL); + serviceTracker_checkAndInvokeSetService(instance, NULL, NULL, NULL); } else { - celix_serviceTracker_useHighestRankingService(tracker, serviceName, tracker, NULL, NULL, serviceTracker_checkAndInvokeSetService); + serviceTracker_useHighestRankingServiceInternal(instance, serviceName, instance, NULL, NULL, serviceTracker_checkAndInvokeSetService); } - serviceTracker_untrackTracked(tracker, remove); + serviceTracker_untrackTracked(instance, remove); framework_logIfError(logger, status, NULL, "Cannot untrack reference"); return status; } -static void serviceTracker_untrackTracked(celix_service_tracker_t *tracker, celix_tracked_entry_t *tracked) { +static void serviceTracker_untrackTracked(celix_service_tracker_instance_t *instance, celix_tracked_entry_t *tracked) { if (tracked != NULL) { - serviceTracker_invokeRemovingService(tracker, tracked); - bundleContext_ungetServiceReference(tracker->context, tracked->reference); + serviceTracker_invokeRemovingService(instance, tracked); + bundleContext_ungetServiceReference(instance->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. @@ -539,34 +656,33 @@ static void serviceTracker_untrackTracked(celix_service_tracker_t *tracker, celi } } -static celix_status_t serviceTracker_invokeRemovingService(service_tracker_pt tracker, celix_tracked_entry_t *tracked) { +static celix_status_t serviceTracker_invokeRemovingService(celix_service_tracker_instance_t *instance, celix_tracked_entry_t *tracked) { celix_status_t status = CELIX_SUCCESS; bool ungetSuccess = true; - if (tracker->customizer != NULL) { - void *handle = NULL; - removed_callback_pt function = NULL; - serviceTrackerCustomizer_getHandle(tracker->customizer, &handle); - serviceTrackerCustomizer_getRemovedFunction(tracker->customizer, &function); + void *customizerHandle = NULL; + removed_callback_pt function = NULL; - if (function != NULL) { - status = function(handle, tracked->reference, tracked->service); - } + serviceTrackerCustomizer_getHandle(&instance->customizer, &customizerHandle); + serviceTrackerCustomizer_getRemovedFunction(&instance->customizer, &function); + + if (function != NULL) { + status = function(customizerHandle, tracked->reference, tracked->service); } - void *handle = tracker->callbackHandle; - if (tracker->remove != NULL) { - tracker->remove(handle, tracked->service); + void *handle = instance->callbackHandle; + if (instance->remove != NULL) { + instance->remove(handle, tracked->service); } - if (tracker->addWithProperties != NULL) { - tracker->removeWithProperties(handle, tracked->service, tracked->properties); + if (instance->addWithProperties != NULL) { + instance->removeWithProperties(handle, tracked->service, tracked->properties); } - if (tracker->removeWithOwner != NULL) { - tracker->removeWithOwner(handle, tracked->service, tracked->properties, tracked->serviceOwner); + if (instance->removeWithOwner != NULL) { + instance->removeWithOwner(handle, tracked->service, tracked->properties, tracked->serviceOwner); } if (status == CELIX_SUCCESS) { - status = bundleContext_ungetService(tracker->context, tracked->reference, &ungetSuccess); + status = bundleContext_ungetService(instance->context, tracked->reference, &ungetSuccess); } if (!ungetSuccess) { @@ -606,9 +722,6 @@ celix_service_tracker_t* celix_serviceTracker_createWithOptions( tracker = calloc(1, sizeof(*tracker)); if (tracker != NULL) { tracker->context = ctx; - celixThreadRwlock_create(&tracker->lock, NULL); - tracker->trackedServices = celix_arrayList_create(); - tracker->listener = NULL; //setting callbacks tracker->callbackHandle = opts->callbackHandle; @@ -622,9 +735,7 @@ celix_service_tracker_t* celix_serviceTracker_createWithOptions( tracker->addWithOwner = opts->addWithOwner; tracker->removeWithOwner = opts->removeWithOwner; - //highest service state - celixThreadMutex_create(&tracker->mutex, NULL); - tracker->currentHighestServiceId = -1; + celixThreadRwlock_create(&tracker->instanceLock, NULL); //setting lang const char *lang = opts->filter.serviceLanguage; @@ -666,14 +777,12 @@ void celix_serviceTracker_destroy(celix_service_tracker_t *tracker) { } } - -bool celix_serviceTracker_useHighestRankingService( - celix_service_tracker_t *tracker, - 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)) { +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)) { bool called = false; celix_tracked_entry_t *tracked = NULL; celix_tracked_entry_t *highest = NULL; @@ -681,9 +790,9 @@ bool celix_serviceTracker_useHighestRankingService( unsigned int i; //first lock tracker and get highest tracked entry - celixThreadRwlock_readLock(&tracker->lock); - for (i = 0; i < arrayList_size(tracker->trackedServices); i++) { - tracked = (celix_tracked_entry_t *) arrayList_get(tracker->trackedServices, i); + celixThreadRwlock_readLock(&instance->lock); + for (i = 0; i < arrayList_size(instance->trackedServices); i++) { + tracked = (celix_tracked_entry_t *) arrayList_get(instance->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); @@ -697,7 +806,7 @@ bool celix_serviceTracker_useHighestRankingService( tracked_retain(highest); } //unlock tracker so that the tracked entry can be removed from the trackedServices list if unregistered. - celixThreadRwlock_unlock(&tracker->lock); + celixThreadRwlock_unlock(&instance->lock); if (highest != NULL) { //got service, call, decrease use count an signal useCond after. @@ -717,6 +826,25 @@ bool celix_serviceTracker_useHighestRankingService( return called; } + +bool celix_serviceTracker_useHighestRankingService( + celix_service_tracker_t *tracker, + 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)) { + celixThreadRwlock_readLock(&tracker->instanceLock); + celix_service_tracker_instance_t *instance = tracker->instance; + bool called = false; + if (instance != NULL) { + called = serviceTracker_useHighestRankingServiceInternal(instance, serviceName, callbackHandle, use, + useWithProperties, useWithOwner); + } + celixThreadRwlock_unlock(&tracker->instanceLock); + return called; +} + void celix_serviceTracker_useServices( service_tracker_t *tracker, const char* serviceName /*sanity*/, @@ -726,32 +854,119 @@ void celix_serviceTracker_useServices( void (*useWithOwner)(void *handle, void *svc, const celix_properties_t *props, const celix_bundle_t *owner)) { int i; - //first lock tracker, get tracked entries and increase use count - celixThreadRwlock_readLock(&tracker->lock); - size_t size = celix_arrayList_size(tracker->trackedServices); - celix_tracked_entry_t* entries[size]; - for (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_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); + size_t size = celix_arrayList_size(instance->trackedServices); + celix_tracked_entry_t *entries[size]; + for (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 (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); + } } - //unlock tracker so that the tracked entry can be removed from the trackedServices list if unregistered. - celixThreadRwlock_unlock(&tracker->lock); + celixThreadRwlock_unlock(&tracker->instanceLock); +} - //then use entries and decrease use count - for (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); +void celix_serviceTracker_syncForFramework(void *fw) { + celixThread_once(&g_once, serviceTracker_once); + celixThreadMutex_lock(&g_mutex); + 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 (useWithProperties != NULL) { - useWithProperties(callbackHandle, entry->service, entry->properties); + if (count > 0) { + pthread_cond_wait(&g_cond, &g_mutex); } - if (useWithOwner != NULL) { - useWithOwner(callbackHandle, entry->service, entry->properties, entry->serviceOwner); + } while (count > 0); + + if (g_shutdownInstances != NULL && celix_arrayList_size(g_shutdownInstances) == 0) { + celix_arrayList_destroy(g_shutdownInstances); + g_shutdownInstances = NULL; + } + celixThreadMutex_unlock(&g_mutex); +} + +void celix_serviceTracker_syncForContext(void *ctx) { + celixThread_once(&g_once, serviceTracker_once); + celixThreadMutex_lock(&g_mutex); + 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_cond, &g_mutex); } + } while (count > 0); - tracked_release(entry); + if (g_shutdownInstances != NULL && celix_arrayList_size(g_shutdownInstances) == 0) { + celix_arrayList_destroy(g_shutdownInstances); + g_shutdownInstances = NULL; + } + celixThreadMutex_unlock(&g_mutex); +} + +static void serviceTracker_addInstanceFromShutdownList(celix_service_tracker_instance_t *instance) { + celixThread_once(&g_once, serviceTracker_once); + celixThreadMutex_lock(&g_mutex); + if (g_shutdownInstances == NULL) { + g_shutdownInstances = celix_arrayList_create(); + } + celix_arrayList_add(g_shutdownInstances, instance); + celixThreadMutex_unlock(&g_mutex); +} + +static void serviceTracker_remInstanceFromShutdownList(celix_service_tracker_instance_t *instance) { + celixThread_once(&g_once, serviceTracker_once); + celixThreadMutex_lock(&g_mutex); + 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_cond); } + celixThreadMutex_unlock(&g_mutex); } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/celix/blob/bcda87f5/libs/framework/src/service_tracker_customizer.c ---------------------------------------------------------------------- diff --git a/libs/framework/src/service_tracker_customizer.c b/libs/framework/src/service_tracker_customizer.c index b62a23a..d4763cb 100644 --- a/libs/framework/src/service_tracker_customizer.c +++ b/libs/framework/src/service_tracker_customizer.c @@ -25,8 +25,8 @@ */ #include <stdlib.h> +#include "service_tracker_customizer.h" -#include "service_tracker_customizer_private.h" #include "celix_log.h" celix_status_t serviceTrackerCustomizer_create(void *handle, http://git-wip-us.apache.org/repos/asf/celix/blob/bcda87f5/libs/framework/src/service_tracker_customizer_private.h ---------------------------------------------------------------------- diff --git a/libs/framework/src/service_tracker_customizer_private.h b/libs/framework/src/service_tracker_customizer_private.h deleted file mode 100644 index 61fb52f..0000000 --- a/libs/framework/src/service_tracker_customizer_private.h +++ /dev/null @@ -1,49 +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. - */ -/* - * service_tracker_customizer_private.h - * - * \date Feb 7, 2013 - * \author <a href="mailto:[email protected]">Apache Celix Project Team</a> - * \copyright Apache License, Version 2.0 - */ - - -#ifndef SERVICE_TRACKER_CUSTOMIZER_PRIVATE_H_ -#define SERVICE_TRACKER_CUSTOMIZER_PRIVATE_H_ - -#include "service_reference.h" - -#include "service_tracker_customizer.h" - - -struct serviceTrackerCustomizer { - void * handle; - celix_status_t (*addingService)(void * handle, service_reference_pt reference, void **service); - celix_status_t (*addedService)(void * handle, service_reference_pt reference, void * service); - celix_status_t (*modifiedService)(void * handle, service_reference_pt reference, void * service); - - /*TODO rename to removingService. because it is invoke during remove not after!*/ - celix_status_t (*removedService)(void * handle, service_reference_pt reference, void * service); - - /*TODO add removed function ? invoked after the remove ?? */ -}; - - -#endif /* SERVICE_TRACKER_CUSTOMIZER_PRIVATE_H_ */ http://git-wip-us.apache.org/repos/asf/celix/blob/bcda87f5/libs/framework/src/service_tracker_private.h ---------------------------------------------------------------------- diff --git a/libs/framework/src/service_tracker_private.h b/libs/framework/src/service_tracker_private.h index d772dc1..171b1a4 100644 --- a/libs/framework/src/service_tracker_private.h +++ b/libs/framework/src/service_tracker_private.h @@ -29,13 +29,20 @@ #define SERVICE_TRACKER_PRIVATE_H_ #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; -struct celix_serviceTracker { bundle_context_t *context; char * filter; - service_tracker_customizer_t *customizer; - celix_service_listener_t *listener; + service_tracker_customizer_t customizer; + celix_service_listener_t listener; void *callbackHandle; @@ -59,9 +66,39 @@ struct celix_serviceTracker { 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 * filter; + service_tracker_customizer_t *customizer; + + 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 instanceLock; + celix_service_tracker_instance_t *instance; /*NULL -> close, !NULL->open*/ + }; -struct celix_tracked_entry { +typedef struct celix_tracked_entry { service_reference_pt reference; void *service; const char *serviceName; @@ -71,8 +108,7 @@ struct celix_tracked_entry { celix_thread_mutex_t mutex; //protects useCount celix_thread_cond_t useCond; size_t useCount; -}; +} celix_tracked_entry_t; -typedef struct celix_tracked_entry celix_tracked_entry_t; #endif /* SERVICE_TRACKER_PRIVATE_H_ */ http://git-wip-us.apache.org/repos/asf/celix/blob/bcda87f5/libs/framework/tst/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/libs/framework/tst/CMakeLists.txt b/libs/framework/tst/CMakeLists.txt index 014eb2a..38d0ed8 100644 --- a/libs/framework/tst/CMakeLists.txt +++ b/libs/framework/tst/CMakeLists.txt @@ -29,6 +29,7 @@ add_executable(test_framework ) target_link_libraries(test_framework Celix::framework ${CURL_LIBRARIES} ${CPPUTEST_LIBRARY}) add_dependencies(test_framework simple_test_bundle1_bundle simple_test_bundle2_bundle simple_test_bundle3_bundle) +target_include_directories(test_framework PRIVATE ../src) configure_file(config.properties.in config.properties @ONLY) configure_file(framework1.properties.in framework1.properties @ONLY) http://git-wip-us.apache.org/repos/asf/celix/blob/bcda87f5/libs/framework/tst/bundle_context_services_test.cpp ---------------------------------------------------------------------- diff --git a/libs/framework/tst/bundle_context_services_test.cpp b/libs/framework/tst/bundle_context_services_test.cpp index 5e1792a..892fab4 100644 --- a/libs/framework/tst/bundle_context_services_test.cpp +++ b/libs/framework/tst/bundle_context_services_test.cpp @@ -28,6 +28,7 @@ #include "celix_api.h" #include "celix_framework_factory.h" #include "celix_service_factory.h" +#include "service_tracker_private.h" #include <CppUTest/TestHarness.h> @@ -684,8 +685,10 @@ TEST(CelixBundleContextServicesTests, trackServiceTrackerTest) { CHECK_EQUAL(2, count); celix_bundleContext_stopTracker(ctx, tracker2); + celix_serviceTracker_syncForContext(ctx); //service tracker shutdown on separate track -> need sync CHECK_EQUAL(1, count); celix_bundleContext_stopTracker(ctx, tracker3); + celix_serviceTracker_syncForContext(ctx); //service tracker shutdown on separate track -> need sync CHECK_EQUAL(0, count); celix_bundleContext_stopTracker(ctx, trackerId);
