diff options
-rw-r--r-- | Doc/library/multiprocessing.rst | 7 | ||||
-rw-r--r-- | Lib/multiprocessing/connection.py | 7 | ||||
-rw-r--r-- | Lib/multiprocessing/forking.py | 9 | ||||
-rw-r--r-- | Lib/multiprocessing/util.py | 15 | ||||
-rw-r--r-- | Lib/test/test_multiprocessing.py | 44 | ||||
-rw-r--r-- | Misc/NEWS | 3 |
6 files changed, 42 insertions, 43 deletions
diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst index bdc07f1..320e492 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 acf43b1..56f375d 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 ca03e95..2729afe 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 da99063..9b6dac2 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 a839917..d10d51b 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 @@ -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. |