Repository: celix Updated Branches: refs/heads/develop d85590363 -> 02ee6c4e5
CELIX-452: Improves the service listener locking. The retain/release on service listener now first check if service listener matches Project: http://git-wip-us.apache.org/repos/asf/celix/repo Commit: http://git-wip-us.apache.org/repos/asf/celix/commit/02ee6c4e Tree: http://git-wip-us.apache.org/repos/asf/celix/tree/02ee6c4e Diff: http://git-wip-us.apache.org/repos/asf/celix/diff/02ee6c4e Branch: refs/heads/develop Commit: 02ee6c4e5abd06a6756e33f79db45ab5e50f2e7a Parents: d855903 Author: Pepijn Noltes <[email protected]> Authored: Tue Jul 17 12:06:40 2018 +0200 Committer: Pepijn Noltes <[email protected]> Committed: Tue Jul 17 12:06:40 2018 +0200 ---------------------------------------------------------------------- cmake/cmake_celix/ContainerPackaging.cmake | 23 ++-- cmake/cmake_celix/OCIPacking.cmake | 156 ++++++++++++++++++++++++ cmake/cmake_celix/runtime_common.sh.in | 2 - libs/framework/src/bundle_cache.c | 3 + libs/framework/src/framework.c | 107 ++++++++-------- 5 files changed, 224 insertions(+), 67 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/celix/blob/02ee6c4e/cmake/cmake_celix/ContainerPackaging.cmake ---------------------------------------------------------------------- diff --git a/cmake/cmake_celix/ContainerPackaging.cmake b/cmake/cmake_celix/ContainerPackaging.cmake index b39cdae..e9452fb 100644 --- a/cmake/cmake_celix/ContainerPackaging.cmake +++ b/cmake/cmake_celix/ContainerPackaging.cmake @@ -273,21 +273,24 @@ function(celix_container_bundles_dir) COMMENT "Copying (imported) bundle '${BUNDLE}' to '${CONTAINER_LOC}/${BD_DIR_NAME}'" ) set(HANDLED TRUE) + else() #Assuming a bundle target (library or add_custom_target) + string(MAKE_C_IDENTIFIER ${BUNDLE} BUNDLE_ID) #Create id with no special chars (e.g. for target like Celix::shell) + set(OUT "${CMAKE_BINARY_DIR}/celix/gen/containers/${CONTAINER_TARGET}/copy-bundle-for-target-${BUNDLE_ID}.timestamp") + set(DEST "${CONTAINER_LOC}/${BD_DIR_NAME}/$<TARGET_PROPERTY:${BUNDLE},BUNDLE_FILENAME>") + add_custom_command(OUTPUT ${OUT} + COMMAND ${CMAKE_COMMAND} -E touch ${OUT} + COMMAND ${CMAKE_COMMAND} -E make_directory ${CONTAINER_LOC}/${BD_DIR_NAME} + COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_PROPERTY:${BUNDLE},BUNDLE_FILE>" ${DEST} + COMMENT "Copying (target) bundle '${BUNDLE}' to '${CONTAINER_LOC}/${BD_DIR_NAME}'" + DEPENDS ${BUNDLE} $<TARGET_PROPERTY:${BUNDLE},BUNDLE_CREATE_BUNDLE_TARGET> + ) + set(HANDLED TRUE) endif () endif () endif () if (NOT HANDLED) #not a imported bundle target so (assuming) a future bundle target - string(MAKE_C_IDENTIFIER ${BUNDLE} BUNDLE_ID) #Create id with no special chars (e.g. for target like Celix::shell) - set(OUT "${CMAKE_BINARY_DIR}/celix/gen/containers/${CONTAINER_TARGET}/copy-bundle-for-target-${BUNDLE_ID}.timestamp") - set(DEST "${CONTAINER_LOC}/${BD_DIR_NAME}/$<TARGET_PROPERTY:${BUNDLE},BUNDLE_FILENAME>") - add_custom_command(OUTPUT ${OUT} - COMMAND ${CMAKE_COMMAND} -E touch ${OUT} - COMMAND ${CMAKE_COMMAND} -E make_directory ${CONTAINER_LOC}/${BD_DIR_NAME} - COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_PROPERTY:${BUNDLE},BUNDLE_FILE>" ${DEST} - COMMENT "Copying (target) bundle '${BUNDLE}' to '${CONTAINER_LOC}/${BD_DIR_NAME}'" - DEPENDS ${BUNDLE} $<TARGET_PROPERTY:${BUNDLE},BUNDLE_CREATE_BUNDLE_TARGET> - ) + message(FATAL_ERROR "Cannot add bundle in container ${CONTAINER_TARGET}. Provided bundle is not a abs path to an existing file nor a cmake target (${BUNDLE}).") endif() list(APPEND DEPS "${OUT}") endforeach() http://git-wip-us.apache.org/repos/asf/celix/blob/02ee6c4e/cmake/cmake_celix/OCIPacking.cmake ---------------------------------------------------------------------- diff --git a/cmake/cmake_celix/OCIPacking.cmake b/cmake/cmake_celix/OCIPacking.cmake new file mode 100644 index 0000000..e67a6fa --- /dev/null +++ b/cmake/cmake_celix/OCIPacking.cmake @@ -0,0 +1,156 @@ +# 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. + +##### setup bundles/container target +if (NOT TARGET celix-oci-bundles) + add_custom_target(celix-oci-application-bundles ALL + DEPENDS $<TARGET_PROPERTY:celix-oci-bundles,OCI_APPLICATION_BUNDLES> + ) + set_target_properties(celix-oci-application-bundles PROPERTIES "OCI_APPLICATION_BUNDLES" "") #initial empty deps list +if (NOT TARGET celix-containers) + add_custom_target(celix-containers ALL + DEPENDS $<TARGET_PROPERTY:celix-containers,CONTAINER_DEPLOYMENTS> + ) + set_target_properties(celix-containers PROPERTIES "CONTAINER_DEPLOYMENTS" "") #initial empty deps list +endif () + + + #set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_TARGET_DEPS" "") #target deps for the container. + #set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_LEVEL_0" "") #bundles to deploy for the container for run level 0 + #set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_LEVEL_1" "") #bundles to deploy for the container for run level 0 + #set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_LEVEL_2" "") #bundles to deploy for the container for run level 0 + #set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_LEVEL_3" "") #bundles to deploy for the container for run level 0 + #set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_LEVEL_4" "") #bundles to deploy for the container for run level 0 + #set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_LEVEL_5" "") #bundles to deploy for the container for run level 0 + #set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_COPY_BUNDLES" ${CONTAINER_COPY}) #copy bundles in bundle dir or link using abs paths. NOTE this cannot be changed after a add_deploy command + + #deploy specific + #set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_NAME" "${CONTAINER_NAME}") + #set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_GROUP" "${CONTAINER_GROUP}") + #set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_LOC" "${CONTAINER_LOC}") + #set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_PROPERTIES" "") + #set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_EMBEDDED_PROPERTIES" "") + ##### + + +#Creates a OCI runtime bundle from a celix containers. +#TODO align with add_celix_docker, make it possible to "inherit" from add_celix_container +function (add_celix_oci_bundle_dir) + set(OPTIONS ) + set(ONE_VAL_ARGS CELIX_CONTAINER OCI_NAME EXPORT) + set(MULTI_VAL_ARGS ) + cmake_parse_arguments(OCI "${OPTIONS}" "${ONE_VAL_ARGS}" "${MULTI_VAL_ARGS}" ${ARGN}) + + if (NOT TARGET ${OCI_CELIX_CONTAINER}) + message(FATAL_ERROR "Cannot create a OCI container from '${OCI_CELIX_CONTAINER}'; Specify a valid Celix container target with the CELIX_CONTAINER arguments") + endif () + + set(TARGET ${OCI_CELIX_CONTAINER}-oci-container) + + add_custom_target(${TARGET} ALL) + get_target_property(DEPS celix-oci-application-bundles PROPERTY "OCI_APPLICATION_BUNDLES" ) + list(APPEND DEPS ${TARGET}) + set_target_properties(celix-oci-application-bundles PROPERTIES "OCI_APPLICATION_BUNDLES" "${DEPS}") + + + celix_oci_container_add_bundles(${OCI_CELIX_CONTAINER} $<TARGET_PROPERTY:${OCI_CELIX_CONTAINER},CONTAINER_BUNDLES_LEVEL_0> LEVEL 0) + celix_oci_container_add_bundles(${OCI_CELIX_CONTAINER} $<TARGET_PROPERTY:${OCI_CELIX_CONTAINER},CONTAINER_BUNDLES_LEVEL_1> LEVEL 1) + celix_oci_container_add_bundles(${OCI_CELIX_CONTAINER} $<TARGET_PROPERTY:${OCI_CELIX_CONTAINER},CONTAINER_BUNDLES_LEVEL_2> LEVEL 2) + celix_oci_container_add_bundles(${OCI_CELIX_CONTAINER} $<TARGET_PROPERTY:${OCI_CELIX_CONTAINER},CONTAINER_BUNDLES_LEVEL_3> LEVEL 3) + celix_oci_container_add_bundles(${OCI_CELIX_CONTAINER} $<TARGET_PROPERTY:${OCI_CELIX_CONTAINER},CONTAINER_BUNDLES_LEVEL_4> LEVEL 4) + celix_oci_container_add_bundles(${OCI_CELIX_CONTAINER} $<TARGET_PROPERTY:${OCI_CELIX_CONTAINER},CONTAINER_BUNDLES_LEVEL_5> LEVEL 5) +endfunction () + +#TODO combine with docker +function (celix_oci_container_bundles) + #0 is docker TARGET + #1..n is bundles + list(GET ARGN 0 CONTAINER_TARGET) + list(REMOVE_AT ARGN 0) + set(TARGET ${CONTAINER_TARGET}-oci-container) + + set(OPTIONS ) + set(ONE_VAL_ARGS LEVEL) + set(MULTI_VAL_ARGS ) + cmake_parse_arguments(BUNDLES "${OPTIONS}" "${ONE_VAL_ARGS}" "${MULTI_VAL_ARGS}" ${ARGN}) + set(BUNDLES_LIST ${BUNDLES_UNPARSED_ARGUMENTS}) + + if (NOT BUNDLES_LEVEL) + set(BUNDLES_LEVEL 1) + endif () + + get_target_property(BUNDLES ${TARGET} "BUNDLES_LEVEL_${BUNDLES_LEVEL}") + get_target_property(BUNDLES_DIR ${TARGET} "BUNDLES_DIR") + get_target_property(LOC ${TARGET} "LOC") + get_target_property(DEPS ${TARGET} "DEPS") + + foreach(BUNDLE IN ITEMS ${BUNDLES_LIST}) + set(HANDLED FALSE) + if(IS_ABSOLUTE ${BUNDLE} AND EXISTS ${BUNDLE}) + get_filename_component(BUNDLE_FILENAME ${BUNDLE} NAME) + set(OUT "${LOC}/${BUNDLES_DIR}/${BUNDLE_FILENAME}") + add_custom_command(OUTPUT ${OUT} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${BUNDLE} ${OUT} + COMMENT "Copying (file) bundle '${BUNDLE}' to '${LOC}/${BUNDLES_DIR}'" + DEPENDS ${BUNDLE} + ) + list(APPEND BUNDLES "${BUNDLES_DIR}/${BUNDLE_FILENAME}") + set(HANDLED TRUE) + elseif (TARGET ${BUNDLE}) + get_target_property(TARGET_TYPE ${BUNDLE} TYPE) + if (TARGET_TYPE STREQUAL "INTERFACE_LIBRARY") + #ignore + set(HANDLED TRUE) + else () + get_target_property(IMP ${BUNDLE} BUNDLE_IMPORTED) + if (IMP) #An imported bundle target -> handle target without DEPENDS + string(MAKE_C_IDENTIFIER ${BUNDLE} BUNDLE_ID) #Create id with no special chars (e.g. for target like Celix::shell) + set(OUT "${CMAKE_BINARY_DIR}/celix/gen/docker/${DOCKER_TARGET}/copy-bundle-for-target-${BUNDLE_ID}.timestamp") + set(DEST "${LOC}/${BUNDLES_DIR}/$<TARGET_PROPERTY:${BUNDLE},BUNDLE_FILENAME>") + add_custom_command(OUTPUT ${OUT} + COMMAND ${CMAKE_COMMAND} -E touch ${OUT} + COMMAND ${CMAKE_COMMAND} -E make_directory ${LOC}/${BUNDLES_DIR} + COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_PROPERTY:${BUNDLE},BUNDLE_FILE>" ${DEST} + COMMENT "Copying (imported) bundle '${BUNDLE}' to '${LOC}/${BUNDLES_DIR}'" + ) + list(APPEND BUNDLES "${BUNDLES_DIR}/$<TARGET_PROPERTY:${BUNDLE},BUNDLE_FILENAME>") + set(HANDLED TRUE) + endif () + endif () + endif () + + if(NOT HANDLED) #assuming (future) bundle target + string(MAKE_C_IDENTIFIER ${BUNDLE} BUNDLE_ID) #Create id with no special chars (e.g. for target like Celix::shell) + set(OUT "${CMAKE_BINARY_DIR}/celix/gen/docker/${DOCKER_TARGET}/copy-bundle-for-target-${BUNDLE_ID}.timestamp") + set(DEST "${LOC}/${BUNDLES_DIR}/$<TARGET_PROPERTY:${BUNDLE},BUNDLE_FILENAME>") + add_custom_command(OUTPUT ${OUT} + COMMAND ${CMAKE_COMMAND} -E touch ${OUT} + COMMAND ${CMAKE_COMMAND} -E make_directory ${LOC}/${BUNDLES_DIR} + COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_PROPERTY:${BUNDLE},BUNDLE_FILE>" ${DEST} + COMMENT "Copying (target) bundle '${BUNDLE}' to '${LOC}/${BUNDLES_DIR}'" + DEPENDS ${BUNDLE} $<TARGET_PROPERTY:${BUNDLE},BUNDLE_CREATE_BUNDLE_TARGET> + ) + list(APPEND BUNDLES "${BUNDLES_DIR}/$<TARGET_PROPERTY:${BUNDLE},BUNDLE_FILENAME>") + endif() + list(APPEND DEPS "${OUT}") + endforeach() + + set_target_properties(${DOCKER_TARGET} PROPERTIES "DOCKER_BUNDLES_LEVEL_${BUNDLES_LEVEL}" "${BUNDLES}") + set_target_properties(${DOCKER_TARGET} PROPERTIES "DOCKER_DEPS" "${DEPS}") +endfunction () + + #TODO install_celix_oci_containers_targets http://git-wip-us.apache.org/repos/asf/celix/blob/02ee6c4e/cmake/cmake_celix/runtime_common.sh.in ---------------------------------------------------------------------- diff --git a/cmake/cmake_celix/runtime_common.sh.in b/cmake/cmake_celix/runtime_common.sh.in index de0c955..7c4be1d 100644 --- a/cmake/cmake_celix/runtime_common.sh.in +++ b/cmake/cmake_celix/runtime_common.sh.in @@ -36,8 +36,6 @@ KILL_OPTS="${KILL_OPTS:--2}" #default is -2, e.g. SIGINT PATIENCE="${PATIENCE:-5}" #in seconds PIDS="" - -="" RUNTIME_STARTTIME=$(date +"%s") trap stop_all INT http://git-wip-us.apache.org/repos/asf/celix/blob/02ee6c4e/libs/framework/src/bundle_cache.c ---------------------------------------------------------------------- diff --git a/libs/framework/src/bundle_cache.c b/libs/framework/src/bundle_cache.c index 39875b5..6de8ab7 100644 --- a/libs/framework/src/bundle_cache.c +++ b/libs/framework/src/bundle_cache.c @@ -149,6 +149,9 @@ celix_status_t bundleCache_getArchives(bundle_cache_pt cache, array_list_pt *arc } framework_logIfError(logger, status, NULL, "Failed to get bundle archives"); + if (status != CELIX_SUCCESS) { + perror("\t"); + } return status; } http://git-wip-us.apache.org/repos/asf/celix/blob/02ee6c4e/libs/framework/src/framework.c ---------------------------------------------------------------------- diff --git a/libs/framework/src/framework.c b/libs/framework/src/framework.c index 39622e4..fcfe07f 100644 --- a/libs/framework/src/framework.c +++ b/libs/framework/src/framework.c @@ -1757,18 +1757,19 @@ void fw_serviceChanged(framework_pt framework, celix_service_event_type_t eventT unsigned int i; celix_fw_service_listener_entry_t *entry; - celix_array_list_t* copy = celix_arrayList_create(); + celix_array_list_t* retainedEntries = celix_arrayList_create(); + celix_array_list_t* matchedEntries = celix_arrayList_create(); celixThreadMutex_lock(&framework->serviceListenersLock); for (i = 0; i < celix_arrayList_size(framework->serviceListeners); i++) { entry = (celix_fw_service_listener_entry_t *) celix_arrayList_get(framework->serviceListeners, i); - celix_arrayList_add(copy, entry); + celix_arrayList_add(retainedEntries, entry); listener_retain(entry); //ensure that use count > 0, so that the listener cannot be destroyed until all pending event are handled. } celixThreadMutex_unlock(&framework->serviceListenersLock); - for (i = 0; i < celix_arrayList_size(copy); ++i) { - entry = (celix_fw_service_listener_entry_t *) celix_arrayList_get(copy, i); + for (i = 0; i < celix_arrayList_size(retainedEntries); ++i) { + entry = (celix_fw_service_listener_entry_t *) celix_arrayList_get(retainedEntries, i); int matched = 0; properties_pt props = NULL; bool matchResult = false; @@ -1778,67 +1779,63 @@ void fw_serviceChanged(framework_pt framework, celix_service_event_type_t eventT } matched = (entry->filter == NULL) || matchResult; if (matched) { - service_reference_pt reference = NULL; - celix_service_event_t *event; - - event = malloc(sizeof(*event)); - - serviceRegistry_getServiceReference(framework->registry, entry->bundle, registration, &reference); - - //NOTE: that you are never sure that the UNREGISTERED event will by handle by an service_listener. listener could be gone - //Every reference retained is therefore stored and called when a service listener is removed from the framework. - if (eventType == OSGI_FRAMEWORK_SERVICE_EVENT_REGISTERED) { - serviceRegistry_retainServiceReference(framework->registry, entry->bundle, reference); - celixThreadMutex_lock(&entry->mutex); - arrayList_add(entry->retainedReferences, reference); //TODO improve by using set (or hashmap) instead of list - celixThreadMutex_unlock(&entry->mutex); - } - - event->type = eventType; - event->reference = reference; - - entry->listener->serviceChanged(entry->listener, event); - - serviceRegistry_ungetServiceReference(framework->registry, entry->bundle, reference); + celix_arrayList_add(matchedEntries, entry); + } else { + listener_release(entry); //Not a match -> release entry + } + } + celix_arrayList_destroy(retainedEntries); + + /* + * TODO FIXME, A deadlock can happen when (e.g.) a service is deregistered, triggering this fw_serviceChanged and + * one of the matching service listener callback tries to remove an other matched service listener. + * The remove service listener will call the listener_waitForDestroy and the fw_serviceChanged part keeps the + * usageCount on > 0. + * + * Not sure how to prevent/handle this. + */ + for (i = 0; i < celix_arrayList_size(matchedEntries); ++i) { + entry = (celix_fw_service_listener_entry_t *) celix_arrayList_get(matchedEntries, i); + + service_reference_pt reference = NULL; + celix_service_event_t event; + + serviceRegistry_getServiceReference(framework->registry, entry->bundle, registration, &reference); + + //NOTE: that you are never sure that the UNREGISTERED event will by handle by an service_listener. listener could be gone + //Every reference retained is therefore stored and called when a service listener is removed from the framework. + if (eventType == OSGI_FRAMEWORK_SERVICE_EVENT_REGISTERED) { + serviceRegistry_retainServiceReference(framework->registry, entry->bundle, reference); + celixThreadMutex_lock(&entry->mutex); + arrayList_add(entry->retainedReferences, reference); //TODO improve by using set (or hashmap) instead of list + celixThreadMutex_unlock(&entry->mutex); + } - if (eventType == OSGI_FRAMEWORK_SERVICE_EVENT_UNREGISTERING) { - //if service listener was active when service was registered, release the retained reference - celixThreadMutex_lock(&entry->mutex); - bool removed = arrayList_removeElement(entry->retainedReferences, reference); - celixThreadMutex_unlock(&entry->mutex); - if (removed) { - serviceRegistry_ungetServiceReference(framework->registry, entry->bundle, reference); // decrease retain counter - } + event.type = eventType; + event.reference = reference; - } + entry->listener->serviceChanged(entry->listener, &event); - free(event); + serviceRegistry_ungetServiceReference(framework->registry, entry->bundle, reference); - } else if (eventType == OSGI_FRAMEWORK_SERVICE_EVENT_MODIFIED) { - bool matchResult = false; - int matched = 0; - if (entry->filter != NULL) { - filter_match(entry->filter, oldprops, &matchResult); + if (eventType == OSGI_FRAMEWORK_SERVICE_EVENT_UNREGISTERING) { + //if service listener was active when service was registered, release the retained reference + celixThreadMutex_lock(&entry->mutex); + bool removed = arrayList_removeElement(entry->retainedReferences, reference); + celixThreadMutex_unlock(&entry->mutex); + if (removed) { + serviceRegistry_ungetServiceReference(framework->registry, entry->bundle, + reference); // decrease retain counter } - matched = (entry->filter == NULL) || matchResult; - if (matched) { - service_reference_pt reference = NULL; - celix_service_event_t *endmatch = malloc(sizeof(*endmatch)); - serviceRegistry_getServiceReference(framework->registry, entry->bundle, registration, &reference); - - endmatch->reference = reference; - endmatch->type = OSGI_FRAMEWORK_SERVICE_EVENT_MODIFIED_ENDMATCH; - entry->listener->serviceChanged(entry->listener, endmatch); - - serviceRegistry_ungetServiceReference(framework->registry, entry->bundle, reference); - free(endmatch); + } - } + if (eventType == OSGI_FRAMEWORK_SERVICE_EVENT_MODIFIED) { + entry->listener->serviceChanged(entry->listener, &event); } listener_release(entry); //decrease usage, so that the listener can be destroyed (if use count is now 0) } - celix_arrayList_destroy(copy); + celix_arrayList_destroy(matchedEntries); } //celix_status_t fw_isServiceAssignable(framework_pt fw, bundle_pt requester, service_reference_pt reference, bool *assignable) {
