diff --git a/kazoo/handlers/utils.py b/kazoo/handlers/utils.py index 717c8be4..e889f822 100644 --- a/kazoo/handlers/utils.py +++ b/kazoo/handlers/utils.py @@ -92,7 +92,8 @@ def get_nowait(self): def wait(self, timeout=None): """Block until the instance is ready.""" with self._condition: - self._condition.wait(timeout) + if not self.ready(): + self._condition.wait(timeout) return self._exception is not _NONE def rawlink(self, callback): diff --git a/kazoo/tests/test_threading_handler.py b/kazoo/tests/test_threading_handler.py index 119154d1..24445339 100644 --- a/kazoo/tests/test_threading_handler.py +++ b/kazoo/tests/test_threading_handler.py @@ -200,6 +200,33 @@ def wait_for_val(): eq_(lst, [True]) th.join() + def test_wait_race(self): + """Test that there is no race condition in `IAsyncResult.wait()`. + + Guards against the reappearance of: + https://github.com/python-zk/kazoo/issues/485 + """ + mock_handler = mock.Mock() + async_result = self._makeOne(mock_handler) + + async_result.set("immediate") + + cv = threading.Event() + + def wait_for_val(): + # NB: should not sleep + async_result.wait(20) + cv.set() + th = threading.Thread(target=wait_for_val) + th.daemon = True + th.start() + + # if the wait() didn't sleep (correctly), cv will be set quickly + # if it did sleep, the cv will not be set yet and this will timeout + cv.wait(10) + eq_(cv.is_set(), True) + th.join() + def test_set_before_wait(self): mock_handler = mock.Mock() async_result = self._makeOne(mock_handler)