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

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

commit 2eca5a7944a68e427fcbcd7b5a64d11ec6c52679
Author: Pepijn Noltes <[email protected]>
AuthorDate: Sun May 8 22:45:47 2022 +0200

    Refactors celix cmp to include intermediate states
---
 documents/diagrams/component_lifecycle.puml        |  47 +++++
 .../gtest/src/DependencyManagerTestSuite.cc        |  37 +++-
 libs/framework/include/celix/dm/Component.h        |  39 +++-
 libs/framework/include/celix_dm_component.h        |  15 +-
 libs/framework/src/dm_component_impl.c             | 197 ++++++++++++++-------
 5 files changed, 252 insertions(+), 83 deletions(-)

diff --git a/documents/diagrams/component_lifecycle.puml 
b/documents/diagrams/component_lifecycle.puml
new file mode 100644
index 00000000..c27fa33e
--- /dev/null
+++ b/documents/diagrams/component_lifecycle.puml
@@ -0,0 +1,47 @@
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+@startuml
+state "Waiting for Required" as WaitingForRequired
+state "Initialized And Waiting For Required" as Initialized
+
+[*] -down-> Inactive
+Inactive -left-> [*]
+Inactive  -right-> WaitingForRequired
+WaitingForRequired -down-> Initializing
+Initializing -down-> Initialized
+
+state Active {
+    state "Tracking Optional Dependencies" as TrackingOptional
+    [*] -> Starting
+    Starting -down-> TrackingOptional
+    TrackingOptional -> Suspending
+    Suspending -> Suspended
+    Suspended -> Resuming
+    Resuming --> TrackingOptional
+    TrackingOptional --> Stopping
+    Stopping -left-> [*]
+}
+
+Initialized -> Active
+Active -> Initialized
+
+Initialized -down-> Deinitializing
+Deinitializing -down-> Inactive
+
+
+
+
+@enduml
\ No newline at end of file
diff --git a/libs/framework/gtest/src/DependencyManagerTestSuite.cc 
b/libs/framework/gtest/src/DependencyManagerTestSuite.cc
index e55efaee..a07cbde9 100644
--- a/libs/framework/gtest/src/DependencyManagerTestSuite.cc
+++ b/libs/framework/gtest/src/DependencyManagerTestSuite.cc
@@ -123,7 +123,7 @@ TEST_F(DependencyManagerTestSuite, 
DmComponentRemoveAllAsync) {
     EXPECT_EQ(1, count.load());
 }
 
-TEST_F(DependencyManagerTestSuite, CDmGetInfo) {
+TEST_F(DependencyManagerTestSuite, DmGetInfo) {
     auto* mng = celix_bundleContext_getDependencyManager(ctx);
     auto* cmp = celix_dmComponent_create(ctx, "test1");
 
@@ -207,7 +207,7 @@ TEST_F(DependencyManagerTestSuite, CxxDmGetInfo) {
     auto& cmpInfo = info.components[0];
     EXPECT_TRUE(!cmpInfo.uuid.empty());
     EXPECT_EQ(cmpInfo.name, "Cmp1");
-    EXPECT_EQ(cmpInfo.state, "WAITING_FOR_REQUIRED");
+    EXPECT_EQ(cmpInfo.state, "CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED");
     EXPECT_FALSE(cmpInfo.isActive);
     EXPECT_EQ(cmpInfo.nrOfTimesStarted, 0);
     EXPECT_EQ(cmpInfo.nrOfTimesResumed, 0);
@@ -641,13 +641,13 @@ TEST_F(DependencyManagerTestSuite, 
UnneededSuspendIsPrevented) {
 
     celix::dm::DependencyManager dm{ctx};
     //cmp1 has lifecycle callbacks, but not set or add/rem callbacks for the 
service dependency -> should not trigger suspend
-    auto& cmp1 = dm.createComponent<CounterComponent>()
+    auto& cmp1 = dm.createComponent<CounterComponent>("CounterCmp1")
             .setCallbacks(nullptr, &CounterComponent::start, 
&CounterComponent::stop, nullptr);
     cmp1.createServiceDependency<TestService>();
     cmp1.build();
 
     //cmp2 has lifecycle callbacks and set, add/rem callbacks for the service 
dependency -> should trigger suspend 2x
-    auto& cmp2 = dm.createComponent<CounterComponent>()
+    auto& cmp2 = dm.createComponent<CounterComponent>("CounterCmp2")
             .setCallbacks(nullptr, &CounterComponent::start, 
&CounterComponent::stop, nullptr);
     cmp2.createServiceDependency<TestService>()
             .setCallbacks(&CounterComponent::setService)
@@ -788,6 +788,35 @@ TEST_F(DependencyManagerTestSuite, 
installBundleWithDepManActivator) {
     celix_arrayList_destroy(list);
 }
 
+TEST_F(DependencyManagerTestSuite, testStateToString) {
+    const char* state = 
celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_INACTIVE);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_INACTIVE");
+    state = 
celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED");
+    state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_INITIALIZING);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_INITIALIZING");
+    state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_DEINITIALIZING);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_DEINITIALIZING");
+    state = 
celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED);
+    EXPECT_STREQ(state, 
"CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED");
+    state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_STARTING);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_STARTING");
+    state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_STOPPING);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_STOPPING");
+    state = 
celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_TRACKING_OPTIONAL);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_TRACKING_OPTIONAL");
+    state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_SUSPENDING);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_SUSPENDING");
+    state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_SUSPENDED);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_SUSPENDED");
+    state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_RESUMING);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_RESUMING");
+    state = celix_dmComponent_stateToString((celix_dm_component_state_enum)0);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_INACTIVE");
+    state = celix_dmComponent_stateToString((celix_dm_component_state_enum)16);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_INACTIVE");
+}
+
 #if __cplusplus >= 201703L //C++17 or higher
 
 class TestInterfaceWithStaticInfo {
diff --git a/libs/framework/include/celix/dm/Component.h 
b/libs/framework/include/celix/dm/Component.h
index 6c9004db..d101b35f 100644
--- a/libs/framework/include/celix/dm/Component.h
+++ b/libs/framework/include/celix/dm/Component.h
@@ -41,10 +41,17 @@
 namespace celix { namespace dm {
 
     enum class ComponentState {
-        INACTIVE = 1,
-        WAITING_FOR_REQUIRED = 2,
-        INSTANTIATED_AND_WAITING_FOR_REQUIRED = 3,
-        TRACKING_OPTIONAL = 4,
+        INACTIVE =                              1,
+        WAITING_FOR_REQUIRED =                  2,
+        INITIALIZING =                          3,
+        DEINITIALIZING =                        4,
+        INSTANTIATED_AND_WAITING_FOR_REQUIRED = 5,
+        STARTING =                              6,
+        STOPPING =                              7,
+        TRACKING_OPTIONAL =                     8,
+        SUSPENDING =                            9,
+        SUSPENDED =                             10,
+        RESUMING =                              11,
     };
 
     class BaseComponent {
@@ -90,14 +97,28 @@ namespace celix { namespace dm {
         ComponentState getState() const {
              auto cState = celix_dmComponent_currentState(cCmp);
              switch (cState) {
-                 case DM_CMP_STATE_INACTIVE:
-                     return ComponentState::INACTIVE;
-                 case DM_CMP_STATE_WAITING_FOR_REQUIRED:
+                 case CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED:
                      return ComponentState::WAITING_FOR_REQUIRED;
-                 case DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED:
+                 case CELIX_DM_CMP_STATE_INITIALIZING:
+                     return ComponentState::INITIALIZING;
+                 case CELIX_DM_CMP_STATE_DEINITIALIZING:
+                     return ComponentState::DEINITIALIZING;
+                 case CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED:
                      return 
ComponentState::INSTANTIATED_AND_WAITING_FOR_REQUIRED;
-                 default:
+                 case CELIX_DM_CMP_STATE_STARTING:
+                     return ComponentState::STARTING;
+                 case CELIX_DM_CMP_STATE_STOPPING:
+                     return ComponentState::STOPPING;
+                 case CELIX_DM_CMP_STATE_TRACKING_OPTIONAL:
                      return ComponentState::TRACKING_OPTIONAL;
+                 case CELIX_DM_CMP_STATE_SUSPENDING:
+                     return ComponentState::SUSPENDING;
+                 case CELIX_DM_CMP_STATE_SUSPENDED:
+                     return ComponentState::SUSPENDED;
+                 case CELIX_DM_CMP_STATE_RESUMING:
+                     return ComponentState::RESUMING;
+                 default:
+                     return ComponentState::INACTIVE;
              }
          }
 
diff --git a/libs/framework/include/celix_dm_component.h 
b/libs/framework/include/celix_dm_component.h
index 29d3cb3d..885a46b2 100644
--- a/libs/framework/include/celix_dm_component.h
+++ b/libs/framework/include/celix_dm_component.h
@@ -35,10 +35,17 @@ extern "C" {
 #define CELIX_DM_COMPONENT_UUID     "component.uuid"
 
 typedef enum celix_dm_component_state_enum {
-    DM_CMP_STATE_INACTIVE = 1,
-    DM_CMP_STATE_WAITING_FOR_REQUIRED = 2,
-    DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED = 3,
-    DM_CMP_STATE_TRACKING_OPTIONAL = 4,
+    CELIX_DM_CMP_STATE_INACTIVE =                               1,
+    CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED =                   2,
+    CELIX_DM_CMP_STATE_INITIALIZING =                           3,
+    CELIX_DM_CMP_STATE_DEINITIALIZING =                         4,
+    CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED =   5,
+    CELIX_DM_CMP_STATE_STARTING =                               6,
+    CELIX_DM_CMP_STATE_STOPPING =                               7,
+    CELIX_DM_CMP_STATE_TRACKING_OPTIONAL =                      8,
+    CELIX_DM_CMP_STATE_SUSPENDING =                             9,
+    CELIX_DM_CMP_STATE_SUSPENDED =                              10,
+    CELIX_DM_CMP_STATE_RESUMING =                               11,
 } celix_dm_component_state_t;
 
 #define CELIX_DM_COMPONENT_MAX_ID_LENGTH 64
diff --git a/libs/framework/src/dm_component_impl.c 
b/libs/framework/src/dm_component_impl.c
index c456487d..c4544f75 100644
--- a/libs/framework/src/dm_component_impl.c
+++ b/libs/framework/src/dm_component_impl.c
@@ -57,6 +57,12 @@ struct celix_dm_component_struct {
     size_t nrOfTimesResumed;
 
     bool isEnabled;
+
+    /**
+     * Whether the component is an a transition (active performTransition 
call).
+     * Should only be used inside the Celix event Thread -> no locking needed.
+     */
+    bool inTransition;
 };
 
 typedef struct dm_interface_struct {
@@ -83,6 +89,7 @@ static void 
celix_dmComponent_disableDirectly(celix_dm_component_t *component);
 static celix_status_t 
celix_dmComponent_disableDependencies(celix_dm_component_t *component);
 static bool celix_dmComponent_isDisabled(celix_dm_component_t *component);
 static void celix_dmComponent_cleanupRemovedDependencies(celix_dm_component_t* 
component);
+static bool celix_dmComponent_isActiveInternal(celix_dm_component_t 
*component);
 
 
 celix_dm_component_t* celix_dmComponent_create(bundle_context_t *context, 
const char* name) {
@@ -123,7 +130,7 @@ celix_dm_component_t* 
celix_dmComponent_createWithUUID(bundle_context_t *context
     component->callbackStart = NULL;
     component->callbackStop = NULL;
     component->callbackDeinit = NULL;
-    component->state = DM_CMP_STATE_INACTIVE;
+    component->state = CELIX_DM_CMP_STATE_INACTIVE;
     component->setCLanguageProperty = false;
 
     component->providedInterfaces = celix_arrayList_create();
@@ -251,7 +258,7 @@ celix_status_t 
celix_dmComponent_addServiceDependency(celix_dm_component_t *comp
 
     celixThreadMutex_lock(&component->mutex);
     arrayList_add(component->dependencies, dep);
-    bool startDep = component->state != DM_CMP_STATE_INACTIVE;
+    bool startDep = component->state != CELIX_DM_CMP_STATE_INACTIVE;
     if (startDep) {
         celix_dmServiceDependency_enable(dep);
     }
@@ -266,7 +273,7 @@ celix_status_t 
celix_dmComponent_removeServiceDependency(celix_dm_component_t *c
 
     celixThreadMutex_lock(&component->mutex);
     celix_arrayList_remove(component->dependencies, dep);
-    bool disableDependency = component->state != DM_CMP_STATE_INACTIVE;
+    bool disableDependency = component->state != CELIX_DM_CMP_STATE_INACTIVE;
     if (disableDependency) {
         celix_dmServiceDependency_disable(dep);
     }
@@ -344,7 +351,7 @@ static celix_status_t 
celix_dmComponent_disable(celix_dm_component_t *component)
  */
 static void celix_dmComponent_disableDirectly(celix_dm_component_t *component) 
{
     component->isEnabled = false;
-    component->state = DM_CMP_STATE_INACTIVE;
+    component->state = CELIX_DM_CMP_STATE_INACTIVE;
     celix_dmComponent_unregisterServices(component, false);
     celix_dmComponent_disableDependencies(component);
 }
@@ -389,7 +396,7 @@ static bool 
celix_dmComponent_isDisabled(celix_dm_component_t *component) {
     celixThreadMutex_lock(&component->mutex);
     isStopped =
             !component->isEnabled &&
-            component->state == DM_CMP_STATE_INACTIVE &&
+            component->state == CELIX_DM_CMP_STATE_INACTIVE &&
             celix_dmComponent_areAllDependenciesDisabled(component);
     celixThreadMutex_unlock(&component->mutex);
     return isStopped;
@@ -432,7 +439,7 @@ celix_status_t 
celix_dmComponent_addInterface(celix_dm_component_t *component, c
         interface->properties = properties;
         interface->svcId= -1L;
         celix_arrayList_add(component->providedInterfaces, interface);
-        if (component->state == DM_CMP_STATE_TRACKING_OPTIONAL) {
+        if (component->state == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL) {
             celix_dmComponent_registerServices(component, false);
         }
         celixThreadMutex_unlock(&component->mutex);
@@ -517,49 +524,69 @@ celix_status_t 
celix_private_dmComponent_handleEvent(celix_dm_component_t *compo
 static celix_status_t celix_dmComponent_suspend(celix_dm_component_t 
*component, celix_dm_service_dependency_t *dependency) {
        celix_status_t status = CELIX_SUCCESS;
        if (component->callbackStop != NULL) {
+        celixThreadMutex_lock(&component->mutex);
+        component->state = CELIX_DM_CMP_STATE_SUSPENDING;
         celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_TRACE,
                "Suspending component %s (uuid=%s)",
                component->name,
                component->uuid);
-        celix_dmComponent_unregisterServices(component, true);
+        celix_dmComponent_unregisterServices(component, false);
                status = component->callbackStop(component->implementation);
-               if (status != CELIX_SUCCESS) {
+        if (status == CELIX_SUCCESS) {
+            component->state = CELIX_DM_CMP_STATE_SUSPENDED;
+            celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_TRACE,
+                                    "Suspended component %s (uuid=%s)",
+                                    component->name,
+                                    component->uuid);
+        } else {
             celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_ERROR,
-                                    "Error stopping component %s (uuid=%s) 
using the stop callback",
+                                    "Error stopping component %s (uuid=%s) 
using the stop callback. Disabling component.",
                                     component->name,
                                     component->uuid);
+            celix_dmComponent_disableDirectly(component);
         }
+        celixThreadMutex_unlock(&component->mutex);
        }
        return status;
 }
 
 static celix_status_t celix_dmComponent_resume(celix_dm_component_t 
*component, celix_dm_service_dependency_t *dependency) {
        celix_status_t status = CELIX_SUCCESS;
-       if (component->callbackStart != NULL) {
-           //ensure that the current state is still TRACKING_OPTION. Else 
during a add/rem/set call a required svc dep has been added.
+    celixThreadMutex_lock(&component->mutex);
+       if (component->state == CELIX_DM_CMP_STATE_SUSPENDED) {
+        component->state = CELIX_DM_CMP_STATE_RESUMING;
         celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_TRACE,
                                 "Resuming component %s (uuid=%s)",
                                 component->name,
                                 component->uuid);
         status = component->callbackStart(component->implementation);
         if (status == CELIX_SUCCESS) {
-            celix_dmComponent_registerServices(component, true);
+            celix_dmComponent_registerServices(component, false);
             component->nrOfTimesResumed += 1;
+            component->state = CELIX_DM_CMP_STATE_TRACKING_OPTIONAL;
+            celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_TRACE,
+                                    "Resumed component %s (uuid=%s)",
+                                    component->name,
+                                    component->uuid);
         } else {
             celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_ERROR,
-                                    "Error starting component %s (uuid=%s) 
using the start callback",
+                                    "Error starting component %s (uuid=%s) 
using the start callback. Disabling component.",
                                     component->name,
                                     component->uuid);
+            celixThreadMutex_lock(&component->mutex);
+            celix_dmComponent_disableDirectly(component);
+            celixThreadMutex_unlock(&component->mutex);
         }
-       }
-       return status;
+    }
+    celixThreadMutex_unlock(&component->mutex);
+    return status;
 }
 
 static bool celix_dmComponent_needsSuspend(celix_dm_component_t *component, 
const celix_dm_event_t* event) {
     bool cmpActive = false;
     celixThreadMutex_lock(&component->mutex);
     switch (component->state) {
-        case DM_CMP_STATE_TRACKING_OPTIONAL:
+        case CELIX_DM_CMP_STATE_TRACKING_OPTIONAL:
             cmpActive = true;
             break;
         default: //DM_CMP_STATE_INACTIVE
@@ -586,6 +613,17 @@ static celix_status_t 
celix_dmComponent_handleEvent(celix_dm_component_t *compon
                             component->uuid,
                             event->dep->serviceName);
 
+
+    if (component->inTransition) {
+        /* Note if the component is already in transition (stopping, starting, 
etc) then only remove the svc
+         * function pointers. This can happen when with dependency loops or if 
a component also depends on a
+         * services it provides. (e.g. during stopping the component a handle 
event will be created to remove the
+         * service dependency).
+         */
+        setAddOrRemFp(event->dep, event->svc, event->props);
+        return CELIX_SUCCESS;
+    }
+
     bool eventHandled = false;
     if (event->eventType == CELIX_DM_EVENT_SVC_ADD || (event->eventType == 
CELIX_DM_EVENT_SVC_SET && event->svc != NULL)) {
         //note adding service or setting new service, so cmp will not be 
stopped in handleChange -> use suspend / resume now
@@ -705,41 +743,49 @@ static celix_status_t 
celix_dmComponent_calculateNewState(celix_dm_component_t *
     celix_status_t status = CELIX_SUCCESS;
 
     bool allResolved = 
celix_dmComponent_areAllRequiredServiceDependenciesResolved(component);
-    if (currentState == DM_CMP_STATE_INACTIVE) {
+    if (currentState == CELIX_DM_CMP_STATE_INACTIVE) {
         if (component->isEnabled) {
-            *newState = DM_CMP_STATE_WAITING_FOR_REQUIRED;
+            *newState = CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED;
         } else {
             *newState = currentState;
         }
-    } else if (currentState == DM_CMP_STATE_WAITING_FOR_REQUIRED) {
+    } else if (currentState == CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED) {
         if (!component->isEnabled) {
-            *newState = DM_CMP_STATE_INACTIVE;
+            *newState = CELIX_DM_CMP_STATE_INACTIVE;
         } else {
             if (allResolved) {
-                *newState = DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED;
+                *newState = CELIX_DM_CMP_STATE_INITIALIZING;
             } else {
                 *newState = currentState;
             }
         }
-    } else if (currentState == 
DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
+    } else if (currentState == CELIX_DM_CMP_STATE_INITIALIZING) {
+        *newState = CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED;
+    } else if (currentState == CELIX_DM_CMP_STATE_DEINITIALIZING) {
+        *newState = CELIX_DM_CMP_STATE_INACTIVE;
+    } else if (currentState == 
CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED) {
         if (!component->isEnabled) {
-            *newState = DM_CMP_STATE_WAITING_FOR_REQUIRED;
+            *newState = CELIX_DM_CMP_STATE_DEINITIALIZING;
         } else {
             if (allResolved) {
-                *newState = DM_CMP_STATE_TRACKING_OPTIONAL;
+                *newState = CELIX_DM_CMP_STATE_STARTING;
             } else {
                 *newState = currentState;
             }
         }
-    } else if (currentState == DM_CMP_STATE_TRACKING_OPTIONAL) {
+    } else if (currentState == CELIX_DM_CMP_STATE_STARTING) {
+        *newState = CELIX_DM_CMP_STATE_TRACKING_OPTIONAL;
+    } else if (currentState == CELIX_DM_CMP_STATE_STOPPING) {
+        *newState = CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED;
+    } else if (currentState == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL) {
         if (component->isEnabled && allResolved) {
             *newState = currentState;
         } else {
-            *newState = DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED;
+            *newState = CELIX_DM_CMP_STATE_STOPPING;
         }
     } else {
         //should not reach
-        *newState = DM_CMP_STATE_INACTIVE;
+        *newState = CELIX_DM_CMP_STATE_INACTIVE;
         status = CELIX_BUNDLE_EXCEPTION;
     }
 
@@ -756,6 +802,7 @@ static bool 
celix_dmComponent_performTransition(celix_dm_component_t *component,
     if (currentState == desiredState) {
         return false;
     }
+    component->inTransition = true;
 
     celix_bundleContext_log(component->context,
            CELIX_LOG_LEVEL_TRACE,
@@ -766,13 +813,24 @@ static bool 
celix_dmComponent_performTransition(celix_dm_component_t *component,
            celix_dmComponent_stateToString(desiredState));
 
     celix_status_t status = CELIX_SUCCESS;
-    if (currentState == DM_CMP_STATE_INACTIVE && desiredState == 
DM_CMP_STATE_WAITING_FOR_REQUIRED) {
+    if (currentState == CELIX_DM_CMP_STATE_INACTIVE && desiredState == 
CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED) {
         celix_dmComponent_enableDependencies(component);
-    } else if (currentState == DM_CMP_STATE_WAITING_FOR_REQUIRED && 
desiredState == DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
+    } else if (currentState == CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED && 
desiredState == CELIX_DM_CMP_STATE_INITIALIZING) {
+        //nop
+    } else if (currentState == CELIX_DM_CMP_STATE_INITIALIZING && desiredState 
== CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED) {
         if (component->callbackInit) {
-               status = component->callbackInit(component->implementation);
+            status = component->callbackInit(component->implementation);
         }
-    } else if (currentState == 
DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED && desiredState == 
DM_CMP_STATE_TRACKING_OPTIONAL) {
+    } else if (currentState == 
CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED && desiredState == 
CELIX_DM_CMP_STATE_DEINITIALIZING) {
+        //nop
+    } else if (currentState == CELIX_DM_CMP_STATE_DEINITIALIZING && 
desiredState == CELIX_DM_CMP_STATE_INACTIVE) {
+        if (component->callbackDeinit) {
+            status = component->callbackDeinit(component->implementation);
+        }
+        celix_dmComponent_disableDependencies(component);
+    } else if (currentState == 
CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED && desiredState == 
CELIX_DM_CMP_STATE_STARTING) {
+        //nop
+    } else if (currentState == CELIX_DM_CMP_STATE_STARTING && desiredState == 
CELIX_DM_CMP_STATE_TRACKING_OPTIONAL) {
         if (component->callbackStart) {
                status = component->callbackStart(component->implementation);
         }
@@ -780,16 +838,14 @@ static bool 
celix_dmComponent_performTransition(celix_dm_component_t *component,
             celix_dmComponent_registerServices(component, false);
             component->nrOfTimesStarted += 1;
         }
-    } else if (currentState == DM_CMP_STATE_TRACKING_OPTIONAL && desiredState 
== DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
+    } else if (currentState == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL && 
desiredState == CELIX_DM_CMP_STATE_STOPPING) {
+        //nop
+    } else if (currentState == CELIX_DM_CMP_STATE_STOPPING && desiredState == 
CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED) {
         celix_dmComponent_unregisterServices(component, false);
         if (component->callbackStop) {
                status = component->callbackStop(component->implementation);
         }
-    } else if (currentState == 
DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED && desiredState == 
DM_CMP_STATE_WAITING_FOR_REQUIRED) {
-        if (component->callbackDeinit) {
-            status = component->callbackDeinit(component->implementation);
-        }
-    } else if (currentState == DM_CMP_STATE_WAITING_FOR_REQUIRED && 
desiredState == DM_CMP_STATE_INACTIVE) {
+    } else if (currentState == CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED && 
desiredState == CELIX_DM_CMP_STATE_INACTIVE) {
         celix_dmComponent_disableDependencies(component);
     } else {
         assert(false); //should not be reached.
@@ -809,6 +865,7 @@ static bool 
celix_dmComponent_performTransition(celix_dm_component_t *component,
         celix_dmComponent_disableDirectly(component);
     }
 
+    component->inTransition = false;
     return transition;
 }
 
@@ -964,25 +1021,8 @@ celix_status_t 
celix_dmComponent_getComponentInfo(celix_dm_component_t *componen
     memcpy(info->name, component->name, DM_COMPONENT_MAX_NAME_LENGTH);
     info->nrOfTimesStarted = component->nrOfTimesStarted;
     info->nrOfTimesResumed = component->nrOfTimesResumed;
-
-    switch (component->state) {
-        case DM_CMP_STATE_INACTIVE :
-            info->state = strdup("INACTIVE");
-            break;
-        case DM_CMP_STATE_WAITING_FOR_REQUIRED :
-            info->state = strdup("WAITING_FOR_REQUIRED");
-            break;
-        case DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED :
-            info->state = strdup("INSTANTIATED_AND_WAITING_FOR_REQUIRED");
-            break;
-        case DM_CMP_STATE_TRACKING_OPTIONAL :
-            info->state = strdup("TRACKING_OPTIONAL");
-            info->active = true;
-            break;
-        default :
-            info->state = strdup("UNKNOWN");
-            break;
-    }
+    info->state = 
celix_utils_strdup(celix_dmComponent_stateToString(component->state));
+    info->active = celix_dmComponent_isActiveInternal(component);
 
     for (int i = 0; i < celix_arrayList_size(component->dependencies); i += 1) 
{
         celix_dm_service_dependency_t *dep = 
celix_arrayList_get(component->dependencies, i);
@@ -1027,22 +1067,47 @@ void 
celix_dmComponent_destroyComponentInfo(dm_component_info_pt info) {
     free(info);
 }
 
+static bool celix_dmComponent_isActiveInternal(celix_dm_component_t 
*component) {
+    //precondition: mutex component->mutex taken.
+    return
+            component->state == CELIX_DM_CMP_STATE_STARTING ||
+            component->state == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL ||
+            component->state == CELIX_DM_CMP_STATE_SUSPENDING ||
+            component->state == CELIX_DM_CMP_STATE_SUSPENDED ||
+            component->state == CELIX_DM_CMP_STATE_RESUMING ||
+            component->state == CELIX_DM_CMP_STATE_STOPPING;
+}
+
 bool celix_dmComponent_isActive(celix_dm_component_t *component) {
     celixThreadMutex_lock(&component->mutex);
-    bool active = component->state == DM_CMP_STATE_TRACKING_OPTIONAL;
+    bool isActivate = celix_dmComponent_isActiveInternal(component);
     celixThreadMutex_unlock(&component->mutex);
-    return active;
+    return isActivate;
 }
 
 const char* celix_dmComponent_stateToString(celix_dm_component_state_t state) {
     switch(state) {
-        case DM_CMP_STATE_INACTIVE:
-            return "DM_CMP_STATE_INACTIVE";
-        case DM_CMP_STATE_WAITING_FOR_REQUIRED:
-            return "DM_CMP_STATE_WAITING_FOR_REQUIRED";
-        case DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED:
-            return "DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED";
-        default: //only DM_CMP_STATE_TRACKING_OPTIONAL left
-            return "DM_CMP_STATE_TRACKING_OPTIONAL";
+        case CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED:
+            return "CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED";
+        case CELIX_DM_CMP_STATE_INITIALIZING:
+            return "CELIX_DM_CMP_STATE_INITIALIZING";
+        case CELIX_DM_CMP_STATE_DEINITIALIZING:
+            return "CELIX_DM_CMP_STATE_DEINITIALIZING";
+        case CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED:
+            return "CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED";
+        case CELIX_DM_CMP_STATE_STARTING:
+            return "CELIX_DM_CMP_STATE_STARTING";
+        case CELIX_DM_CMP_STATE_STOPPING:
+            return "CELIX_DM_CMP_STATE_STOPPING";
+        case CELIX_DM_CMP_STATE_TRACKING_OPTIONAL:
+            return "CELIX_DM_CMP_STATE_TRACKING_OPTIONAL";
+        case CELIX_DM_CMP_STATE_SUSPENDING:
+            return "CELIX_DM_CMP_STATE_SUSPENDING";
+        case CELIX_DM_CMP_STATE_SUSPENDED:
+            return "CELIX_DM_CMP_STATE_SUSPENDED";
+        case CELIX_DM_CMP_STATE_RESUMING:
+            return "CELIX_DM_CMP_STATE_RESUMING";
+        default: //only CELIX_DM_CMP_STATE_INACTIVE left
+            return "CELIX_DM_CMP_STATE_INACTIVE";
     }
 }

Reply via email to