]> granicus.if.org Git - python/commitdiff
Issue #14753: Make multiprocessing treat negative timeouts as it did in 3.2
authorRichard Oudkerk <shibturn@gmail.com>
Thu, 10 May 2012 15:11:12 +0000 (16:11 +0100)
committerRichard Oudkerk <shibturn@gmail.com>
Thu, 10 May 2012 15:11:12 +0000 (16:11 +0100)
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.

Doc/library/multiprocessing.rst
Lib/multiprocessing/connection.py
Lib/multiprocessing/forking.py
Lib/multiprocessing/util.py
Lib/test/test_multiprocessing.py
Misc/NEWS

index bdc07f14ab21210800a6d0ff756a600d2245c80d..320e49298beee97da84e8760e0cbf389b349f37f 100644 (file)
@@ -928,6 +928,12 @@ object -- see :ref:`multiprocessing-managers`.
 
 .. 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 @@ multiple connections at the same time.
    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
index acf43b1feffe326a77287eeb2ca548b42bc0558f..56f375d237a025c7c3e1ec177679bdc74f19d544 100644 (file)
@@ -23,8 +23,7 @@ import itertools
 
 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 _winapi:
             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 @@ class Connection(_ConnectionBase):
         return self._recv(size)
 
     def _poll(self, timeout):
-        if timeout < 0.0:
-            timeout = None
         r = wait([self._handle], timeout)
         return bool(r)
 
index ca03e955ff134383098468cc9cdae6cbeded50b9..2729afe2bbd01e6728d082aa19c9aa250b1894de 100644 (file)
@@ -75,12 +75,9 @@ else:
 #
 
 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 @@ if sys.platform != 'win32':
         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
 
index da99063e578717292d24820d348da6925134bb5b..9b6dac2006f08040680afb6e9b0d1459e8af570e 100644 (file)
@@ -295,18 +295,3 @@ class ForkAwareLocal(threading.local):
         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
index a839917c3611d5f5ce027b131ca227292364c9a3..d10d51b8ed821bf9d54dbc279749bae5e1fa1214 100644 (file)
@@ -83,23 +83,13 @@ HAVE_GETVALUE = not getattr(_multiprocessing,
                             '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)
+from multiprocessing.connection import wait
 
-    def wait_for_handle(handle, timeout):
-        if timeout is not None and timeout < 0.0:
-            timeout = None
-        return handle in _select([handle], [], [], timeout)[0]
+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 @@ class _TestProcess(BaseTestCase):
         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 @@ class _TestConnection(BaseTestCase):
         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 @@ class TestWait(unittest.TestCase):
         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
index f383e91340ebfa4965ef56394526c2d50b2320c0..d123c01ff545c5175c997c17a86e7b4a7a0c5fa0 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -23,6 +23,9 @@ Core and Builtins
 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.