summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBarry Warsaw2009-08-25 18:49:43 -0400
committerBarry Warsaw2009-08-25 18:49:43 -0400
commit0ce2f82735911c738349b1812c86e09355ecee60 (patch)
treeed3c5432cce22e9edaf5dc4199594d629b1a2952
parent67ee413b62f5e1f9530c74a2dd83d2498f15908d (diff)
downloadmailman-0ce2f82735911c738349b1812c86e09355ecee60.tar.gz
mailman-0ce2f82735911c738349b1812c86e09355ecee60.tar.zst
mailman-0ce2f82735911c738349b1812c86e09355ecee60.zip
-rw-r--r--buildout.cfg3
-rw-r--r--setup.py2
-rw-r--r--src/mailman/bin/arch.py2
-rw-r--r--src/mailman/bin/master.py71
4 files changed, 71 insertions, 7 deletions
diff --git a/buildout.cfg b/buildout.cfg
index dd6708ccc..187a0cdbc 100644
--- a/buildout.cfg
+++ b/buildout.cfg
@@ -45,7 +45,6 @@ eggs = mailman
[pylint]
recipe = zc.recipe.egg
eggs =
- logilab.pylintinstaller
- pylint==0.15.2
+ pylint
entry-points = pylint=pylint.lint:Run
arguments = sys.argv[1:]
diff --git a/setup.py b/setup.py
index 9c3e89c84..4062ea3a6 100644
--- a/setup.py
+++ b/setup.py
@@ -42,6 +42,8 @@ import os
import mailman.commands
import mailman.messages
+# Create the .mo files from the .po files. There may be errors and warnings
+# here and that could cause the digester.txt test to fail.
start_dir = os.path.dirname(mailman.messages.__file__)
for dirpath, dirnames, filenames in os.walk(start_dir):
for filename in filenames:
diff --git a/src/mailman/bin/arch.py b/src/mailman/bin/arch.py
index a27fa8d7f..abf057c3f 100644
--- a/src/mailman/bin/arch.py
+++ b/src/mailman/bin/arch.py
@@ -15,6 +15,8 @@
# You should have received a copy of the GNU General Public License along with
# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
+from __future__ import absolute_import, unicode_literals
+
import os
import sys
import errno
diff --git a/src/mailman/bin/master.py b/src/mailman/bin/master.py
index 829468883..579583c67 100644
--- a/src/mailman/bin/master.py
+++ b/src/mailman/bin/master.py
@@ -220,6 +220,66 @@ Exiting.""")
+class PIDWatcher:
+ """A class which safely manages child process ids."""
+
+ def __init__(self):
+ self._pids = {}
+
+ def __iter__(self):
+ # Safely iterate over all the keys in the dictionary. Because
+ # asynchronous signals are involved, the dictionary's size could
+ # change during iteration. Iterate over a copy of the keys to avoid
+ # that.
+ for pid in self._pids.keys():
+ yield pid
+
+ def add(self, pid, info):
+ """Add process information.
+
+ :param pid: The process id. The watcher must not already be tracking
+ this process id.
+ :type pid: int
+ :param info: The process information.
+ :type info: 4-tuple consisting of
+ (queue-runner-name, slice-number, slice-count, restart-count)
+ """
+ old_info = self._pids.get(pid)
+ assert old_info is None, (
+ 'Duplicate process id {0} with existing info: {1}'.format(
+ pid, old_info))
+ self._pids[pid] = info
+
+ def pop(self, pid):
+ """Remove and return existing process information.
+
+ :param pid: The process id. The watcher must already be tracking this
+ process id.
+ :type pid: int
+ :return: The process information.
+ :rtype: 4-tuple consisting of
+ (queue-runner-name, slice-number, slice-count, restart-count)
+ :raise KeyError: if the process id is not being tracked.
+ """
+ return self._pids.pop(pid)
+
+ def drop(self, pid):
+ """Remove and return existing process information.
+
+ This is like `pop()` except that no `KeyError` is raised if the
+ process id is not being tracked.
+
+ :param pid: The process id.
+ :type pid: int
+ :return: The process information, or None if the process id is not
+ being tracked.
+ :rtype: 4-tuple consisting of
+ (queue-runner-name, slice-number, slice-count, restart-count)
+ """
+ return self._pids.pop(pid, None)
+
+
+
class Loop:
"""Main control loop class."""
@@ -227,7 +287,7 @@ class Loop:
self._lock = lock
self._restartable = restartable
self._config_file = config_file
- self._kids = {}
+ self._kids = PIDWatcher()
def install_signal_handlers(self):
"""Install various signals handlers for control from mailmanctl."""
@@ -336,7 +396,7 @@ class Loop:
pid = self._start_runner(spec)
log = logging.getLogger('mailman.qrunner')
log.debug('[%d] %s', pid, spec)
- self._kids[pid] = info
+ self._kids.add(pid, info)
def loop(self):
"""Main loop.
@@ -394,8 +454,9 @@ qrunner %s reached maximum restart limit of %d, not restarting.""",
# SIGTERM or we aren't restarting.
if restart:
spec = '%s:%d:%d' % (qrname, slice_number, count)
- newpid = self._start_runner(spec)
- self._kids[newpid] = (qrname, slice_number, count, restarts)
+ new_pid = self._start_runner(spec)
+ new_info = (qrname, slice_number, count, restarts)
+ self._kids.add(new_pid, new_info)
def cleanup(self):
"""Ensure that all children have exited."""
@@ -414,7 +475,7 @@ qrunner %s reached maximum restart limit of %d, not restarting.""",
try:
# pylint: disable-msg=W0612
pid, status = os.wait()
- del self._kids[pid]
+ self._kids.drop(pid)
except OSError, e:
if e.errno == errno.ECHILD:
break