# HG changeset patch # User Doug Simon # Date 1399159610 -7200 # Node ID d0e3f6963ed78039b9a847df7cd83f9770fd6584 # Parent d370d87e528fe6fe31ddb53249eb0409dd670e0a mx: made parallel Java builds interact correctly with management of subprocesses upon abort/quit diff -r d370d87e528f -r d0e3f6963ed7 mxtool/mx.py --- a/mxtool/mx.py Sat May 03 18:04:52 2014 +0200 +++ b/mxtool/mx.py Sun May 04 01:26:50 2014 +0200 @@ -1300,8 +1300,21 @@ time.sleep(delay) # Makes the current subprocess accessible to the abort() function -# This is a tuple of the Popen object and args. -_currentSubprocess = (None, None) +# This is a list of tuples of the subprocess.Popen or +# multiprocessing.Process object and args. +_currentSubprocesses = [] + +def _addSubprocess(p, args): + entry = (p, args) + _currentSubprocesses.append(entry) + return entry + +def _removeSubprocess(entry): + if entry and entry in _currentSubprocesses: + try: + _currentSubprocesses.remove(entry) + except: + pass def waitOn(p): if get_os() == 'windows': @@ -1340,8 +1353,7 @@ if timeout is None and _opts.ptimeout != 0: timeout = _opts.ptimeout - global _currentSubprocess - + sub = None try: # On Unix, the new subprocess should be in a separate group so that a timeout alarm # can use os.killpg() to kill the whole subprocess group @@ -1359,7 +1371,7 @@ stdout = out if not callable(out) else subprocess.PIPE stderr = err if not callable(err) else subprocess.PIPE p = subprocess.Popen(args, cwd=cwd, stdout=stdout, stderr=stderr, preexec_fn=preexec_fn, creationflags=creationflags, env=env) - _currentSubprocess = (p, args) + sub = _addSubprocess(p, args) if callable(out): t = Thread(target=redirect, args=(p.stdout, out)) t.daemon = True # thread dies with the program @@ -1382,7 +1394,7 @@ except KeyboardInterrupt: abort(1) finally: - _currentSubprocess = (None, None) + _removeSubprocess(sub) if retcode and nonZeroIsFatal: if _opts.verbose: @@ -1663,21 +1675,20 @@ return result def _send_sigquit(): - p, args = _currentSubprocess - - def _isJava(): - if args: - name = args[0].split(os.sep)[-1] - return name == "java" - return False - - if p is not None and _isJava(): - if get_os() == 'windows': - log("mx: implement me! want to send SIGQUIT to my child process") - else: - _kill_process_group(p.pid, sig=signal.SIGQUIT) - time.sleep(0.1) - + for p, args in _currentSubprocesses: + + def _isJava(): + if args: + name = args[0].split(os.sep)[-1] + return name == "java" + return False + + if p is not None and _isJava(): + if get_os() == 'windows': + log("mx: implement me! want to send SIGQUIT to my child process") + else: + _kill_process_group(p.pid, sig=signal.SIGQUIT) + time.sleep(0.1) def abort(codeOrMessage): """ @@ -1692,12 +1703,14 @@ # import traceback # traceback.print_stack() - p, _ = _currentSubprocess - if p is not None: - if get_os() == 'windows': - p.kill() - else: - _kill_process_group(p.pid, signal.SIGKILL) + for p, args in _currentSubprocesses: + try: + if get_os() == 'windows': + p.terminate() + else: + _kill_process_group(p.pid, signal.SIGKILL) + except BaseException as e: + log('error while killing subprocess {} "{}": {}'.format(p.pid, ' '.join(args), e)) raise SystemExit(codeOrMessage) @@ -2113,6 +2126,7 @@ if t.proc.is_alive(): active.append(t) else: + _removeSubprocess(t.sub) if t.proc.exitcode != 0: return ([], joinTasks(tasks)) return (active, []) @@ -2162,6 +2176,7 @@ task.proc = multiprocessing.Process(target=executeTask, args=(task,)) task.proc.start() active.append(task) + task.sub = _addSubprocess(task.proc, ['JavaCompileTask', str(task)]) if len(active) == cpus: break