Commits

Neal Norwitz  committed d62b134

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).

  • Participants
  • Parent commits de8f5d1
  • Branches legacy-trunk

Comments (0)

Files changed (11)

File Doc/lib/libos.tex

 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

File Lib/test/fork_wait.py

+"""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

File Lib/test/test_fork1.py

 """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
+class ForkTest(ForkWait):
+    def wait_impl(self, cpid):
+        spid, status = os.waitpid(cpid, 0)
+        self.assertEqual(spid, cpid)
+        self.assertEqual(status, 0, "cause = %d, exit = %d" % (status&0xff, status>>8))
 
-SHORTSLEEP = 0.5
+def test_main():
+    run_unittest(ForkTest)
 
-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
-        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
-
-main()
+if __name__ == "__main__":
+    test_main()

File Lib/test/test_wait3.py

+"""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()

File Lib/test/test_wait4.py

+"""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()
 Gregor Schmid
 Ralf Schmitt
 Peter Schneider-Kamp
+Chad J. Schroeder
 Sam Schulenburg
 Stefan Schwarzer
 Dietmar Schwertberger
 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.
 

File Modules/posixmodule.c

 }
 #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\
 #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 */
 #! /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.
 #
 
 
 
+
+
 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 \
  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

File configure.in

  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.

File pyconfig.h.in

 /* 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