PATCH: Making process handling more robust
Mark Mitchell
mark at codesourcery.com
Thu Aug 14 10:06:45 UTC 2003
This patch improves the handling of tests that spawn new processes.
In particular, orphaned grandchildren are now killed when the child
process exits.
--
Mark Mitchell
CodeSourcery, LLC
mark at codesourcery.com
2003-08-14 Mark Mitchell <mark at codesourcery.com>
* qm/executable.py (TimeoutExecutable.__init__): Document -2 value
for timeout.
(TimeoutExecutable._InitializeChild): Create the monitor pid in
the -2 case as well.
(TimeoutExectuable.Run): Kill the entire process group, not just
the monitor pid.
(TimeoutExectuable.__UseSeparateProcessGroupForChild): New
method.
(Filter.__init__): Adjust documentation of timeout parameter.
* qm/test/classes/command.py (ExecTestBase.RunProgram): Pass -2 to
Filter when no timeout is specified.
Index: qm/executable.py
===================================================================
RCS file: /home/sc/Repository/qm/qm/executable.py,v
retrieving revision 1.12
diff -c -5 -p -r1.12 executable.py
*** qm/executable.py 14 Aug 2003 02:24:40 -0000 1.12
--- qm/executable.py 14 Aug 2003 09:59:48 -0000
*************** class TimeoutExecutable(Executable):
*** 383,422 ****
def __init__(self, timeout = -1):
"""Construct a new 'TimeoutExecutable'.
'timeout' -- The number of seconds that the child is permitted
to run. This value may be a floating-point value. However,
! the value may be rounded to an integral value on some
! systems. If the 'timeout' is negative, this class behaves
! like 'Executable'."""
super(TimeoutExecutable, self).__init__()
# This functionality is not yet supported under Windows.
! if timeout >= 0:
assert sys.platform != "win32"
- self.__timeout = timeout
-
def _InitializeChild(self):
# Put the child into its own process group. This step is
# performed in both the parent and the child; therefore both
# processes can safely assume that the creation of the process
# group has taken place.
! if self.__timeout >= 0:
os.setpgid(0, 0)
super(TimeoutExecutable, self)._InitializeChild()
def _HandleChild(self):
super(TimeoutExecutable, self)._HandleChild()
! if self.__timeout >= 0:
# Put the child into its own process group. This step is
# performed in both the parent and the child; therefore both
# processes can safely assume that the creation of the process
# group has taken place.
child_pid = self._GetChildPID()
--- 383,428 ----
def __init__(self, timeout = -1):
"""Construct a new 'TimeoutExecutable'.
'timeout' -- The number of seconds that the child is permitted
to run. This value may be a floating-point value. However,
! the value may be rounded to an integral value on some systems.
! Once the timeout expires, the child and its entire process
! group is killed. (The processes in the process group are sent
! the 'SIGKILL' signal.) If the 'timeout' is -2, the child is
! allowed to run forever, but when it terminates the child's
! process group is killed.
!
! If the 'timeout' is -1, this class behaves exactly like
! 'Executable'."""
super(TimeoutExecutable, self).__init__()
+ self.__timeout = timeout
+
# This functionality is not yet supported under Windows.
! if self.__UseSeparateProcessGroupForChild():
assert sys.platform != "win32"
def _InitializeChild(self):
# Put the child into its own process group. This step is
# performed in both the parent and the child; therefore both
# processes can safely assume that the creation of the process
# group has taken place.
! if self.__UseSeparateProcessGroupForChild():
os.setpgid(0, 0)
super(TimeoutExecutable, self)._InitializeChild()
def _HandleChild(self):
super(TimeoutExecutable, self)._HandleChild()
! if self.__UseSeparateProcessGroupForChild():
# Put the child into its own process group. This step is
# performed in both the parent and the child; therefore both
# processes can safely assume that the creation of the process
# group has taken place.
child_pid = self._GetChildPID()
*************** class TimeoutExecutable(Executable):
*** 450,463 ****
# Put the monitoring process into the child's process
# group. We know the process group still exists at this
# point because either (a) we are in the process
# group, or (b) the parent has not yet called waitpid.
os.setpgid(0, child_pid)
! # Give the child time to run.
! time.sleep (self.__timeout)
! # Kill all processes in the child process group.
! os.kill(0, signal.SIGKILL)
finally:
# Exit. This code is in a finally clause so that
# we are guaranteed to get here no matter what.
os._exit(0)
--- 456,473 ----
# Put the monitoring process into the child's process
# group. We know the process group still exists at this
# point because either (a) we are in the process
# group, or (b) the parent has not yet called waitpid.
os.setpgid(0, child_pid)
! if self.__timeout >= 0:
! # Give the child time to run.
! time.sleep (self.__timeout)
! # Kill all processes in the child process group.
! os.kill(0, signal.SIGKILL)
! else:
! # This call to select will never terminate.
! select.select ([], [], [])
finally:
# Exit. This code is in a finally clause so that
# we are guaranteed to get here no matter what.
os._exit(0)
*************** class TimeoutExecutable(Executable):
*** 471,487 ****
environment,
dir,
path)
finally:
# Clean up the monitoring program; it is no longer needed.
! if self.__timeout >= 0:
! os.kill(self.__monitor_pid, signal.SIGKILL)
os.waitpid(self.__monitor_pid, 0)
return status
class RedirectedExecutable(TimeoutExecutable):
"""A 'RedirectedExecutable' redirects the standard I/O streams."""
def _InitializeParent(self):
--- 481,507 ----
environment,
dir,
path)
finally:
# Clean up the monitoring program; it is no longer needed.
! if self.__UseSeparateProcessGroupForChild():
! os.kill(-self._GetChildPID(), signal.SIGKILL)
os.waitpid(self.__monitor_pid, 0)
return status
+ def __UseSeparateProcessGroupForChild(self):
+ """Returns true if the child wil be placed in its own process group.
+
+ returns -- True if the child wil be placed in its own process
+ group. In that case, a separate monitoring process will also
+ be created."""
+
+ return self.__timeout >= 0 or self.__timeout == -2
+
+
class RedirectedExecutable(TimeoutExecutable):
"""A 'RedirectedExecutable' redirects the standard I/O streams."""
def _InitializeParent(self):
*************** class Filter(RedirectedExecutable):
*** 832,843 ****
"""Create a new 'Filter'.
'input' -- The string containing the input to provide to the
child process.
! 'timeout' -- If non-negative, the number of seconds to wait
! for the child to complete its processing."""
super(Filter, self).__init__(timeout)
self.__input = input
self.__next = 0
--- 852,862 ----
"""Create a new 'Filter'.
'input' -- The string containing the input to provide to the
child process.
! 'timeout' -- As for 'TimeoutExecutable.__init__'."""
super(Filter, self).__init__(timeout)
self.__input = input
self.__next = 0
Index: qm/test/classes/command.py
===================================================================
RCS file: /home/sc/Repository/qm/qm/test/classes/command.py,v
retrieving revision 1.39
diff -c -5 -p -r1.39 command.py
*** qm/test/classes/command.py 11 Aug 2003 23:33:58 -0000 1.39
--- qm/test/classes/command.py 14 Aug 2003 09:59:48 -0000
*************** class ExecTestBase(Test):
*** 174,184 ****
'Result.PASS' or to add annotations."""
# Construct the environment.
environment = self.MakeEnvironment(context)
# Create the executable.
! e = qm.executable.Filter(self.stdin, self.timeout)
# Run it.
exit_status = e.Run(arguments, environment, path = program)
# If the process terminated normally, check the outputs.
if sys.platform == "win32" or os.WIFEXITED(exit_status):
--- 174,193 ----
'Result.PASS' or to add annotations."""
# Construct the environment.
environment = self.MakeEnvironment(context)
# Create the executable.
! if self.timeout >= 0:
! timeout = self.timeout
! else:
! # If no timeout was specified, we sill run this process in a
! # separate process group and kill the entire process group
! # when the child is done executing. That means that
! # orphaned child processes created by the test will be
! # cleaned up.
! timeout = -2
! e = qm.executable.Filter(self.stdin, timeout)
# Run it.
exit_status = e.Run(arguments, environment, path = program)
# If the process terminated normally, check the outputs.
if sys.platform == "win32" or os.WIFEXITED(exit_status):
More information about the qmtest
mailing list