You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pn...@apache.org on 2020/05/24 18:54:14 UTC

[celix] branch feature/register_with_reserved_id created (now 819b9b2)

This is an automated email from the ASF dual-hosted git repository.

pnoltes pushed a change to branch feature/register_with_reserved_id
in repository https://gitbox.apache.org/repos/asf/celix.git.


      at 819b9b2  Adds support for reserving service id, so that registering service can be done safely outside of locks/mutexes.

This branch includes the following new commits:

     new 819b9b2  Adds support for reserving service id, so that registering service can be done safely outside of locks/mutexes.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[celix] 01/01: Adds support for reserving service id, so that registering service can be done safely outside of locks/mutexes.

Posted by pn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pnoltes pushed a commit to branch feature/register_with_reserved_id
in repository https://gitbox.apache.org/repos/asf/celix.git

commit 819b9b24e0d7688c0fe99c47e5300ce2524d0a16
Author: Pepijn Noltes <pe...@gmail.com>
AuthorDate: Sun May 24 20:53:49 2020 +0200

    Adds support for reserving service id, so that registering service can be done safely outside of locks/mutexes.
    
    This can be done, because if an unregister service happens before a register service with a reserved service id, the
    expected future register service will be (silently) cancelled.
---
 bundles/logging/log_admin/src/celix_log_admin.c    |  16 +-
 .../tms_tst/disc_mock/disc_mock_activator.c        |   1 +
 .../gtest/src/bundle_context_services_test.cpp     | 160 +++++++++++++-
 libs/framework/include/celix_bundle_context.h      |  39 +++-
 libs/framework/include/service_registry.h          |  12 +-
 libs/framework/src/bundle.c                        |   2 +-
 libs/framework/src/bundle_context.c                | 245 ++++++++++++++++++---
 libs/framework/src/bundle_context_private.h        |   4 +
 libs/framework/src/framework.c                     |  39 +++-
 libs/framework/src/framework_private.h             |   4 +-
 libs/framework/src/service_registry.c              |  74 ++-----
 libs/framework/src/service_tracker.c               |  28 +--
 libs/utils/include/celix_log_utils.h               |   5 -
 libs/utils/src/celix_log_utils.c                   |  14 +-
 14 files changed, 510 insertions(+), 133 deletions(-)

diff --git a/bundles/logging/log_admin/src/celix_log_admin.c b/bundles/logging/log_admin/src/celix_log_admin.c
index 900940d..ad49461 100644
--- a/bundles/logging/log_admin/src/celix_log_admin.c
+++ b/bundles/logging/log_admin/src/celix_log_admin.c
@@ -176,6 +176,8 @@ static void celix_logAdmin_addLogSvcForName(celix_log_admin_t* admin, const char
 
     celixThreadRwlock_writeLock(&admin->lock);
     celix_log_service_entry_t* found = hashMap_get(admin->loggers, name);
+    long reservedId = -1L;
+    celix_log_service_t *newLogSvc = NULL;
     if (found == NULL) {
         //new
         newEntry = calloc(1, sizeof(*newEntry));
@@ -194,8 +196,12 @@ static void celix_logAdmin_addLogSvcForName(celix_log_admin_t* admin, const char
         newEntry->logSvc.logDetails = celix_logAdmin_logDetails;
         newEntry->logSvc.vlog = celix_logAdmin_vlog;
         newEntry->logSvc.vlogDetails = celix_logAdmin_vlogDetails;
+        newEntry->logSvcId = celix_bundleContext_reserveSvcId(admin->ctx);
         hashMap_put(admin->loggers, (void*)newEntry->name, newEntry);
 
+        reservedId = newEntry->logSvcId;
+        newLogSvc = &newEntry->logSvc;
+
         if (celix_utils_stringEquals(newEntry->name, CELIX_LOG_ADMIN_FRAMEWORK_LOG_NAME)) {
             celix_framework_t* fw = celix_bundleContext_getFramework(admin->ctx);
             celix_framework_setLogCallback(fw, newEntry, celix_logAdmin_frameworkLogFunction);
@@ -205,8 +211,9 @@ static void celix_logAdmin_addLogSvcForName(celix_log_admin_t* admin, const char
     }
     celixThreadRwlock_unlock(&admin->lock);
 
-    if (newEntry != NULL) {
-        //register new instance
+    if (newLogSvc != NULL) {
+        //register new log service outside of the lock using a reserved service id.
+
         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) {
@@ -218,8 +225,9 @@ static void celix_logAdmin_addLogSvcForName(celix_log_admin_t* admin, const char
         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);
+        opts.svc = newLogSvc;
+        opts.reservedSvcId = reservedId;
+        celix_bundleContext_registerServiceWithOptions(admin->ctx, &opts);
     }
 }
 
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 bundleActivator_stop(void * userData, celix_bundle_context_t *con
     celix_status_t status;
     struct disc_mock_activator * act = userData;
 
+    serviceRegistration_unregister(act->endpointListenerService);
     status = serviceRegistration_unregister(act->reg);
 
     return status;
diff --git a/libs/framework/gtest/src/bundle_context_services_test.cpp b/libs/framework/gtest/src/bundle_context_services_test.cpp
index e544d2f..c62760e 100644
--- a/libs/framework/gtest/src/bundle_context_services_test.cpp
+++ b/libs/framework/gtest/src/bundle_context_services_test.cpp
@@ -412,9 +412,7 @@ TEST_F(CelixBundleContextServicesTests, servicesTrackerTest) {
 
 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 +425,9 @@ TEST_F(CelixBundleContextServicesTests, servicesTrackerInvalidArgsTest) {
     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
@@ -711,8 +711,62 @@ TEST_F(CelixBundleContextServicesTests, servicesTrackerSetTest) {
     ASSERT_EQ(3, 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 {
@@ -825,3 +879,99 @@ TEST_F(CelixBundleContextServicesTests, trackServiceTrackerTest) {
     celix_bundleContext_stopTracker(ctx, trackerId);
     celix_bundleContext_stopTracker(ctx, tracker4);
 }
+
+TEST_F(CelixBundleContextServicesTests, reserveServiceId) {
+    void *svc = (void*)0x42;
+
+    celix_service_registration_options_t opts{};
+    opts.serviceName = "test_service";
+    opts.reservedSvcId = celix_bundleContext_reserveSvcId(ctx);
+    opts.svc = &svc;
+    ASSERT_GT(opts.reservedSvcId, 0);
+
+    long svcId = celix_bundleContext_registerServiceWithOptions(ctx, &opts);
+    EXPECT_TRUE(svcId >= 0);
+    EXPECT_EQ(opts.reservedSvcId, svcId);
+
+    long foundSvcId = celix_bundleContext_findService(ctx, "test_service");
+    EXPECT_GE(foundSvcId, 0);
+
+    celix_bundleContext_unregisterService(ctx, svcId);
+    foundSvcId = celix_bundleContext_findService(ctx, "test_service");
+    EXPECT_EQ(-1, foundSvcId);
+}
+
+TEST_F(CelixBundleContextServicesTests, reserveServiceIdAndRemoveBeforeRegister) {
+    void *svc = (void*)0x42;
+
+    celix_service_registration_options_t opts{};
+    opts.serviceName = "test_service";
+    opts.reservedSvcId = celix_bundleContext_reserveSvcId(ctx);
+    opts.svc = &svc;
+    ASSERT_GT(opts.reservedSvcId, 0);
+
+    celix_bundleContext_unregisterService(ctx, opts.reservedSvcId); //will effectually cancel the registering for opts.reservedSvcId
+
+    long svcId = celix_bundleContext_registerServiceWithOptions(ctx, &opts); //will not register service, but
+    EXPECT_EQ(-2, svcId);
+
+    celix_bundleContext_unregisterService(ctx, svcId);
+    long foundSvcId = celix_bundleContext_findService(ctx, "test_service");
+    EXPECT_EQ(-1, foundSvcId);
+}
+
+TEST_F(CelixBundleContextServicesTests, reserveServiceIdInvalidUse) {
+    void *svc = (void*)0x42;
+
+    celix_properties_t *props = celix_properties_create();
+    celix_properties_set(props, "test.prop", "val");
+
+    celix_service_registration_options_t opts{};
+    opts.serviceName = "test_service";
+    opts.reservedSvcId = 42; //NOTE not valid -> not reserved through celix_bundleContext_reserveSvcId.
+    opts.svc = &svc;
+    opts.properties = props;
+    ASSERT_GT(opts.reservedSvcId, 0);
+
+    long svcId = celix_bundleContext_registerServiceWithOptions(ctx, &opts);
+    EXPECT_EQ(-1, svcId);
+
+    props = celix_properties_create();
+    celix_properties_set(props, "test.prop", "val");
+    opts.reservedSvcId = celix_bundleContext_reserveSvcId(ctx);
+    opts.properties = props;
+    svcId = celix_bundleContext_registerServiceWithOptions(ctx, &opts);
+    EXPECT_GE(svcId, 0);
+
+    props = celix_properties_create();
+    celix_properties_set(props, "test.prop", "val");
+    opts.properties = props;
+    long invalidSvcId = celix_bundleContext_registerServiceWithOptions(ctx, &opts); //using a reservedId again is not valid.
+    EXPECT_EQ(-1, invalidSvcId);
+
+    celix_bundleContext_unregisterService(ctx, svcId);
+    celix_bundleContext_unregisterService(ctx, svcId);
+    celix_bundleContext_unregisterService(ctx, svcId);
+    celix_bundleContext_unregisterService(ctx, invalidSvcId);
+    celix_bundleContext_unregisterService(ctx, invalidSvcId);
+}
+
+TEST_F(CelixBundleContextServicesTests, missingStopUnregisters) {
+    void *svc = (void *) 0x42;
+    long svcId = celix_bundleContext_registerService(ctx, svc, "test_service", nullptr); //dangling service registration
+    EXPECT_GE(svcId, 0);
+
+    long reservedId = celix_bundleContext_reserveSvcId(ctx); //dangling resvered id
+    EXPECT_GE(reservedId, 0);
+
+    long trkId1 = celix_bundleContext_trackBundles(ctx, nullptr, nullptr, nullptr); //dangling bundle tracker
+    EXPECT_GE(trkId1, 0);
+
+    long trkId2 = celix_bundleContext_trackService(ctx, "test_service", nullptr, nullptr); //dangling svc tracker
+    EXPECT_GE(trkId2, 0);
+
+    long trkId3 = celix_bundleContext_trackServiceTrackers(ctx, "test_service", nullptr, nullptr, nullptr); //dangling svc tracker tracker
+    EXPECT_GE(trkId3, 0);
+
+    //note "forgetting" unregister/stopTracking
+}
diff --git a/libs/framework/include/celix_bundle_context.h b/libs/framework/include/celix_bundle_context.h
index 2f11e06..bf11df7 100644
--- a/libs/framework/include/celix_bundle_context.h
+++ b/libs/framework/include/celix_bundle_context.h
@@ -47,7 +47,7 @@ extern "C" {
 * @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.
+* @return The serviceId (>= 0), -1 if the registration was unsuccessful and -2 if the registration was cancel (only possible when using opts.reservedServiceId.
 */
 long celix_bundleContext_registerService(celix_bundle_context_t *ctx, void *svc, const char *serviceName, celix_properties_t *properties);
 
@@ -126,9 +126,35 @@ typedef struct celix_service_registration_options {
      * for this.
      */
     const char *serviceVersion OPTS_INIT;
+
+    /**
+     * If set to > 0, this svc id will be used to register the service.
+     * The reservedSvcId should be reserved with a call to celix_bundleContext_reserveSvcId
+     */
+    long reservedSvcId OPTS_INIT;
 } celix_service_registration_options_t;
 
 /**
+ * Reserves a service id, which is expected to be used to register a service in the future.
+ *
+ * If a celix_bundleContext_unregisterService with the reserved service id is called earlier than
+ * the celix_bundleContext_registerServiceWithOptions with the reserved service. The registering
+ * of the service will be cancelled instead.
+
+ * This can help in registering/unregistering services outside of locks without creating race
+ * conditions.
+ * Note that if this is used service can "unregistered" before they are actually registered and
+ * as such the service pointer can be invalid at the time of registering.
+ * This means that the service registering can be done outside of locks, but accessing service
+ * should always be done using locks.
+ * 
+ *
+ * @param ctx the bundle context.
+ * @return A service id, bounded to this bundle context. Note in this case the svc id will be > 0.
+ */
+long celix_bundleContext_reserveSvcId(celix_bundle_context_t* ctx);
+
+/**
  * C Macro to create a empty celix_service_registration_options_t type.
  */
 #ifndef __cplusplus
@@ -137,7 +163,8 @@ typedef struct celix_service_registration_options {
     .serviceName = NULL, \
     .properties = NULL, \
     .serviceLanguage = NULL, \
-    .serviceVersion = NULL }
+    .serviceVersion = NULL, \
+    .reservedSvcId = 0 }
 #endif
 
 
@@ -192,7 +219,8 @@ celix_array_list_t* celix_bundleContext_findServices(celix_bundle_context_t *ctx
  */
 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;
 
@@ -256,7 +284,8 @@ celix_array_list_t* celix_bundleContext_findServicesWithOptions(celix_bundle_con
  * If a service is removed a the callback with be called with next highest ranking service or NULL as service.
  *
  * @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.
@@ -273,6 +302,7 @@ long celix_bundleContext_trackService(
  *
  * @param ctx The bundle context.
  * @param serviceName The required service name to track
+ *                    If NULL is all service are tracked.
  * @param callbackHandle The data pointer, which will be used in the callbacks
  * @param add is a required callback, which will be called when a service is added and initially for the existing service.
  * @param remove is a required callback, which will be called when a service is removed
@@ -815,6 +845,7 @@ typedef struct celix_service_tracker_info {
  *
  * @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.
diff --git a/libs/framework/include/service_registry.h b/libs/framework/include/service_registry.h
index db93cb5..4697b79 100644
--- a/libs/framework/include/service_registry.h
+++ b/libs/framework/include/service_registry.h
@@ -60,8 +60,6 @@ serviceRegistry_registerServiceFactory(service_registry_pt registry, celix_bundl
 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,15 @@ celix_status_t celix_serviceRegistry_addServiceListener(celix_service_registry_t
 
 celix_status_t celix_serviceRegistry_removeServiceListener(celix_service_registry_t *reg, celix_service_listener_t *listener);
 
+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 +110,7 @@ celix_serviceRegistry_registerServiceFactory(
         const char *serviceName,
         celix_service_factory_t *factory,
         celix_properties_t* props,
+        long reserveId,
         service_registration_t **registration);
 
 /**
diff --git a/libs/framework/src/bundle.c b/libs/framework/src/bundle.c
index 9994675..be747b9 100644
--- a/libs/framework/src/bundle.c
+++ b/libs/framework/src/bundle.c
@@ -173,7 +173,7 @@ celix_status_t bundle_getEntry(const_bundle_pt bundle, const char* name, char**
 }
 
 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;
 	}
diff --git a/libs/framework/src/bundle_context.c b/libs/framework/src/bundle_context.c
index b7f5326..b052d2e 100644
--- a/libs/framework/src/bundle_context.c
+++ b/libs/framework/src/bundle_context.c
@@ -21,6 +21,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <utils.h>
+#include <assert.h>
 
 #include "celix_utils.h"
 #include "celix_constants.h"
@@ -34,11 +35,14 @@
 #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);
 
 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 +65,7 @@ celix_status_t bundleContext_create(framework_pt framework, celix_framework_logg
             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->reservedIds =  hashMap_create(NULL,NULL,NULL,NULL);
             context->nextTrackerId = 1L;
 
             *bundle_context = context;
@@ -80,15 +85,21 @@ celix_status_t bundleContext_destroy(bundle_context_pt context) {
 	    celixThreadMutex_lock(&context->mutex);
 
 
-	    bundleContext_cleanupBundleTrackers(context);
-	    bundleContext_cleanupServiceTrackers(context);
-        bundleContext_cleanupServiceTrackerTrackers(context);
+	    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);
+        assert(hashMap_size(context->reservedIds) == 0);
+        hashMap_destroy(context->reservedIds, false, false);
 
-	    //NOTE still present service registrations will be cleared during bundle stop in the
+        //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 +117,15 @@ celix_status_t bundleContext_destroy(bundle_context_pt context) {
 	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 atleast 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);
@@ -451,7 +471,10 @@ long celix_bundleContext_registerServiceFactory(celix_bundle_context_t *ctx, cel
 
 long celix_bundleContext_registerServiceWithOptions(bundle_context_t *ctx, const celix_service_registration_options_t *opts) {
     long svcId = -1;
+    long reservedId = -1;
     service_registration_t *reg = NULL;
+
+    //set properties
     celix_properties_t *props = opts->properties;
     if (props == NULL) {
         props = celix_properties_create();
@@ -461,16 +484,43 @@ long celix_bundleContext_registerServiceWithOptions(bundle_context_t *ctx, const
     }
     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) {
+
+    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");
+    }
+
+    //check reserved ids
+    bool cancelled = false;
+    if (valid && opts->reservedSvcId > 0) {
+        celixThreadMutex_lock(&ctx->mutex);
+        if (hashMap_containsKey(ctx->reservedIds, (void*)opts->reservedSvcId)) {
+            cancelled = (bool)hashMap_remove(ctx->reservedIds, (void*)opts->reservedSvcId);
+            if (!cancelled) {
+                reservedId = opts->reservedSvcId;
+            } else {
+                svcId = -2;
+            }
+        } else {
+            //not reserved
+            valid = false;
+            fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Invalid reservedSvcId provided (%li). Ensure that service id are reserved using celix_bundleContext_reserveId and only used once", opts->reservedSvcId);
+        }
+        celixThreadMutex_unlock(&ctx->mutex);
+    }
+
+    if (valid && !cancelled) {
         if (opts->factory != NULL) {
-            reg = celix_framework_registerServiceFactory(ctx->framework, ctx->bundle, opts->serviceName, opts->factory, props);
+            reg = celix_framework_registerServiceFactory(ctx->framework, ctx->bundle, opts->serviceName, opts->factory, props, reservedId);
         } else {
-            bundleContext_registerService(ctx, opts->serviceName, opts->svc, props, &reg);
+            reg = celix_framework_registerService(ctx->framework, ctx->bundle, opts->serviceName, opts->svc, props, reservedId);
         }
         svcId = serviceRegistration_getServiceId(reg); //save to call with NULL
     } else {
         framework_logIfError(ctx->framework->logger, CELIX_ILLEGAL_ARGUMENT, NULL, "Required serviceName argument is NULL");
     }
+
+
     if (svcId < 0) {
         properties_destroy(props);
     } else {
@@ -483,6 +533,7 @@ long celix_bundleContext_registerServiceWithOptions(bundle_context_t *ctx, const
 
 void celix_bundleContext_unregisterService(bundle_context_t *ctx, long serviceId) {
     service_registration_t *found = NULL;
+    bool wasReserved = false;
     if (ctx != NULL && serviceId >= 0) {
         celixThreadMutex_lock(&ctx->mutex);
         unsigned int size = arrayList_size(ctx->svcRegistrations);
@@ -499,8 +550,22 @@ void celix_bundleContext_unregisterService(bundle_context_t *ctx, long serviceId
         }
         celixThreadMutex_unlock(&ctx->mutex);
 
+        celixThreadMutex_lock(&ctx->mutex);
+        if (hashMap_containsKey(ctx->reservedIds, (void*)serviceId)) {
+            bool cancelled = (bool)hashMap_get(ctx->reservedIds, (void*)serviceId);
+            if (cancelled) {
+                //already cancelled
+                fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Service registration for service with a svc id %li already cancelled!", serviceId);
+            }
+            wasReserved = true;
+            hashMap_put(ctx->reservedIds, (void*)serviceId, (void*)true);
+        }
+        celixThreadMutex_unlock(&ctx->mutex);
+
         if (found != NULL) {
             serviceRegistration_unregister(found);
+        } else if (wasReserved) {
+            //nop
         } 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));
         }
@@ -604,36 +669,130 @@ bool celix_bundleContext_useBundle(
 }
 
 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);
+    }
+    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);
     }
-    hashMap_destroy(ctx->bundleTrackers, false, false);
 }
 
 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_service_tracker_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->filter);
+        if (danglingTrkIds == NULL) {
+            danglingTrkIds = celix_arrayList_create();
+        }
+        celix_arrayList_addLong(danglingTrkIds, trkId);
+    }
+    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);
     }
-    hashMap_destroy(ctx->serviceTrackers, false, false);
 }
 
 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);
+    }
+    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);
     }
-    hashMap_destroy(ctx->metaTrackers, false, false);
 }
 
+static void bundleContext_cleanupServiceRegistration(bundle_context_t* ctx) {
+    module_pt module;
+    const char *symbolicName;
+    bundle_getCurrentModule(ctx->bundle, &module);
+    module_getSymbolicName(module, &symbolicName);
+
+    celix_array_list_t* danglingSvcIds = NULL;
+
+    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();
+        }
+        celix_arrayList_addLong(danglingSvcIds, svcId);
+    }
+    hash_map_iterator_t iter = hashMapIterator_construct(ctx->reservedIds);
+    while (hashMapIterator_hasNext(&iter)) {
+        long svcId = (long)hashMapIterator_nextKey(&iter);
+        fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Dangling reserved svc id %li for bundle %s. Add missing 'celix_bundleContext_unregisterService' calls.", svcId, symbolicName);
+    }
+    hashMap_clear(ctx->reservedIds, false, false);
+    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);
+    }
+}
 
 void celix_bundleContext_stopTracker(bundle_context_t *ctx, long trackerId) {
     if (ctx != NULL && trackerId >0) {
@@ -841,12 +1000,23 @@ long celix_bundleContext_trackServices(
 
 
 long celix_bundleContext_trackServicesWithOptions(bundle_context_t *ctx, const celix_service_tracking_options_t *opts) {
+    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");
+    }
+
     long trackerId = -1;
     celix_service_tracker_t *tracker = celix_serviceTracker_createWithOptions(ctx, opts);
     if (tracker != NULL) {
         celixThreadMutex_lock(&ctx->mutex);
         trackerId = ctx->nextTrackerId++;
-        hashMap_put(ctx->serviceTrackers, (void*)trackerId, tracker);
+        hashMap_put(ctx->serviceTrackers, (void *) trackerId, tracker);
         celixThreadMutex_unlock(&ctx->mutex);
     }
     return trackerId;
@@ -915,9 +1085,11 @@ static celix_status_t bundleContext_callServicedTrackerTrackerCallback(void *han
             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);
@@ -944,8 +1116,7 @@ long celix_bundleContext_trackServiceTrackers(
     long trackerId = -1L;
 
     if (serviceName == NULL) {
-        fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Required serviceName not provided for function ", __FUNCTION__);
-        return trackerId;
+        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));
@@ -953,7 +1124,7 @@ long celix_bundleContext_trackServiceTrackers(
     entry->callbackHandle = callbackHandle;
     entry->add = trackerAdd;
     entry->remove = trackerRemove;
-    entry->serviceName = strndup(serviceName, 1024*1024);
+    entry->serviceName = celix_utils_strdup(serviceName);
 
     entry->hook.handle = entry;
     entry->hook.added = bundleContext_callServicedTrackerTrackerAdd;
@@ -1052,4 +1223,18 @@ char* celix_bundleContext_getBundleSymbolicName(celix_bundle_context_t *ctx, lon
     char *name = NULL;
     celix_framework_useBundle(ctx->framework, false, bndId, &name, celix_bundleContext_getBundleSymbolicNameCallback);
     return name;
+}
+
+long celix_bundleContext_reserveSvcId(celix_bundle_context_t *ctx) {
+    celixThreadMutex_lock(&ctx->mutex);
+    long reservedId = celix_serviceRegistry_nextSvcId(ctx->framework->registry);
+    if (reservedId == 0) {
+        //note svcId is the first and valid id, but for reserved id, we want a service id > 0,
+        //so that a 0 initialized CELIX_EMPTY_SERVICE_REGISTRATION_OPTIONS is configured not to
+        //use a reservedId
+        reservedId = celix_serviceRegistry_nextSvcId(ctx->framework->registry);
+    }
+    hashMap_put(ctx->reservedIds, (void*)reservedId, (void*)false);
+    celixThreadMutex_unlock(&ctx->mutex);
+    return reservedId;
 }
\ 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..e442bd1 100644
--- a/libs/framework/src/bundle_context_private.h
+++ b/libs/framework/src/bundle_context_private.h
@@ -56,7 +56,11 @@ struct celix_bundle_context {
 	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 *reservedIds; //key = reserved svc id, value =  cancelled (bool)
 };
 
 
+void celix_bundleContext_cleanup(celix_bundle_context_t *ctx);
+
+
 #endif /* BUNDLE_CONTEXT_PRIVATE_H_ */
diff --git a/libs/framework/src/framework.c b/libs/framework/src/framework.c
index 73c9e3a..45ac88a 100644
--- a/libs/framework/src/framework.c
+++ b/libs/framework/src/framework.c
@@ -1104,7 +1104,8 @@ celix_status_t fw_stopBundle(framework_pt framework, long bndId, bool record) {
 
             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);
+                celix_serviceTracker_syncForContext(framework->bundle->context);
                 if (status == CELIX_SUCCESS) {
                     module_pt module = NULL;
                     const char *symbolicName = NULL;
@@ -1112,8 +1113,6 @@ celix_status_t fw_stopBundle(framework_pt framework, long bndId, bool record) {
                     bundle_getCurrentModule(entry->bnd, &module);
                     module_getSymbolicName(module, &symbolicName);
                     bundle_getBundleId(entry->bnd, &id);
-
-                    serviceRegistry_clearReferencesFor(framework->registry, entry->bnd);
                 }
 
                 if (context != NULL) {
@@ -1125,6 +1124,8 @@ celix_status_t fw_stopBundle(framework_pt framework, long bndId, bool record) {
             } else if (bndId == 0) {
                 //framework bundle
                 celix_serviceTracker_syncForContext(framework->bundle->context);
+                celix_bundleContext_cleanup(entry->bnd->context);
+                celix_serviceTracker_syncForContext(framework->bundle->context);
             }
 	    }
 
@@ -2384,9 +2385,9 @@ bool celix_framework_useBundle(framework_t *fw, bool onlyActive, long bundleId,
     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) {
+service_registration_t* celix_framework_registerServiceFactory(framework_t *fw , celix_bundle_t *bnd, const char* serviceName, celix_service_factory_t *factory, celix_properties_t *properties, long reservedId) {
     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);
@@ -2394,7 +2395,33 @@ service_registration_t* celix_framework_registerServiceFactory(framework_t *fw ,
 
 
     if (serviceName != NULL && factory != NULL) {
-        status = CELIX_DO_IF(status, celix_serviceRegistry_registerServiceFactory(fw->registry, bnd, serviceName, factory, properties, &reg));
+        status = celix_serviceRegistry_registerServiceFactory(fw->registry, bnd, serviceName, factory, properties, reservedId, &reg);
+    } else {
+        fw_log(fw->logger, CELIX_LOG_LEVEL_ERROR, "Invalid arguments serviceName and/or factory argument is NULL (%s/%p)", serviceName, factory);
+        status = CELIX_ILLEGAL_ARGUMENT;
+    }
+
+    fw_bundleEntry_decreaseUseCount(entry);
+
+    framework_logIfError(fw->logger, status, error, "Cannot register service factory: %s", serviceName);
+
+    return reg;
+}
+
+service_registration_t* celix_framework_registerService(framework_t *fw , celix_bundle_t *bnd, const char* serviceName, void* svc, celix_properties_t *properties, long reservedId) {
+    const char *error = NULL;
+    celix_status_t status;
+    service_registration_t *reg = NULL;
+
+    long bndId = celix_bundle_getId(bnd);
+    celix_framework_bundle_entry_t *entry = fw_bundleEntry_getBundleEntryAndIncreaseUseCount(fw, bndId);
+
+
+    if (serviceName != NULL && svc != NULL) {
+        status = celix_serviceRegistry_registerService(fw->registry, bnd, serviceName, svc, properties, reservedId, &reg);
+    } else {
+        fw_log(fw->logger, CELIX_LOG_LEVEL_ERROR, "Invalid arguments serviceName and/or svc argument is NULL (%s/%p)", serviceName, svc);
+        status = CELIX_ILLEGAL_ARGUMENT;
     }
 
     fw_bundleEntry_decreaseUseCount(entry);
diff --git a/libs/framework/src/framework_private.h b/libs/framework/src/framework_private.h
index 4c0b517..bbd6370 100644
--- a/libs/framework/src/framework_private.h
+++ b/libs/framework/src/framework_private.h
@@ -151,6 +151,8 @@ FRAMEWORK_EXPORT bundle_pt framework_getBundleById(framework_pt framework, long
  **********************************************************************************************************************
  **********************************************************************************************************************/
 
-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);
+service_registration_t* celix_framework_registerServiceFactory(framework_t *fw, celix_bundle_t *bnd, const char* serviceName, celix_service_factory_t *factory, celix_properties_t *properties, long reservedSvcId);
+service_registration_t* celix_framework_registerService(framework_t *fw, celix_bundle_t *bnd, const char* serviceName, void* svc, celix_properties_t *properties, long reservedSvcId);
+
 
 #endif /* FRAMEWORK_PRIVATE_H_ */
diff --git a/libs/framework/src/service_registry.c b/libs/framework/src/service_registry.c
index 6c7b633..0184f2d 100644
--- a/libs/framework/src/service_registry.c
+++ b/libs/framework/src/service_registry.c
@@ -36,11 +36,10 @@
 #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,
@@ -79,8 +78,8 @@ celix_status_t serviceRegistry_create(framework_pt framework, service_registry_p
 
 		reg->serviceRegistrations = hashMap_create(NULL, NULL, NULL, NULL);
 		reg->framework = framework;
-		reg->nextServiceId = 1L;
-		reg->serviceReferences = hashMap_create(NULL, NULL, NULL, NULL);
+        reg->nextServiceId = 1L;
+        reg->serviceReferences = hashMap_create(NULL, NULL, NULL, NULL);
 
         reg->checkDeletedReferences = CHECK_DELETED_REFERENCES;
         reg->deletedServiceReferences = hashMap_create(NULL, NULL, NULL, NULL);
@@ -198,16 +197,16 @@ celix_status_t serviceRegistry_getRegisteredServices(service_registry_pt registr
 }
 
 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 +298,6 @@ celix_status_t serviceRegistry_unregisterService(service_registry_pt registry, b
 	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(&registry->lock);
-    registrations = hashMap_get(registry->serviceRegistrations, bundle);
-    registrationsLeft = (registrations != NULL);
-    if (registrationsLeft) {
-        registrationsLeft = (arrayList_size(registrations) > 0);
-    }
-    celixThreadRwlock_unlock(&registry->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(&registry->lock);
-        registrations = hashMap_get(registry->serviceRegistrations, bundle);
-        registrationsLeft = (registrations != NULL);
-        if (registrationsLeft) {
-            registrationsLeft = (arrayList_size(registrations) > 0);
-        }
-        celixThreadRwlock_unlock(&registry->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;
@@ -959,8 +914,21 @@ celix_serviceRegistry_registerServiceFactory(
         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, 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 *) factory, props, CELIX_FACTORY_SERVICE, 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) {
diff --git a/libs/framework/src/service_tracker.c b/libs/framework/src/service_tracker.c
index 53fd31a..8775c23 100644
--- a/libs/framework/src/service_tracker.c
+++ b/libs/framework/src/service_tracker.c
@@ -687,11 +687,12 @@ celix_service_tracker_t* celix_serviceTracker_createWithOptions(
         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;
@@ -738,23 +739,23 @@ celix_service_tracker_t* celix_serviceTracker_createWithOptions(
             //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);
+                    asprintf(&tracker->filter, "(&(%s=%s)%s%s)", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, versionRange, opts->filter.filter);
                 } else if (versionRange != NULL) {
-                    asprintf(&tracker->filter, "(&(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, opts->filter.serviceName, versionRange);
+                    asprintf(&tracker->filter, "(&(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, versionRange);
                 } else if (opts->filter.filter != NULL) {
-                    asprintf(&tracker->filter, "(&(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, opts->filter.serviceName, opts->filter.filter);
+                    asprintf(&tracker->filter, "(&(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, opts->filter.filter);
                 } else {
-                    asprintf(&tracker->filter, "(&(%s=%s))", OSGI_FRAMEWORK_OBJECTCLASS, opts->filter.serviceName);
+                    asprintf(&tracker->filter, "(&(%s=%s))", OSGI_FRAMEWORK_OBJECTCLASS, 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);
+                    asprintf(&tracker->filter, "(&(%s=%s)(%s=%s)%s%s)", OSGI_FRAMEWORK_OBJECTCLASS, 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);
+                    asprintf(&tracker->filter, "(&(%s=%s)(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, 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);
+                    asprintf(&tracker->filter, "(&(%s=%s)(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, 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);
+                    asprintf(&tracker->filter, "(&(%s=%s)(%s=%s))", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang);
                 }
             }
 
@@ -765,12 +766,7 @@ celix_service_tracker_t* celix_serviceTracker_createWithOptions(
             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;
 }
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 @@ celix_log_level_e celix_logUtils_logLevelFromStringWithCheck(const char *level,
  * 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 @@ void celix_logUtils_logToStdout(const char *logName, celix_log_level_e level, co
  *
  * 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 @@ void celix_logUtils_logToStdoutDetails(const char *logName, celix_log_level_e le
  * 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 @@ void celix_logUtils_vLogToStdout(const char *logName, celix_log_level_e level, c
  *
  * 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/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 @@ void celix_logUtils_vLogToStdout(const char *logName, celix_log_level_e level, c
     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 @@ void celix_logUtils_vLogToStdoutDetails(const char *logName, celix_log_level_e l
         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