Catch exceptions in project list generator

If the generator that produces per-project worker arguments raises an
exception it triggers python bug http://bugs.python.org/issue8296.
Rewrite the generator expression as a generator function, and catch
Exceptions and KeyboardInterrupts to end the iteration.

Also add a pool worker initializer to disable SIGINT to prevent
KeyboardInterrupts inside multiprocessing.Pool in the worker threads
causing the same problem.

Fixes easy-to-reproduce hangs when hitting ctrl-c during
repo forall -c echo

Change-Id: Ie4a65b3e1e07a64ed6bb6ff20f3912c4326718ca
This commit is contained in:
Colin Cross 2015-05-13 00:04:36 -07:00
parent 35de228f33
commit 31a7be561e

View File

@ -20,6 +20,7 @@ import multiprocessing
import re import re
import os import os
import select import select
import signal
import sys import sys
import subprocess import subprocess
@ -207,14 +208,12 @@ without iterating through the remaining projects.
os.environ['REPO_COUNT'] = str(len(projects)) os.environ['REPO_COUNT'] = str(len(projects))
pool = multiprocessing.Pool(opt.jobs) pool = multiprocessing.Pool(opt.jobs, InitWorker)
try: try:
config = self.manifest.manifestProject.config config = self.manifest.manifestProject.config
results_it = pool.imap( results_it = pool.imap(
DoWorkWrapper, DoWorkWrapper,
([mirror, opt, cmd, shell, cnt, config, self._SerializeProject(p)] self.ProjectArgs(projects, mirror, opt, cmd, shell, config))
for cnt, p in enumerate(projects))
)
pool.close() pool.close()
for r in results_it: for r in results_it:
rc = rc or r rc = rc or r
@ -236,12 +235,28 @@ without iterating through the remaining projects.
if rc != 0: if rc != 0:
sys.exit(rc) sys.exit(rc)
def ProjectArgs(self, projects, mirror, opt, cmd, shell, config):
for cnt, p in enumerate(projects):
try:
project = self._SerializeProject(p)
except Exception as e:
print('Project list error: %r' % e,
file=sys.stderr)
return
except KeyboardInterrupt:
print('Project list interrupted',
file=sys.stderr)
return
yield [mirror, opt, cmd, shell, cnt, config, project]
class WorkerKeyboardInterrupt(Exception): class WorkerKeyboardInterrupt(Exception):
""" Keyboard interrupt exception for worker processes. """ """ Keyboard interrupt exception for worker processes. """
pass pass
def InitWorker():
signal.signal(signal.SIGINT, signal.SIG_IGN)
def DoWorkWrapper(args): def DoWorkWrapper(args):
""" A wrapper around the DoWork() method. """ A wrapper around the DoWork() method.