Patch #1309579: wait3 and wait4 were added to the posix module by Chad J. Schroeder.

This was a fair amount of rework of the patch.  Refactored test_fork1 so it
could be reused by the new tests for wait3/4.  Also made them into new style
unittests (derive from unittest.TestCase).
This commit is contained in:
Neal Norwitz 2006-03-20 06:30:08 +00:00
parent 910b5eec07
commit 05a45599d7
11 changed files with 307 additions and 67 deletions

View File

@ -1731,6 +1731,27 @@ The \function{spawn()} functions called with \constant{P_NOWAIT}
return suitable process handles.
\end{funcdesc}
\begin{funcdesc}{wait3}{\{optional{options}}
Similar to \function{waitpid()}, except no process id argument is given and
a 3-element tuple containing the child's process id, exit status indication,
and resource usage information is returned. Refer to
\module{resource}.\function{getrusage()}
for details on resource usage information. The option argument is the same
as that provided to \function{waitpid()} and \function{wait4()}.
Availability: \UNIX.
\versionadded{2.5}
\end{funcdesc}
\begin{funcdesc}{wait4}{pid, options}
Similar to \function{waitpid()}, except a 3-element tuple, containing the
child's process id, exit status indication, and resource usage information
is returned. Refer to \module{resource}.\function{getrusage()} for details
on resource usage information. The arguments to \function{wait4()} are
the same as those provided to \function{waitpid()}.
Availability: \UNIX.
\versionadded{2.5}
\end{funcdesc}
\begin{datadesc}{WNOHANG}
The option for \function{waitpid()} to return immediately if no child
process status is available immediately. The function returns

71
Lib/test/fork_wait.py Normal file
View File

@ -0,0 +1,71 @@
"""This test case provides support for checking forking and wait behavior.
To test different wait behavior, overrise the wait_impl method.
We want fork1() semantics -- only the forking thread survives in the
child after a fork().
On some systems (e.g. Solaris without posix threads) we find that all
active threads survive in the child after a fork(); this is an error.
While BeOS doesn't officially support fork and native threading in
the same application, the present example should work just fine. DC
"""
import os, sys, time, thread, unittest
from test.test_support import TestSkipped
LONGSLEEP = 2
SHORTSLEEP = 0.5
NUM_THREADS = 4
class ForkWait(unittest.TestCase):
def setUp(self):
self.alive = {}
self.stop = 0
def f(self, id):
while not self.stop:
self.alive[id] = os.getpid()
try:
time.sleep(SHORTSLEEP)
except IOError:
pass
def wait_impl(self, cpid):
spid, status = os.waitpid(cpid, 0)
self.assertEquals(spid, cpid)
self.assertEquals(status, 0, "cause = %d, exit = %d" % (status&0xff, status>>8))
def test_wait(self):
for i in range(NUM_THREADS):
thread.start_new(self.f, (i,))
time.sleep(LONGSLEEP)
a = self.alive.keys()
a.sort()
self.assertEquals(a, range(NUM_THREADS))
prefork_lives = self.alive.copy()
if sys.platform in ['unixware7']:
cpid = os.fork1()
else:
cpid = os.fork()
if cpid == 0:
# Child
time.sleep(LONGSLEEP)
n = 0
for key in self.alive:
if self.alive[key] != prefork_lives[key]:
n += 1
os._exit(n)
else:
# Parent
self.wait_impl(cpid)
# Tell threads to die
self.stop = 1
time.sleep(2*SHORTSLEEP) # Wait for threads to die

View File

@ -1,75 +1,23 @@
"""This test checks for correct fork() behavior.
We want fork1() semantics -- only the forking thread survives in the
child after a fork().
On some systems (e.g. Solaris without posix threads) we find that all
active threads survive in the child after a fork(); this is an error.
While BeOS doesn't officially support fork and native threading in
the same application, the present example should work just fine. DC
"""
import os, sys, time, thread
from test.test_support import verify, verbose, TestSkipped
import os
from test.fork_wait import ForkWait
from test.test_support import TestSkipped, run_unittest
try:
os.fork
except AttributeError:
raise TestSkipped, "os.fork not defined -- skipping test_fork1"
LONGSLEEP = 2
SHORTSLEEP = 0.5
NUM_THREADS = 4
alive = {}
stop = 0
def f(id):
while not stop:
alive[id] = os.getpid()
try:
time.sleep(SHORTSLEEP)
except IOError:
pass
def main():
for i in range(NUM_THREADS):
thread.start_new(f, (i,))
time.sleep(LONGSLEEP)
a = alive.keys()
a.sort()
verify(a == range(NUM_THREADS))
prefork_lives = alive.copy()
if sys.platform in ['unixware7']:
cpid = os.fork1()
else:
cpid = os.fork()
if cpid == 0:
# Child
time.sleep(LONGSLEEP)
n = 0
for key in alive.keys():
if alive[key] != prefork_lives[key]:
n = n+1
os._exit(n)
else:
# Parent
class ForkTest(ForkWait):
def wait_impl(self, cpid):
spid, status = os.waitpid(cpid, 0)
verify(spid == cpid)
verify(status == 0,
"cause = %d, exit = %d" % (status&0xff, status>>8) )
global stop
# Tell threads to die
stop = 1
time.sleep(2*SHORTSLEEP) # Wait for threads to die
self.assertEqual(spid, cpid)
self.assertEqual(status, 0, "cause = %d, exit = %d" % (status&0xff, status>>8))
main()
def test_main():
run_unittest(ForkTest)
if __name__ == "__main__":
test_main()

32
Lib/test/test_wait3.py Normal file
View File

@ -0,0 +1,32 @@
"""This test checks for correct wait3() behavior.
"""
import os
from test.fork_wait import ForkWait
from test.test_support import TestSkipped, run_unittest
try:
os.fork
except AttributeError:
raise TestSkipped, "os.fork not defined -- skipping test_wait3"
try:
os.wait3
except AttributeError:
raise TestSkipped, "os.wait3 not defined -- skipping test_wait3"
class Wait3Test(ForkWait):
def wait_impl(self, cpid):
while 1:
spid, status, rusage = os.wait3(0)
if spid == cpid:
break
self.assertEqual(spid, cpid)
self.assertEqual(status, 0, "cause = %d, exit = %d" % (status&0xff, status>>8))
self.assertTrue(rusage)
def test_main():
run_unittest(Wait3Test)
if __name__ == "__main__":
test_main()

29
Lib/test/test_wait4.py Normal file
View File

@ -0,0 +1,29 @@
"""This test checks for correct wait4() behavior.
"""
import os
from test.fork_wait import ForkWait
from test.test_support import TestSkipped, run_unittest
try:
os.fork
except AttributeError:
raise TestSkipped, "os.fork not defined -- skipping test_wait4"
try:
os.wait4
except AttributeError:
raise TestSkipped, "os.wait4 not defined -- skipping test_wait4"
class Wait4Test(ForkWait):
def wait_impl(self, cpid):
spid, status, rusage = os.wait4(cpid, 0)
self.assertEqual(spid, cpid)
self.assertEqual(status, 0, "cause = %d, exit = %d" % (status&0xff, status>>8))
self.assertTrue(rusage)
def test_main():
run_unittest(Wait4Test)
if __name__ == "__main__":
test_main()

View File

@ -536,6 +536,7 @@ David Scherer
Gregor Schmid
Ralf Schmitt
Peter Schneider-Kamp
Chad J. Schroeder
Sam Schulenburg
Stefan Schwarzer
Dietmar Schwertberger

View File

@ -295,6 +295,8 @@ Core and builtins
Extension Modules
-----------------
- Patch #1309579: wait3 and wait4 were added to the posix module.
- Patch #1231053: The audioop module now supports encoding/decoding of alaw.
In addition, the existing ulaw code was updated.

View File

@ -5091,6 +5091,128 @@ posix_setgroups(PyObject *self, PyObject *args)
}
#endif /* HAVE_SETGROUPS */
static PyObject *
wait_helper(int pid, int status, struct rusage *ru)
{
PyObject *result;
static PyObject *struct_rusage;
if (pid == -1)
return posix_error();
if (struct_rusage == NULL) {
PyObject *m = PyImport_ImportModule("resource");
if (m == NULL)
return NULL;
struct_rusage = PyObject_GetAttrString(m, "struct_rusage");
Py_DECREF(m);
if (struct_rusage == NULL)
return NULL;
}
/* XXX(nnorwitz): Copied (w/mods) from resource.c, there should be only one. */
result = PyStructSequence_New((PyTypeObject*) struct_rusage);
if (!result)
return NULL;
#ifndef doubletime
#define doubletime(TV) ((double)(TV).tv_sec + (TV).tv_usec * 0.000001)
#endif
PyStructSequence_SET_ITEM(result, 0,
PyFloat_FromDouble(doubletime(ru->ru_utime)));
PyStructSequence_SET_ITEM(result, 1,
PyFloat_FromDouble(doubletime(ru->ru_stime)));
#define SET_INT(result, index, value)\
PyStructSequence_SET_ITEM(result, index, PyInt_FromLong(value))
SET_INT(result, 2, ru->ru_maxrss);
SET_INT(result, 3, ru->ru_ixrss);
SET_INT(result, 4, ru->ru_idrss);
SET_INT(result, 5, ru->ru_isrss);
SET_INT(result, 6, ru->ru_minflt);
SET_INT(result, 7, ru->ru_majflt);
SET_INT(result, 8, ru->ru_nswap);
SET_INT(result, 9, ru->ru_inblock);
SET_INT(result, 10, ru->ru_oublock);
SET_INT(result, 11, ru->ru_msgsnd);
SET_INT(result, 12, ru->ru_msgrcv);
SET_INT(result, 13, ru->ru_nsignals);
SET_INT(result, 14, ru->ru_nvcsw);
SET_INT(result, 15, ru->ru_nivcsw);
#undef SET_INT
if (PyErr_Occurred()) {
Py_DECREF(result);
return NULL;
}
return Py_BuildValue("iiO", pid, status, result);
}
#ifdef HAVE_WAIT3
PyDoc_STRVAR(posix_wait3__doc__,
"wait3(options) -> (pid, status, rusage)\n\n\
Wait for completion of a child process.");
static PyObject *
posix_wait3(PyObject *self, PyObject *args)
{
int pid, options;
struct rusage ru;
#ifdef UNION_WAIT
union wait status;
#define status_i (status.w_status)
#else
int status;
#define status_i status
#endif
status_i = 0;
if (!PyArg_ParseTuple(args, "i:wait3", &options))
return NULL;
Py_BEGIN_ALLOW_THREADS
pid = wait3(&status, options, &ru);
Py_END_ALLOW_THREADS
return wait_helper(pid, status_i, &ru);
#undef status_i
}
#endif /* HAVE_WAIT3 */
#ifdef HAVE_WAIT4
PyDoc_STRVAR(posix_wait4__doc__,
"wait4(pid, options) -> (pid, status, rusage)\n\n\
Wait for completion of a given child process.");
static PyObject *
posix_wait4(PyObject *self, PyObject *args)
{
int pid, options;
struct rusage ru;
#ifdef UNION_WAIT
union wait status;
#define status_i (status.w_status)
#else
int status;
#define status_i status
#endif
status_i = 0;
if (!PyArg_ParseTuple(args, "ii:wait4", &pid, &options))
return NULL;
Py_BEGIN_ALLOW_THREADS
pid = wait4(pid, &status, options, &ru);
Py_END_ALLOW_THREADS
return wait_helper(pid, status_i, &ru);
#undef status_i
}
#endif /* HAVE_WAIT4 */
#ifdef HAVE_WAITPID
PyDoc_STRVAR(posix_waitpid__doc__,
"waitpid(pid, options) -> (pid, status)\n\n\
@ -7696,6 +7818,12 @@ static PyMethodDef posix_methods[] = {
#ifdef HAVE_WAIT
{"wait", posix_wait, METH_NOARGS, posix_wait__doc__},
#endif /* HAVE_WAIT */
#ifdef HAVE_WAIT3
{"wait3", posix_wait3, METH_VARARGS, posix_wait3__doc__},
#endif /* HAVE_WAIT3 */
#ifdef HAVE_WAIT4
{"wait4", posix_wait4, METH_VARARGS, posix_wait4__doc__},
#endif /* HAVE_WAIT4 */
#if defined(HAVE_WAITPID) || defined(HAVE_CWAIT)
{"waitpid", posix_waitpid, METH_VARARGS, posix_waitpid__doc__},
#endif /* HAVE_WAITPID */

6
configure vendored
View File

@ -1,5 +1,5 @@
#! /bin/sh
# From configure.in Revision: 42437 .
# From configure.in Revision: 42563 .
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.59 for python 2.5.
#
@ -14083,6 +14083,8 @@ echo "${ECHO_T}MACHDEP_OBJS" >&6
@ -14097,7 +14099,7 @@ for ac_func in alarm bind_textdomain_codeset chown clock confstr ctermid \
setlocale setregid setreuid setsid setpgid setpgrp setuid setvbuf snprintf \
sigaction siginterrupt sigrelse strftime \
sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile tmpnam tmpnam_r \
truncate uname unsetenv utimes waitpid wcscoll _getpty
truncate uname unsetenv utimes waitpid wait3 wait4 wcscoll _getpty
do
as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
echo "$as_me:$LINENO: checking for $ac_func" >&5

View File

@ -2148,7 +2148,7 @@ AC_CHECK_FUNCS(alarm bind_textdomain_codeset chown clock confstr ctermid \
setlocale setregid setreuid setsid setpgid setpgrp setuid setvbuf snprintf \
sigaction siginterrupt sigrelse strftime \
sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile tmpnam tmpnam_r \
truncate uname unsetenv utimes waitpid wcscoll _getpty)
truncate uname unsetenv utimes waitpid wait3 wait4 wcscoll _getpty)
# For some functions, having a definition is not sufficient, since
# we want to take their address.

View File

@ -670,6 +670,12 @@
/* Define to 1 if you have the <utime.h> header file. */
#undef HAVE_UTIME_H
/* Define to 1 if you have the `wait3' function. */
#undef HAVE_WAIT3
/* Define to 1 if you have the `wait4' function. */
#undef HAVE_WAIT4
/* Define to 1 if you have the `waitpid' function. */
#undef HAVE_WAITPID