Set dependents desired state to STOPPED immediately
authorDavin McCall <davmac@davmac.org>
Mon, 7 Sep 2015 17:12:51 +0000 (18:12 +0100)
committerDavin McCall <davmac@davmac.org>
Mon, 7 Sep 2015 17:12:51 +0000 (18:12 +0100)
when stop of dependency requested.

Previously, if the state was STARTING when the stop
was issued, dependencies were not stopped.

service.cc
service.h

index 047a1b64bcd8f2e99e77480e11a8d64877e245ba..a040deb20f8d0feb534e0e9e574060143950a4ec 100644 (file)
@@ -284,7 +284,7 @@ bool ServiceRecord::start_ps_process(const std::vector<std::string> &pargs)
         // Tokenize the command, and add additional arguments from pargs:
         vector<string> progAndArgs = tokenize(program_name);
         progAndArgs.insert(progAndArgs.end(), pargs.begin(), pargs.end());
-        
+       
         const char * pname = progAndArgs[0].c_str();
         const char ** args = new const char *[progAndArgs.size() + 1];
         
@@ -367,15 +367,8 @@ void ServiceRecord::failed_dependency()
 void ServiceRecord::dependentStopped()
 {
     if (service_state != SVC_STOPPED && (desired_state == SVC_STOPPED || force_stop)) {
-        bool all_deps_stopped = true;
-        for (sr_iter i = dependents.begin(); i != dependents.end(); ++i) {
-            if ((*i)->service_state != SVC_STOPPED) {
-                all_deps_stopped = false;
-                break;
-            }
-        }
-        
-        if (all_deps_stopped) {
+        // Check the other dependents before we stop.
+        if (stopCheckDependents()) {
             stopping();
         }
     }
@@ -390,33 +383,58 @@ void ServiceRecord::stop()
         // TODO inform listeners waiting for start of cancellation
     }
     
+    if (desired_state == SVC_STOPPED) return;
+    
     desired_state = SVC_STOPPED;
 
     if (service_state != SVC_STARTED) {
+        if (service_state == SVC_STARTING) {
+            // Well this is awkward: we're going to have to continue
+            // starting, but we don't want any dependents to think that
+            // they are still waiting to start.
+            // Make sure they remain stopped:
+            stopDependents();
+        }
+        
         // If we're starting we need to wait for that to complete.
         // If we're already stopping/stopped there's nothing to do.
         return;
     }
-
-    // Make sure all dependents have stopped.
     
+    // If we get here, we are in STARTED state; stop all dependents.
+    if (stopCheckDependents()) {
+        stopping();
+    }
+}
+
+bool ServiceRecord::stopCheckDependents()
+{
     bool all_deps_stopped = true;
     for (sr_iter i = dependents.begin(); i != dependents.end(); ++i) {
         if ((*i)->service_state != SVC_STOPPED) {
             all_deps_stopped = false;
-            (*i)->stop();
+            break;
         }
     }
     
-    if (! all_deps_stopped) {
-        // The dependents will notify this service once they've stopped.
-        return;
+    return all_deps_stopped;
+}
+
+bool ServiceRecord::stopDependents()
+{
+    bool all_deps_stopped = true;
+    for (sr_iter i = dependents.begin(); i != dependents.end(); ++i) {
+        if ((*i)->service_state != SVC_STOPPED) {
+            all_deps_stopped = false;
+            (*i)->stop();
+        }
     }
     
-    // Ok, dependents have stopped. We can stop ourselves.
-    stopping();
+    return all_deps_stopped;
 }
 
+
+
 // Dependency stopped or is stopping; we must stop too.
 void ServiceRecord::stopping()
 {
index 011d24b70c3cea35cccbee8030598cc62abb0f6a..5dea8a8c78713f1539517c31ad6cea330d5c3597 100644 (file)
--- a/service.h
+++ b/service.h
@@ -3,12 +3,33 @@
 #include <vector>
 #include "ev.h"
 
-/* Possible service states */
+/*
+ * Possible service states
+ *
+ * Services have both a current state and a desired state. The desired state can be
+ * either SVC_STARTED or SVC_STOPPED. The current state can also be SVC_STARTING
+ * or SVC_STOPPING.
+ *
+ * The total state is a combination of the two, current and desired:
+ * SVC_STOPPED/SVC_STOPPED  : stopped and will remain stopped
+ * SVC_STOPPED/SVC_STARTED  : stopped and will be started; waiting for dependencies to start.
+ * SVC_STARTING/SVC_STARTED : starting, but not yet started. All dependencies have started already.
+ * SVC_STARTING/SVC_STOPPED : as above, but the service will be stopped again as soon as it has
+ *                            completed startup.
+ * SVC_STARTED/SVC_STARTED  : running and will continue running.
+ * SVC_STARTED/SVC_STOPPED  : running but will stop; waiting for dependents to stop.
+ * SVC_STOPPING/SVC_STOPPED : stopping and will stop. All dependents have stopped.
+ * SVC_STOPPING/SVC_STARTED : as above, but the service will be re-started again once it stops.
+ *
+ * A scripted service is in the STARTING/STOPPING states during the script execution.
+ * A process service is in the STOPPING state when it has been signalled to stop (and is never
+ *       in the STARTING state; it moves directly from STOPPED to STARTED).
+ */
 // TODO can we use typesafe enum?
 constexpr static int SVC_STOPPED  = 0;  // service is not running
-constexpr static int SVC_STARTING = 1;  // service script is running with "start"
-constexpr static int SVC_STARTED  = 2;  // service is running; start script finished.
-constexpr static int SVC_STOPPING = 3;  // service script is running with "stop"
+constexpr static int SVC_STARTING = 1;  // service is starting, and will start (or fail to start) in time. All dependencies have started.
+constexpr static int SVC_STARTED  = 2;  // service is running
+constexpr static int SVC_STOPPING = 3;  // service script is stopping and will stop.
 
 
 /* Service types */
@@ -123,7 +144,14 @@ class ServiceRecord
     static void process_child_callback(struct ev_loop *loop, struct ev_child *w,
             int revents);
     
-    void dependentStopped(); // called when a dependent stopped
+    // A dependent has reached STOPPED state
+    void dependentStopped();
+
+    // check if all dependents have stopped
+    bool stopCheckDependents();
+    
+    // issue a stop to all dependents, return true if they are all already stopped
+    bool stopDependents();
     
     void forceStop(); // force-stop this service and all dependents