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:15 UTC

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

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