diff options
| author | bwarsaw | 2001-07-08 04:18:24 +0000 |
|---|---|---|
| committer | bwarsaw | 2001-07-08 04:18:24 +0000 |
| commit | 141105e73478a43d5739f162f6248b8f4dab4876 (patch) | |
| tree | e63d277126b68b4abfa977f9f4382d08755e27d3 | |
| parent | 61d7788a3952f9e3fda73002523d137cab5bb0bb (diff) | |
| download | mailman-141105e73478a43d5739f162f6248b8f4dab4876.tar.gz mailman-141105e73478a43d5739f162f6248b8f4dab4876.tar.zst mailman-141105e73478a43d5739f162f6248b8f4dab4876.zip | |
Cleaner startup/restart/shutdown implementation. This allows the
sub-qrunners to cleanly exit themselves instead of potentially
stopping them in the middle of processing a message. Specifically,
sighup_handler(): Send SIGHUPs to the child processes instead of
SIGINTs.
start_lock_refresher(): Install our own SIGHUP handler, which simply
toggles off an instance-based loop flag. The signal kicks us out of
the sleep, so the next while test will return false and break us out
of the loop, existing the lock refresher. No need to deal with
KeyboardInterrupts.
start_runner(): In the parent, just return the pid of the child. In
the child, do the syslogging, and also install our own process's
SIGHUP handler, which will call the qrunner's stop() method. This
lets the qrunner break out of its loop at the next convenient place
(i.e. after processing one of its files). Again, no need to deal with
KeyboardInterrupts.
master(): More accurate syslog messages. Also, make sure the children
receive SIGHUP instead of SIGINT.
| -rw-r--r-- | Mailman/Queue/Control.py | 78 |
1 files changed, 48 insertions, 30 deletions
diff --git a/Mailman/Queue/Control.py b/Mailman/Queue/Control.py index 6651ea94b..ddb75d80c 100644 --- a/Mailman/Queue/Control.py +++ b/Mailman/Queue/Control.py @@ -45,13 +45,13 @@ KIDS = {} # cause a new StampedLogger to be opened the next time a message is logged. def sighup_handler(signum, frame): syslog.close() - # SIGINT all the children so that they'll restart automatically. Don't + # SIGHUP all the children so that they'll restart automatically. Don't # kill the process group because we don't want ourselves to get the # signal! for pid in KIDS.keys(): - os.kill(pid, signal.SIGINT) + os.kill(pid, signal.SIGHUP) # And just to tweak things... - syslog('qrunner', 'qrunner caught SIGHUP. Re-opening log files.') + syslog('qrunner', 'Master qrunner caught SIGHUP. Re-opening log files.') @@ -61,13 +61,27 @@ def start_lock_refresher(lock): if pid: # parent return pid - # In the child, we simply wake up once per day and refresh the lock - try: - while 1: - time.sleep(SNOOZE) - lock.refresh() - except KeyboardInterrupt: - pass + # In the child, we simply wake up once per day and refresh the lock. + # Install a SIGHUP handler to break out of the loop cleanly. + class Loop: + def __init__(self): + self._stop = 0 + def stop(self): + self._stop = 1 + def continue_p(self): + return not self._stop + + loop = Loop() + def sighup_handler(signum, frame, loop=loop): + syslog('qrunner', 'Lock refresher caught SIGHUP. Stopping.') + loop.stop() + # Enable the lock refresher's SIGHUP handler + signal.signal(signal.SIGHUP, sighup_handler) + syslog('qrunner', 'Lock refresher started.') + while loop.continue_p(): + time.sleep(SNOOZE) + lock.refresh() + syslog('qrunner', 'Lock refresher exited.') os._exit(0) @@ -76,24 +90,27 @@ def start_runner(qrclass, slice, count): pid = os.fork() if pid: # parent - syslog('qrunner', '%s qrunner started with pid: %d', qrclass, pid) return pid else: # child - try: - qrunner = qrclass(slice, count).run() - syslog('qrunner', '%s qrunner exiting', qrclass) - except KeyboardInterrupt: - # Due to race conditions, the subproc could get the SIGINT inside - # the syslog call. It's of no consequence. - pass + qrunner = qrclass(slice, count) + def sighup_handler(signum, frame, qrunner=qrunner): + # Exit the qrunner cleanly. + qrunner.stop() + syslog('qrunner', '%s qrunner caught SIGHUP. Stopping.' % + qrunner.__class__.__name__) + # Enable the child's SIGHUP handler + signal.signal(signal.SIGHUP, sighup_handler) + syslog('qrunner', '%s qrunner started.', qrclass.__name__) + qrunner.run() + syslog('qrunner', '%s qrunner exiting.', qrclass.__name__) os._exit(0) def master(restart, lock): # Start up the lock refresher process - watchdog_pid = start_lock_refresher(lock) + refresher_pid = start_lock_refresher(lock) # Start up all the qrunners for classname, count in mm_cfg.QRUNNERS: modulename = 'Mailman.Queue.' + classname @@ -120,20 +137,20 @@ def master(restart, lock): killsig = status & 0xff exitstatus = (status >> 8) & 0xff # What should we do with this information other than log it? - if pid == watchdog_pid: + if pid == refresher_pid: syslog('qrunner', '''\ -qrunner watchdog detected lock refresher exit +Master qrunner detected lock refresher exit (pid: %d, sig: %d, sts: %d) %s''', pid, killsig, exitstatus, restarting) if restart: - watchdog_pid = start_lock_refresher(lock) + refresher_pid = start_lock_refresher(lock) else: qrclass, slice, count = KIDS[pid] syslog('qrunner', '''\ -qrunner watchdog detected subprocess exit - (pid: %d, sig: %d, sts: %d, class: %s, slice %d of %d) %s''', - pid, killsig, exitstatus, qrclass, slice, count, - restarting) +Master qrunner detected subprocess exit + (pid: %d, sig: %d, sts: %d, class: %s, slice: %d/%d) %s''', + pid, killsig, exitstatus, qrclass.__name__, + slice+1, count, restarting) del KIDS[pid] # Now perhaps restart the process if restart: @@ -143,12 +160,13 @@ qrunner watchdog detected subprocess exit pass finally: # Should we leave the main loop for any reason, we want to be sure all - # of our children are exited cleanly. Send SIGINTs to all the child - # processes and wait for them all to exit. Include the watchdog! - KIDS[watchdog_pid] = watchdog_pid + # of our children are exited cleanly. Send SIGHUPs to all the child + # processes and wait for them all to exit. Include the lock + # refresher. + KIDS[refresher_pid] = refresher_pid for pid in KIDS.keys(): try: - os.kill(pid, signal.SIGINT) + os.kill(pid, signal.SIGHUP) except OSError, e: if e.errno == errno.ESRCH: # The child has already exited |
