mirror of https://github.com/python/cpython
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:
parent
910b5eec07
commit
05a45599d7
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
|
@ -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()
|
|
@ -536,6 +536,7 @@ David Scherer
|
|||
Gregor Schmid
|
||||
Ralf Schmitt
|
||||
Peter Schneider-Kamp
|
||||
Chad J. Schroeder
|
||||
Sam Schulenburg
|
||||
Stefan Schwarzer
|
||||
Dietmar Schwertberger
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue