diff -r 21f6c4378846 Lib/multiprocessing/synchronize.py --- a/Lib/multiprocessing/synchronize.py Mon Oct 26 04:37:55 2015 -0400 +++ b/Lib/multiprocessing/synchronize.py Thu Nov 05 22:20:10 2015 +0000 @@ -259,7 +259,12 @@ try: # wait for notification or timeout - return self._wait_semaphore.acquire(True, timeout) + notified = self._wait_semaphore.acquire(True, timeout) + except: + notified = False + raise + else: + return notified finally: # indicate that this thread has woken self._woken_count.release() @@ -268,16 +273,15 @@ for i in range(count): self._lock.acquire() + # decrement counters since wasn't notified + if not notified: + self._woken_count.acquire(False) + self._sleeping_count.acquire(False) + def notify(self): assert self._lock._semlock._is_mine(), 'lock is not owned' assert not self._wait_semaphore.acquire(False) - # to take account of timeouts since last notify() we subtract - # woken_count from sleeping_count and rezero woken_count - while self._woken_count.acquire(False): - res = self._sleeping_count.acquire(False) - assert res - if self._sleeping_count.acquire(False): # try grabbing a sleeper self._wait_semaphore.release() # wake up one sleeper self._woken_count.acquire() # wait for the sleeper to wake @@ -289,12 +293,6 @@ assert self._lock._semlock._is_mine(), 'lock is not owned' assert not self._wait_semaphore.acquire(False) - # to take account of timeouts since last notify*() we subtract - # woken_count from sleeping_count and rezero woken_count - while self._woken_count.acquire(False): - res = self._sleeping_count.acquire(False) - assert res - sleepers = 0 while self._sleeping_count.acquire(False): self._wait_semaphore.release() # wake up one sleeper diff -r 21f6c4378846 Lib/test/_test_multiprocessing.py --- a/Lib/test/_test_multiprocessing.py Mon Oct 26 04:37:55 2015 -0400 +++ b/Lib/test/_test_multiprocessing.py Thu Nov 05 22:20:10 2015 +0000 @@ -1103,6 +1103,34 @@ p.start() self.assertEqual(wait(), True) + @classmethod + def _test_issue_25469_f(cls, event): + while not event.wait(0): + pass + + def test_issue_25469(self): + event = self.Event() + procs = [self.Process(target=self._test_issue_25469_f, args=(event,)) + for _ in range(3)] + set_ = TimingWrapper(event.set) + + for p in procs: + p.daemon = True + p.start() + # allow for child process to make many Event.wait() calls with timeout + time.sleep(3) + set_() + + for p in procs: + p.join(1) + self.assertFalse(p.is_alive()) + + t_after_waits = set_.elapsed + set_() + # Timed-out calls to Event.wait() should not result in Event.set() + # taking any longer than when it's called otherwise + self.assertAlmostEqual(t_after_waits, set_.elapsed, 2) + # # Tests for Barrier - adapted from tests in test/lock_tests.py #