Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c8ce715

Browse files
author
Charles-François Natali
committed
Issue #14087: multiprocessing: add Condition.wait_for(). Patch by sbt.
1 parent a3f4457 commit c8ce715

5 files changed

Lines changed: 113 additions & 0 deletions

File tree

Doc/library/multiprocessing.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,9 @@ object -- see :ref:`multiprocessing-managers`.
897897
If *lock* is specified then it should be a :class:`Lock` or :class:`RLock`
898898
object from :mod:`multiprocessing`.
899899

900+
.. versionchanged:: 3.3
901+
The :meth:`wait_for` method was added.
902+
900903
.. class:: Event()
901904

902905
A clone of :class:`threading.Event`.
@@ -1281,6 +1284,9 @@ their parent process exits. The manager classes are defined in the
12811284
If *lock* is supplied then it should be a proxy for a
12821285
:class:`threading.Lock` or :class:`threading.RLock` object.
12831286

1287+
.. versionchanged:: 3.3
1288+
The :meth:`wait_for` method was added.
1289+
12841290
.. method:: Event()
12851291

12861292
Create a shared :class:`threading.Event` object and return a proxy for it.

Lib/multiprocessing/managers.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
from multiprocessing import Process, current_process, active_children, Pool, util, connection
4949
from multiprocessing.process import AuthenticationString
5050
from multiprocessing.forking import exit, Popen, ForkingPickler
51+
from time import time as _time
5152

5253
#
5354
# Register some things for pickling
@@ -996,6 +997,24 @@ def notify(self):
996997
return self._callmethod('notify')
997998
def notify_all(self):
998999
return self._callmethod('notify_all')
1000+
def wait_for(self, predicate, timeout=None):
1001+
result = predicate()
1002+
if result:
1003+
return result
1004+
if timeout is not None:
1005+
endtime = _time() + timeout
1006+
else:
1007+
endtime = None
1008+
waittime = None
1009+
while not result:
1010+
if endtime is not None:
1011+
waittime = endtime - _time()
1012+
if waittime <= 0:
1013+
break
1014+
self.wait(waittime)
1015+
result = predicate()
1016+
return result
1017+
9991018

10001019
class EventProxy(BaseProxy):
10011020
_exposed_ = ('is_set', 'set', 'clear', 'wait')

Lib/multiprocessing/synchronize.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from multiprocessing.process import current_process
4444
from multiprocessing.util import register_after_fork, debug
4545
from multiprocessing.forking import assert_spawning, Popen
46+
from time import time as _time
4647

4748
# Try to import the mp.synchronize module cleanly, if it fails
4849
# raise ImportError for platforms lacking a working sem_open implementation.
@@ -290,6 +291,24 @@ def notify_all(self):
290291
while self._wait_semaphore.acquire(False):
291292
pass
292293

294+
def wait_for(self, predicate, timeout=None):
295+
result = predicate()
296+
if result:
297+
return result
298+
if timeout is not None:
299+
endtime = _time() + timeout
300+
else:
301+
endtime = None
302+
waittime = None
303+
while not result:
304+
if endtime is not None:
305+
waittime = endtime - _time()
306+
if waittime <= 0:
307+
break
308+
self.wait(waittime)
309+
result = predicate()
310+
return result
311+
293312
#
294313
# Event
295314
#

Lib/test/test_multiprocessing.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,73 @@ def test_timeout(self):
887887
self.assertEqual(res, False)
888888
self.assertTimingAlmostEqual(wait.elapsed, TIMEOUT1)
889889

890+
@classmethod
891+
def _test_waitfor_f(cls, cond, state):
892+
with cond:
893+
state.value = 0
894+
cond.notify()
895+
result = cond.wait_for(lambda : state.value==4)
896+
if not result or state.value != 4:
897+
sys.exit(1)
898+
899+
@unittest.skipUnless(HAS_SHAREDCTYPES, 'needs sharedctypes')
900+
def test_waitfor(self):
901+
# based on test in test/lock_tests.py
902+
cond = self.Condition()
903+
state = self.Value('i', -1)
904+
905+
p = self.Process(target=self._test_waitfor_f, args=(cond, state))
906+
p.daemon = True
907+
p.start()
908+
909+
with cond:
910+
result = cond.wait_for(lambda : state.value==0)
911+
self.assertTrue(result)
912+
self.assertEqual(state.value, 0)
913+
914+
for i in range(4):
915+
time.sleep(0.01)
916+
with cond:
917+
state.value += 1
918+
cond.notify()
919+
920+
p.join(5)
921+
self.assertFalse(p.is_alive())
922+
self.assertEqual(p.exitcode, 0)
923+
924+
@classmethod
925+
def _test_waitfor_timeout_f(cls, cond, state, success):
926+
with cond:
927+
expected = 0.1
928+
dt = time.time()
929+
result = cond.wait_for(lambda : state.value==4, timeout=expected)
930+
dt = time.time() - dt
931+
# borrow logic in assertTimeout() from test/lock_tests.py
932+
if not result and expected * 0.6 < dt < expected * 10.0:
933+
success.value = True
934+
935+
@unittest.skipUnless(HAS_SHAREDCTYPES, 'needs sharedctypes')
936+
def test_waitfor_timeout(self):
937+
# based on test in test/lock_tests.py
938+
cond = self.Condition()
939+
state = self.Value('i', 0)
940+
success = self.Value('i', False)
941+
942+
p = self.Process(target=self._test_waitfor_timeout_f,
943+
args=(cond, state, success))
944+
p.daemon = True
945+
p.start()
946+
947+
# Only increment 3 times, so state == 4 is never reached.
948+
for i in range(3):
949+
time.sleep(0.01)
950+
with cond:
951+
state.value += 1
952+
cond.notify()
953+
954+
p.join(5)
955+
self.assertTrue(success.value)
956+
890957

891958
class _TestEvent(BaseTestCase):
892959

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ Core and Builtins
3939
Library
4040
-------
4141

42+
- Issue #14087: multiprocessing: add Condition.wait_for(). Patch by sbt.
43+
4244
- Issue #14452: SysLogHandler no longer inserts a UTF-8 BOM into the message.
4345

4446
- Issue #14386: Expose the dict_proxy internal type as types.MappingProxyType.

0 commit comments

Comments
 (0)