If a service stops and won't restart, release explicit activation.
authorDavin McCall <davmac@davmac.org>
Sat, 17 Jun 2017 23:54:41 +0000 (00:54 +0100)
committerDavin McCall <davmac@davmac.org>
Sat, 17 Jun 2017 23:54:41 +0000 (00:54 +0100)
src/service.cc
src/service.h
src/tests/tests.cc

index 90d5b2e12cd459943bf5441b313c34343d08d722..2d83960289ce3e07fa8b6baf1a306f6b243a0ea5 100644 (file)
@@ -1041,6 +1041,12 @@ void service_record::do_stop() noexcept
 {
     if (pinned_started) return;
 
+    if (start_explicit && ! do_auto_restart()) {
+        start_explicit = false;
+        release();
+        if (required_by == 0) return; // release will re-call us anyway
+    }
+
     if (service_state != service_state_t::STARTED) {
         if (service_state == service_state_t::STARTING) {
             if (! can_interrupt_start()) {
index 8313360614a3911334407e4b83d546edd2fc3b3b..e3f40b113d8a8e6d797893a5437e05327d0df860 100644 (file)
@@ -794,7 +794,7 @@ class service_set
     // Stop the specified service. Its active mark will be cleared.
     void stop_service(service_record *svc)
     {
-        svc->stop();
+        svc->stop(true);
         processQueues();
     }
 
index 12e2095239eecc9eb32890ad394fc3c7d30005ac..d1f350adff16f0385f3874a7691569b76936d299 100644 (file)
@@ -3,6 +3,8 @@
 
 #include "service.h"
 
+// Test 1: starting a service starts dependencies; stopping the service releases and
+// stops dependencies.
 void test1()
 {
     service_set sset;
@@ -33,6 +35,8 @@ void test1()
     assert(s1->getState() == service_state_t::STOPPED);
 }
 
+// Test 2: Multiple dependents will hold a dependency active if one of the dependents is
+// stopped/released.
 void test2()
 {
     service_set sset;
@@ -73,6 +77,66 @@ void test2()
     assert(s1->getState() == service_state_t::STOPPED);
 }
 
+// Test 3: stopping a dependency causes its dependents to stop.
+void test3()
+{
+    service_set sset;
+
+    service_record *s1 = new service_record(&sset, "test-service-1", service_type::INTERNAL, {}, {});
+    service_record *s2 = new service_record(&sset, "test-service-2", service_type::INTERNAL, {s1}, {});
+    service_record *s3 = new service_record(&sset, "test-service-3", service_type::INTERNAL, {s2}, {});
+    sset.add_service(s1);
+    sset.add_service(s2);
+    sset.add_service(s3);
+
+    assert(sset.find_service("test-service-1") == s1);
+    assert(sset.find_service("test-service-2") == s2);
+    assert(sset.find_service("test-service-3") == s3);
+
+    // Start all three services:
+    sset.start_service(s3);
+
+    // Now stop s1, which should also force s2 and s3 to stop:
+    sset.stop_service(s1);
+
+    assert(s3->getState() == service_state_t::STOPPED);
+    assert(s2->getState() == service_state_t::STOPPED);
+    assert(s1->getState() == service_state_t::STOPPED);
+}
+
+// Test 4: an explicitly activated service with automatic restart will restart if it
+// stops due to a dependency stopping, therefore also causing the dependency to restart.
+void test4()
+{
+    service_set sset;
+
+    service_record *s1 = new service_record(&sset, "test-service-1", service_type::INTERNAL, {}, {});
+    service_record *s2 = new service_record(&sset, "test-service-2", service_type::INTERNAL, {s1}, {});
+    service_record *s3 = new service_record(&sset, "test-service-3", service_type::INTERNAL, {s2}, {});
+    s2->setAutoRestart(true);
+    sset.add_service(s1);
+    sset.add_service(s2);
+    sset.add_service(s3);
+
+    assert(sset.find_service("test-service-1") == s1);
+    assert(sset.find_service("test-service-2") == s2);
+    assert(sset.find_service("test-service-3") == s3);
+
+    // Start all three services:
+    sset.start_service(s3);
+
+    // Also explicitly activate s2:
+    sset.start_service(s2);
+
+    // Now stop s1, which should also force s2 and s3 to stop.
+    // s2 (and therefore s1) should restart:
+    sset.stop_service(s1);
+
+    assert(s3->getState() == service_state_t::STOPPED);
+    assert(s2->getState() == service_state_t::STARTED);
+    assert(s1->getState() == service_state_t::STARTED);
+}
+
 int main(int argc, char **argv)
 {
     std::cout << "test1... ";
@@ -82,4 +146,12 @@ int main(int argc, char **argv)
     std::cout << "test2... ";
     test2();
     std::cout << "PASSED" << std::endl;
+
+    std::cout << "test3... ";
+    test3();
+    std::cout << "PASSED" << std::endl;
+
+    std::cout << "test4... ";
+    test4();
+    std::cout << "PASSED" << std::endl;
 }