From 05a45599d7c7e7584a80d7ef70dc47e984664619 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Mon, 20 Mar 2006 06:30:08 +0000 Subject: 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). --- Doc/lib/libos.tex | 21 ++++++++ Lib/test/fork_wait.py | 71 +++++++++++++++++++++++++++ Lib/test/test_fork1.py | 76 +++++------------------------ Lib/test/test_wait3.py | 32 +++++++++++++ Lib/test/test_wait4.py | 29 +++++++++++ Misc/ACKS | 1 + Misc/NEWS | 2 + Modules/posixmodule.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++ configure | 6 ++- configure.in | 2 +- pyconfig.h.in | 6 +++ 11 files changed, 307 insertions(+), 67 deletions(-) create mode 100644 Lib/test/fork_wait.py create mode 100644 Lib/test/test_wait3.py create mode 100644 Lib/test/test_wait4.py diff --git a/Doc/lib/libos.tex b/Doc/lib/libos.tex index 9af5889..8c4b770 100644 --- a/Doc/lib/libos.tex +++ b/Doc/lib/libos.tex @@ -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 diff --git a/Lib/test/fork_wait.py b/Lib/test/fork_wait.py new file mode 100644 index 0000000..5600bdb --- /dev/null +++ b/Lib/test/fork_wait.py @@ -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 diff --git a/Lib/test/test_fork1.py b/Lib/test/test_fork1.py index aca7a84..cba5fc7 100644 --- a/Lib/test/test_fork1.py +++ b/Lib/test/test_fork1.py @@ -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)) + +def test_main(): + run_unittest(ForkTest) -main() +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_wait3.py b/Lib/test/test_wait3.py new file mode 100644 index 0000000..a1cbd7b --- /dev/null +++ b/Lib/test/test_wait3.py @@ -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() diff --git a/Lib/test/test_wait4.py b/Lib/test/test_wait4.py new file mode 100644 index 0000000..027e5c3 --- /dev/null +++ b/Lib/test/test_wait4.py @@ -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() diff --git a/Misc/ACKS b/Misc/ACKS index 9225031..12983c5 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -536,6 +536,7 @@ David Scherer Gregor Schmid Ralf Schmitt Peter Schneider-Kamp +Chad J. Schroeder Sam Schulenburg Stefan Schwarzer Dietmar Schwertberger diff --git a/Misc/NEWS b/Misc/NEWS index 8069142..06f64f6 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -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. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 116b66b..50b2cc1 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -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 */ diff --git a/configure b/configure index aad20c8..36af89e 100755 --- a/configure +++ b/configure @@ -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. # @@ -14086,6 +14086,8 @@ echo "${ECHO_T}MACHDEP_OBJS" >&6 + + for ac_func in alarm bind_textdomain_codeset chown clock confstr ctermid \ execv fork fpathconf ftime ftruncate \ gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \ @@ -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 diff --git a/configure.in b/configure.in index d617108..5d9ec56 100644 --- a/configure.in +++ b/configure.in @@ -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. diff --git a/pyconfig.h.in b/pyconfig.h.in index 9c3ca53..8df7f9b 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -670,6 +670,12 @@ /* Define to 1 if you have the 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 -- cgit v0.12