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).
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 @@
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))
-main()
+def test_main():
+ run_unittest(ForkTest)
+
+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 @@
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 @@
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 @@
}
#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 @@
#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 @@
+
+
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 @@
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 @@
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 <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