[Zodb-checkins] CVS: ZODB3/zdaemon - zdaemon.py:1.12

Guido van Rossum guido@python.org
Tue, 12 Nov 2002 12:34:08 -0500


Update of /cvs-repository/ZODB3/zdaemon
In directory cvs.zope.org:/tmp/cvs-serv29219

Modified Files:
	zdaemon.py 
Log Message:
Implement the governor without a sleep.
Align help output properly.


=== ZODB3/zdaemon/zdaemon.py 1.11 => 1.12 ===
--- ZODB3/zdaemon/zdaemon.py:1.11	Tue Nov 12 11:33:13 2002
+++ ZODB3/zdaemon/zdaemon.py	Tue Nov 12 12:34:08 2002
@@ -57,11 +57,15 @@
 """
 XXX TO DO
 
-- Do the governor without actual sleeps, using event scheduling etc.
+- The "stop" command should first send a SIGTERM, then sleep
+  backofflimit seconds, then send SIGKILL, and keep repeating SIGKILL
+  until it actually dies.
 
 - True OO design -- use multiple classes rather than folding
   everything into one class.
 
+- Add unit tests.
+
 - Add docstrings.
 
 """
@@ -307,12 +311,13 @@
         os.setsid()
 
     mood = 1 # 1: up, 0: down, -1: suicidal
+    delay = 0 # If nonzero, delay starting until this time
     appid = 0 # Application pid; indicates status (0 == not running)
 
     def runforever(self):
         self.info("daemon manager started")
         while self.mood >= 0 or self.appid:
-            if self.mood > 0 and not self.appid:
+            if self.mood > 0 and not self.appid and not self.delay:
                 self.forkandexec()
             if self.waitstatus:
                 self.reportstatus()
@@ -320,6 +325,10 @@
             if self.commandsocket:
                 r.append(self.commandsocket)
             timeout = self.backofflimit
+            if self.delay:
+                timeout = max(0, min(timeout, self.delay - time.time()))
+                if timeout <= 0:
+                    self.delay = 0
             try:
                 r, w, x = select.select(r, w, x, timeout)
             except select.error, err:
@@ -386,6 +395,8 @@
 
     def cmd_start(self, args):
         self.mood = 1 # Up
+        self.backoff = 0
+        self.delay = 0
         if not self.appid:
             self.forkandexec()
             self.sendreply("Application started")
@@ -394,6 +405,8 @@
 
     def cmd_stop(self, args):
         self.mood = 0 # Down
+        self.backoff = 0
+        self.delay = 0
         if self.appid:
             os.kill(self.appid, signal.SIGTERM)
             self.sendreply("Sent SIGTERM")
@@ -402,6 +415,8 @@
 
     def cmd_restart(self, args):
         self.mood = 1 # Up
+        self.backoff = 0
+        self.delay = 0
         if self.appid:
             os.kill(self.appid, signal.SIGTERM)
             self.sendreply("Sent SIGTERM; will restart later")
@@ -411,6 +426,8 @@
 
     def cmd_exit(self, args):
         self.mood = -1 # Suicidal
+        self.backoff = 0
+        self.delay = 0
         if self.appid:
             os.kill(self.appid, signal.SIGTERM)
             self.sendreply("Sent SIGTERM; will exit later")
@@ -444,8 +461,14 @@
         else:
             status = "running"
         self.sendreply("status=%s\n" % status +
-                       "manager=%d\n" % os.getpid() + 
-                       "application=%d\n" % self.appid +
+                       "now=%r\n" % time.time() +
+                       "mood=%d\n" % self.mood +
+                       "delay=%r\n" % self.delay +
+                       "backoff=%r\n" % self.backoff +
+                       "lasttime=%r\n" % self.lasttime +
+                       "application=%r\n" % self.appid +
+                       "manager=%r\n" % os.getpid() + 
+                       "backofflimit=%r\n" % self.backofflimit +
                        "filename=%r\n" % self.filename +
                        "args=%r\n" % self.args)
 
@@ -456,11 +479,11 @@
             "  status -- report application status (default command)\n"
             "  kill [signal] -- send a signal to the application\n"
             "                   (default signal is SIGTERM)\n"
-            "start -- start the application if not already running\n"
-            "stop -- stop the application if running\n"
-            "        (the daemon manager keeps running)\n"
-            "restart -- stop followed by start\n"
-            "exit -- stop the application and exit\n"
+            "  start -- start the application if not already running\n"
+            "  stop -- stop the application if running\n"
+            "          (the daemon manager keeps running)\n"
+            "  restart -- stop followed by start\n"
+            "  exit -- stop the application and exit\n"
             )
 
     def sendreply(self, msg):
@@ -482,9 +505,10 @@
 
     def governor(self):
         # Back off if respawning too often
+        now = time.time()
         if not self.lasttime:
             pass
-        elif time.time() - self.lasttime < self.backofflimit:
+        elif now - self.lasttime < self.backofflimit:
             # Exited rather quickly; slow down the restarts
             self.backoff += 1
             if self.backoff >= self.backofflimit:
@@ -494,14 +518,14 @@
                     self.error("restarting too often; quit")
                     self.exit(1)
             self.info("sleep %s to avoid rapid restarts" % self.backoff)
-            time.sleep(self.backoff)
+            self.delay = now + self.backoff
         else:
             # Reset the backoff timer
             self.backoff = 0
-        self.lasttime = time.time()
+            self.delay = 0
 
     def forkandexec(self):
-        self.governor()
+        self.lasttime = time.time()
         pid = os.fork()
         if pid != 0:
             # Parent
@@ -530,6 +554,7 @@
         self.waitstatus = None
         if pid == self.appid:
             self.appid = 0
+            self.governor()
         if os.WIFEXITED(sts):
             es = os.WEXITSTATUS(sts)
             msg = "pid %d: exit status %s" % (pid, es)