diff options
author | jaseg <git@jaseg.de> | 2024-06-21 16:14:37 +0200 |
---|---|---|
committer | jaseg <git@jaseg.de> | 2024-06-21 16:14:37 +0200 |
commit | f1621b629df60c29945392b5739ac97f33e31bdf (patch) | |
tree | 82c12af9584ef82f2a1544edfb4f35980b677493 | |
parent | 5bb298ad11b7ad7dbeeddfd87546c0d34b28397c (diff) | |
download | python-mpv-f1621b629df60c29945392b5739ac97f33e31bdf.tar.gz python-mpv-f1621b629df60c29945392b5739ac97f33e31bdf.tar.bz2 python-mpv-f1621b629df60c29945392b5739ac97f33e31bdf.zip |
Fix race condition in property observer code leading to futures.InvalidStateError
Previously, prepare_and_wait_for_property was slightly confused on the
lifetime of that future.
This closes #282
-rw-r--r-- | mpv.py | 33 | ||||
-rwxr-xr-x | tests/test_mpv.py | 22 |
2 files changed, 40 insertions, 15 deletions
@@ -1036,30 +1036,38 @@ class MPV(object): rv = cond(val) if rv: result.set_result(rv) + + except InvalidStateError: + pass + except Exception as e: try: result.set_exception(e) - except InvalidStateError: + except: pass - except InvalidStateError: - pass - self.observe_property(name, observer) - err_unregister = self._set_error_handler(result) try: result.set_running_or_notify_cancel() + + self.observe_property(name, observer) + err_unregister = self._set_error_handler(result) if catch_errors: self._exception_futures.add(result) yield result - rv = cond(getattr(self, name.replace('-', '_'))) - if level_sensitive and rv: - result.set_result(rv) + if level_sensitive: + rv = cond(getattr(self, name.replace('-', '_'))) + if rv: + result.set_result(rv) + return + + self.check_core_alive() + result.result(timeout) + + except InvalidStateError: + pass - else: - self.check_core_alive() - result.result(timeout) finally: err_unregister() self.unobserve_property(name, observer) @@ -1821,9 +1829,6 @@ class MPV(object): pass else: warnings.warn(f'Unhandled exception {e} inside stream open callback for URI {uri}\n{traceback.format_exc()}') - - - return ErrorCode.LOADING_FAILED cb_info.contents.cookie = None diff --git a/tests/test_mpv.py b/tests/test_mpv.py index 0935a32..6a962f0 100755 --- a/tests/test_mpv.py +++ b/tests/test_mpv.py @@ -23,7 +23,7 @@ from contextlib import contextmanager import os.path import os import time -from concurrent.futures import Future +from concurrent.futures import Future, InvalidStateError os.environ["PATH"] = os.path.dirname(__file__) + os.pathsep + os.environ["PATH"] @@ -915,6 +915,25 @@ class CommandTests(MpvTestCase): class RegressionTests(MpvTestCase): + def test_wait_for_property_concurrency(self): + players = [mpv.MPV(vo=testvo, loglevel='debug', log_handler=timed_print()) for i in range(2)] + + try: + for _ in range(150): + for player in players: + player.play('tests/test.webm') + for player in players: + player.wait_for_property('seekable') + for player in players: + player.seek(0, reference='absolute', precision='exact') + + except InvalidStateError: + self.fail('InvalidStateError thrown from wait_for_property') + + finally: + for player in players: + player.terminate() + def test_unobserve_property_runtime_error(self): """ Ensure a `RuntimeError` is not thrown within @@ -966,3 +985,4 @@ class RegressionTests(MpvTestCase): m.slang = 'ru' m.terminate() # needed for synchronization of event thread handler.assert_has_calls([mock.call('slang', ['jp']), mock.call('slang', ['ru'])]) + |