summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Oudkerk <shibturn@gmail.com>2012-05-10 16:11:12 +0100
committerRichard Oudkerk <shibturn@gmail.com>2012-05-10 16:11:12 +0100
commit59d5404bc79beaa39c76fb26012238b26e9b0166 (patch)
tree071cc74196e6fcc572c836b6be02a8d9a6bfc276
parentca5f91b888bc0056fc08d062f65cc783bbba8532 (diff)
downloadcpython-git-59d5404bc79beaa39c76fb26012238b26e9b0166.tar.gz
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.
-rw-r--r--Doc/library/multiprocessing.rst7
-rw-r--r--Lib/multiprocessing/connection.py7
-rw-r--r--Lib/multiprocessing/forking.py9
-rw-r--r--Lib/multiprocessing/util.py15
-rw-r--r--Lib/test/test_multiprocessing.py44
-rw-r--r--Misc/NEWS3
6 files changed, 42 insertions, 43 deletions
diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst
index bdc07f14ab..320e49298b 100644
--- a/Doc/library/multiprocessing.rst
+++ b/Doc/library/multiprocessing.rst
@@ -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
diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py
index acf43b1fef..56f375d237 100644
--- a/Lib/multiprocessing/connection.py
+++ b/Lib/multiprocessing/connection.py
@@ -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)
diff --git a/Lib/multiprocessing/forking.py b/Lib/multiprocessing/forking.py
index ca03e955ff..2729afe2bb 100644
--- a/Lib/multiprocessing/forking.py
+++ b/Lib/multiprocessing/forking.py
@@ -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
diff --git a/Lib/multiprocessing/util.py b/Lib/multiprocessing/util.py
index da99063e57..9b6dac2006 100644
--- a/Lib/multiprocessing/util.py
+++ b/Lib/multiprocessing/util.py
@@ -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
diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py
index a839917c36..d10d51b8ed 100644
--- a/Lib/test/test_multiprocessing.py
+++ b/Lib/test/test_multiprocessing.py
@@ -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
diff --git a/Misc/NEWS b/Misc/NEWS
index f383e91340..d123c01ff5 100644
--- 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.