| Index: src/third_party/pylib/mozrunner/killableprocess.py
|
| ===================================================================
|
| --- src/third_party/pylib/mozrunner/killableprocess.py (revision 9275)
|
| +++ src/third_party/pylib/mozrunner/killableprocess.py (working copy)
|
| @@ -1,292 +0,0 @@
|
| -# killableprocess - subprocesses which can be reliably killed
|
| -#
|
| -# Parts of this module are copied from the subprocess.py file contained
|
| -# in the Python distribution.
|
| -#
|
| -# Copyright (c) 2003-2004 by Peter Astrand <astrand@lysator.liu.se>
|
| -#
|
| -# Additions and modifications written by Benjamin Smedberg
|
| -# <benjamin@smedbergs.us> are Copyright (c) 2006 by the Mozilla Foundation
|
| -# <http://www.mozilla.org/>
|
| -#
|
| -# More Modifications
|
| -# Copyright (c) 2006-2007 by Mike Taylor <bear@code-bear.com>
|
| -# Copyright (c) 2007-2008 by Mikeal Rogers <mikeal@mozilla.com>
|
| -#
|
| -# By obtaining, using, and/or copying this software and/or its
|
| -# associated documentation, you agree that you have read, understood,
|
| -# and will comply with the following terms and conditions:
|
| -#
|
| -# Permission to use, copy, modify, and distribute this software and
|
| -# its associated documentation for any purpose and without fee is
|
| -# hereby granted, provided that the above copyright notice appears in
|
| -# all copies, and that both that copyright notice and this permission
|
| -# notice appear in supporting documentation, and that the name of the
|
| -# author not be used in advertising or publicity pertaining to
|
| -# distribution of the software without specific, written prior
|
| -# permission.
|
| -#
|
| -# THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
| -# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
|
| -# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
| -# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
| -# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
| -# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
| -# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
| -
|
| -"""killableprocess - Subprocesses which can be reliably killed
|
| -
|
| -This module is a subclass of the builtin "subprocess" module. It allows
|
| -processes that launch subprocesses to be reliably killed on Windows (via the Popen.kill() method.
|
| -
|
| -It also adds a timeout argument to Wait() for a limited period of time before
|
| -forcefully killing the process.
|
| -
|
| -Note: On Windows, this module requires Windows 2000 or higher (no support for
|
| -Windows 95, 98, or NT 4.0). It also requires ctypes, which is bundled with
|
| -Python 2.5+ or available from http://python.net/crew/theller/ctypes/
|
| -"""
|
| -
|
| -import subprocess
|
| -import sys
|
| -import os
|
| -import time
|
| -import datetime
|
| -import types
|
| -import exceptions
|
| -
|
| -try:
|
| - from subprocess import CalledProcessError
|
| -except ImportError:
|
| - # Python 2.4 doesn't implement CalledProcessError
|
| - class CalledProcessError(Exception):
|
| - """This exception is raised when a process run by check_call() returns
|
| - a non-zero exit status. The exit status will be stored in the
|
| - returncode attribute."""
|
| - def __init__(self, returncode, cmd):
|
| - self.returncode = returncode
|
| - self.cmd = cmd
|
| - def __str__(self):
|
| - return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
|
| -
|
| -mswindows = (sys.platform == "win32")
|
| -
|
| -if mswindows:
|
| - import winprocess
|
| -else:
|
| - import signal
|
| -
|
| -def call(*args, **kwargs):
|
| - waitargs = {}
|
| - if "timeout" in kwargs:
|
| - waitargs["timeout"] = kwargs.pop("timeout")
|
| -
|
| - return Popen(*args, **kwargs).wait(**waitargs)
|
| -
|
| -def check_call(*args, **kwargs):
|
| - """Call a program with an optional timeout. If the program has a non-zero
|
| - exit status, raises a CalledProcessError."""
|
| -
|
| - retcode = call(*args, **kwargs)
|
| - if retcode:
|
| - cmd = kwargs.get("args")
|
| - if cmd is None:
|
| - cmd = args[0]
|
| - raise CalledProcessError(retcode, cmd)
|
| -
|
| -if not mswindows:
|
| - def DoNothing(*args):
|
| - pass
|
| -
|
| -class Popen(subprocess.Popen):
|
| - kill_called = False
|
| - if mswindows:
|
| - def _execute_child(self, args, executable, preexec_fn, close_fds,
|
| - cwd, env, universal_newlines, startupinfo,
|
| - creationflags, shell,
|
| - p2cread, p2cwrite,
|
| - c2pread, c2pwrite,
|
| - errread, errwrite):
|
| - if not isinstance(args, types.StringTypes):
|
| - args = subprocess.list2cmdline(args)
|
| -
|
| - # Always or in the create new process group
|
| - creationflags |= winprocess.CREATE_NEW_PROCESS_GROUP
|
| -
|
| - if startupinfo is None:
|
| - startupinfo = winprocess.STARTUPINFO()
|
| -
|
| - if None not in (p2cread, c2pwrite, errwrite):
|
| - startupinfo.dwFlags |= winprocess.STARTF_USESTDHANDLES
|
| -
|
| - startupinfo.hStdInput = int(p2cread)
|
| - startupinfo.hStdOutput = int(c2pwrite)
|
| - startupinfo.hStdError = int(errwrite)
|
| - if shell:
|
| - startupinfo.dwFlags |= winprocess.STARTF_USESHOWWINDOW
|
| - startupinfo.wShowWindow = winprocess.SW_HIDE
|
| - comspec = os.environ.get("COMSPEC", "cmd.exe")
|
| - args = comspec + " /c " + args
|
| -
|
| - # determine if we can create create a job
|
| - canCreateJob = winprocess.CanCreateJobObject()
|
| -
|
| - # set process creation flags
|
| - creationflags |= winprocess.CREATE_SUSPENDED
|
| - creationflags |= winprocess.CREATE_UNICODE_ENVIRONMENT
|
| - if canCreateJob:
|
| - creationflags |= winprocess.CREATE_BREAKAWAY_FROM_JOB
|
| -
|
| - # create the process
|
| - hp, ht, pid, tid = winprocess.CreateProcess(
|
| - executable, args,
|
| - None, None, # No special security
|
| - 1, # Must inherit handles!
|
| - creationflags,
|
| - winprocess.EnvironmentBlock(env),
|
| - cwd, startupinfo)
|
| - self._child_created = True
|
| - self._handle = hp
|
| - self._thread = ht
|
| - self.pid = pid
|
| - self.tid = tid
|
| -
|
| - if canCreateJob:
|
| - # We create a new job for this process, so that we can kill
|
| - # the process and any sub-processes
|
| - self._job = winprocess.CreateJobObject()
|
| - winprocess.AssignProcessToJobObject(self._job, int(hp))
|
| - else:
|
| - self._job = None
|
| -
|
| - winprocess.ResumeThread(int(ht))
|
| - ht.Close()
|
| -
|
| - if p2cread is not None:
|
| - p2cread.Close()
|
| - if c2pwrite is not None:
|
| - c2pwrite.Close()
|
| - if errwrite is not None:
|
| - errwrite.Close()
|
| - time.sleep(.1)
|
| -
|
| - def kill(self, group=True):
|
| - """Kill the process. If group=True, all sub-processes will also be killed."""
|
| - self.kill_called = True
|
| - if mswindows:
|
| - if group and self._job:
|
| - winprocess.TerminateJobObject(self._job, 127)
|
| - else:
|
| - try:
|
| - winprocess.TerminateProcess(self._handle, 127)
|
| - except:
|
| - # TODO: better error handling here
|
| - pass
|
| - self.returncode = 127
|
| - else:
|
| - if group:
|
| - try:
|
| - os.killpg(self.pid, signal.SIGKILL)
|
| - except: pass
|
| - else:
|
| - os.kill(self.pid, signal.SIGKILL)
|
| - self.returncode = -9
|
| -
|
| - def wait(self, timeout=None, group=True):
|
| - """Wait for the process to terminate. Returns returncode attribute.
|
| - If timeout seconds are reached and the process has not terminated,
|
| - it will be forcefully killed. If timeout is -1, wait will not
|
| - time out."""
|
| -
|
| - if timeout is not None:
|
| - # timeout is now in milliseconds
|
| - timeout = timeout * 1000
|
| -
|
| - if self.returncode is not None:
|
| - return self.returncode
|
| -
|
| - starttime = datetime.datetime.now()
|
| -
|
| - if mswindows:
|
| - if timeout is None:
|
| - timeout = -1
|
| - rc = winprocess.WaitForSingleObject(self._handle, timeout)
|
| -
|
| - if rc != winprocess.WAIT_TIMEOUT:
|
| - def check():
|
| - now = datetime.datetime.now()
|
| - diff = now - starttime
|
| - if (diff.seconds * 1000 * 1000 + diff.microseconds) < (timeout * 1000):
|
| - if self._job:
|
| - if (winprocess.QueryInformationJobObject(self._job, 8)['BasicInfo']['ActiveProcesses'] > 0):
|
| - return True
|
| - else:
|
| - return True
|
| - return False
|
| - while check():
|
| - time.sleep(.5)
|
| -
|
| - now = datetime.datetime.now()
|
| - diff = now - starttime
|
| - if (diff.seconds * 1000 * 1000 + diff.microseconds) > (timeout * 1000):
|
| - self.kill(group)
|
| - else:
|
| - self.returncode = winprocess.GetExitCodeProcess(self._handle)
|
| - else:
|
| - if (sys.platform == 'linux2') or (sys.platform in ('sunos5', 'solaris')):
|
| - def group_wait(timeout):
|
| - try:
|
| - os.waitpid(self.pid, 0)
|
| - except OSError, e:
|
| - pass # If wait has already been called on this pid, bad things happen
|
| - return self.returncode
|
| - elif sys.platform == 'darwin':
|
| - def group_wait(timeout):
|
| - try:
|
| - count = 0
|
| - if timeout is None and self.kill_called:
|
| - timeout = 10 # Have to set some kind of timeout or else this could go on forever
|
| - if timeout is None:
|
| - while 1:
|
| - os.killpg(self.pid, signal.SIG_DFL)
|
| - while ((count * 2) <= timeout):
|
| - os.killpg(self.pid, signal.SIG_DFL)
|
| - # count is increased by 500ms for every 0.5s of sleep
|
| - time.sleep(.5); count += 500
|
| - except exceptions.OSError:
|
| - return self.returncode
|
| -
|
| - if timeout is None:
|
| - if group is True:
|
| - return group_wait(timeout)
|
| - else:
|
| - subprocess.Popen.wait(self)
|
| - return self.returncode
|
| -
|
| - returncode = False
|
| -
|
| - now = datetime.datetime.now()
|
| - diff = now - starttime
|
| - while (diff.seconds * 1000 * 1000 + diff.microseconds) < (timeout * 1000) and ( returncode is False ):
|
| - if group is True:
|
| - return group_wait(timeout)
|
| - else:
|
| - if subprocess.poll() is not None:
|
| - returncode = self.returncode
|
| - time.sleep(.5)
|
| - now = datetime.datetime.now()
|
| - diff = now - starttime
|
| - return self.returncode
|
| -
|
| - return self.returncode
|
| - # We get random maxint errors from subprocesses __del__
|
| - __del__ = lambda self: None
|
| -
|
| -def setpgid_preexec_fn():
|
| - os.setpgid(0, 0)
|
| -
|
| -def runCommand(cmd, **kwargs):
|
| - if sys.platform != "win32":
|
| - return Popen(cmd, preexec_fn=setpgid_preexec_fn, **kwargs)
|
| - else:
|
| - return Popen(cmd, **kwargs)
|
|
|