[Python-checkins] cpython: Issue #14753: Make multiprocessing treat negative timeouts as it did in 3.2

richard.oudkerk python-checkins at python.org
Thu May 10 17:13:31 CEST 2012


http://hg.python.org/cpython/rev/99ef4501205b
changeset:   76863:99ef4501205b
user:        Richard Oudkerk <shibturn at gmail.com>
date:        Thu May 10 16:11:12 2012 +0100
summary:
  Issue #14753: Make multiprocessing treat negative timeouts as it did in 3.2

In Python 3.2 and earlier, Process.join() and Connection.poll()
treated negative timeouts as zero timeouts.  Earlier versions from
the 3.3 line of development treat them as infinite timeouts.

The patch reverts to the old behaviour.

files:
  Doc/library/multiprocessing.rst   |   7 ++
  Lib/multiprocessing/connection.py |   7 +--
  Lib/multiprocessing/forking.py    |   9 +--
  Lib/multiprocessing/util.py       |  15 -----
  Lib/test/test_multiprocessing.py  |  48 +++++++++++-------
  Misc/NEWS                         |   3 +
  6 files changed, 44 insertions(+), 45 deletions(-)


diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst
--- a/Doc/library/multiprocessing.rst
+++ b/Doc/library/multiprocessing.rst
@@ -928,6 +928,12 @@
 
 .. note::
 
+   The :meth:`acquire` and :meth:`wait` methods of each of these types
+   treat negative timeouts as zero timeouts.  This differs from
+   :mod:`threading` where, since version 3.2, the equivalent
+   :meth:`acquire` methods treat negative timeouts as infinite
+   timeouts.
+
    On Mac OS X, ``sem_timedwait`` is unsupported, so calling ``acquire()`` with
    a timeout will emulate that function's behavior using a sleeping loop.
 
@@ -1899,6 +1905,7 @@
    those objects in *object_list* which are ready.  If *timeout* is a
    float then the call blocks for at most that many seconds.  If
    *timeout* is ``None`` then it will block for an unlimited period.
+   A negative timeout is equivalent to a zero timeout.
 
    For both Unix and Windows, an object can appear in *object_list* if
    it is
diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py
--- a/Lib/multiprocessing/connection.py
+++ b/Lib/multiprocessing/connection.py
@@ -23,8 +23,7 @@
 
 import _multiprocessing
 from multiprocessing import current_process, AuthenticationError, BufferTooShort
-from multiprocessing.util import (
-    get_temp_dir, Finalize, sub_debug, debug, _eintr_retry)
+from multiprocessing.util import get_temp_dir, Finalize, sub_debug, debug
 from multiprocessing.forking import ForkingPickler
 try:
     import _winapi
@@ -323,8 +322,6 @@
             if (self._got_empty_message or
                         _winapi.PeekNamedPipe(self._handle)[0] != 0):
                 return True
-            if timeout < 0:
-                timeout = None
             return bool(wait([self], timeout))
 
         def _get_more_data(self, ov, maxsize):
@@ -402,8 +399,6 @@
         return self._recv(size)
 
     def _poll(self, timeout):
-        if timeout < 0.0:
-            timeout = None
         r = wait([self._handle], timeout)
         return bool(r)
 
diff --git a/Lib/multiprocessing/forking.py b/Lib/multiprocessing/forking.py
--- a/Lib/multiprocessing/forking.py
+++ b/Lib/multiprocessing/forking.py
@@ -75,12 +75,9 @@
 #
 
 if sys.platform != 'win32':
-    import select
-
     exit = os._exit
     duplicate = os.dup
     close = os.close
-    _select = util._eintr_retry(select.select)
 
     #
     # We define a Popen class similar to the one from subprocess, but
@@ -130,10 +127,10 @@
         def wait(self, timeout=None):
             if self.returncode is None:
                 if timeout is not None:
-                    r = _select([self.sentinel], [], [], timeout)[0]
-                    if not r:
+                    from .connection import wait
+                    if not wait([self.sentinel], timeout):
                         return None
-                # This shouldn't block if select() returned successfully.
+                # This shouldn't block if wait() returned successfully.
                 return self.poll(os.WNOHANG if timeout == 0.0 else 0)
             return self.returncode
 
diff --git a/Lib/multiprocessing/util.py b/Lib/multiprocessing/util.py
--- a/Lib/multiprocessing/util.py
+++ b/Lib/multiprocessing/util.py
@@ -295,18 +295,3 @@
         register_after_fork(self, lambda obj : obj.__dict__.clear())
     def __reduce__(self):
         return type(self), ()
-
-
-#
-# Automatic retry after EINTR
-#
-
-def _eintr_retry(func):
-    @functools.wraps(func)
-    def wrapped(*args, **kwargs):
-        while True:
-            try:
-                return func(*args, **kwargs)
-            except InterruptedError:
-                continue
-    return wrapped
diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py
--- a/Lib/test/test_multiprocessing.py
+++ b/Lib/test/test_multiprocessing.py
@@ -83,23 +83,13 @@
                             'HAVE_BROKEN_SEM_GETVALUE', False)
 
 WIN32 = (sys.platform == "win32")
-if WIN32:
-    from _winapi import WaitForSingleObject, INFINITE, WAIT_OBJECT_0
-
-    def wait_for_handle(handle, timeout):
-        if timeout is None or timeout < 0.0:
-            timeout = INFINITE
-        else:
-            timeout = int(1000 * timeout)
-        return WaitForSingleObject(handle, timeout) == WAIT_OBJECT_0
-else:
-    from select import select
-    _select = util._eintr_retry(select)
-
-    def wait_for_handle(handle, timeout):
-        if timeout is not None and timeout < 0.0:
-            timeout = None
-        return handle in _select([handle], [], [], timeout)[0]
+
+from multiprocessing.connection import wait
+
+def wait_for_handle(handle, timeout):
+    if timeout is not None and timeout < 0.0:
+        timeout = None
+    return wait([handle], timeout)
 
 try:
     MAXFD = os.sysconf("SC_OPEN_MAX")
@@ -291,9 +281,18 @@
         self.assertIn(p, self.active_children())
         self.assertEqual(p.exitcode, None)
 
+        join = TimingWrapper(p.join)
+
+        self.assertEqual(join(0), None)
+        self.assertTimingAlmostEqual(join.elapsed, 0.0)
+        self.assertEqual(p.is_alive(), True)
+
+        self.assertEqual(join(-1), None)
+        self.assertTimingAlmostEqual(join.elapsed, 0.0)
+        self.assertEqual(p.is_alive(), True)
+
         p.terminate()
 
-        join = TimingWrapper(p.join)
         self.assertEqual(join(), None)
         self.assertTimingAlmostEqual(join.elapsed, 0.0)
 
@@ -1664,6 +1663,9 @@
         self.assertEqual(poll(), False)
         self.assertTimingAlmostEqual(poll.elapsed, 0)
 
+        self.assertEqual(poll(-1), False)
+        self.assertTimingAlmostEqual(poll.elapsed, 0)
+
         self.assertEqual(poll(TIMEOUT1), False)
         self.assertTimingAlmostEqual(poll.elapsed, TIMEOUT1)
 
@@ -2785,6 +2787,16 @@
         p.terminate()
         p.join()
 
+    def test_neg_timeout(self):
+        from multiprocessing.connection import wait
+        a, b = multiprocessing.Pipe()
+        t = time.time()
+        res = wait([a], timeout=-1)
+        t = time.time() - t
+        self.assertEqual(res, [])
+        self.assertLess(t, 1)
+        a.close()
+        b.close()
 
 #
 # Issue 14151: Test invalid family on invalid environment
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -23,6 +23,9 @@
 Library
 -------
 
+- Issue #14753: Make multiprocessing's handling of negative timeouts
+  the same as it was in Python 3.2.
+
 - Issue #14583: Fix importlib bug when a package's __init__.py would first
   import one of its modules then raise an error.
 

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list