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
This commit is contained in:
parent
5bb298ad11
commit
f1621b629d
2 changed files with 40 additions and 15 deletions
33
mpv.py
33
mpv.py
|
|
@ -1036,30 +1036,38 @@ class MPV(object):
|
||||||
rv = cond(val)
|
rv = cond(val)
|
||||||
if rv:
|
if rv:
|
||||||
result.set_result(rv)
|
result.set_result(rv)
|
||||||
|
|
||||||
|
except InvalidStateError:
|
||||||
|
pass
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
try:
|
try:
|
||||||
result.set_exception(e)
|
result.set_exception(e)
|
||||||
except InvalidStateError:
|
except:
|
||||||
pass
|
pass
|
||||||
except InvalidStateError:
|
|
||||||
pass
|
|
||||||
self.observe_property(name, observer)
|
|
||||||
err_unregister = self._set_error_handler(result)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result.set_running_or_notify_cancel()
|
result.set_running_or_notify_cancel()
|
||||||
|
|
||||||
|
self.observe_property(name, observer)
|
||||||
|
err_unregister = self._set_error_handler(result)
|
||||||
if catch_errors:
|
if catch_errors:
|
||||||
self._exception_futures.add(result)
|
self._exception_futures.add(result)
|
||||||
|
|
||||||
yield result
|
yield result
|
||||||
|
|
||||||
rv = cond(getattr(self, name.replace('-', '_')))
|
if level_sensitive:
|
||||||
if level_sensitive and rv:
|
rv = cond(getattr(self, name.replace('-', '_')))
|
||||||
result.set_result(rv)
|
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:
|
finally:
|
||||||
err_unregister()
|
err_unregister()
|
||||||
self.unobserve_property(name, observer)
|
self.unobserve_property(name, observer)
|
||||||
|
|
@ -1821,9 +1829,6 @@ class MPV(object):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
warnings.warn(f'Unhandled exception {e} inside stream open callback for URI {uri}\n{traceback.format_exc()}')
|
warnings.warn(f'Unhandled exception {e} inside stream open callback for URI {uri}\n{traceback.format_exc()}')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return ErrorCode.LOADING_FAILED
|
return ErrorCode.LOADING_FAILED
|
||||||
|
|
||||||
cb_info.contents.cookie = None
|
cb_info.contents.cookie = None
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ from contextlib import contextmanager
|
||||||
import os.path
|
import os.path
|
||||||
import os
|
import os
|
||||||
import time
|
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"]
|
os.environ["PATH"] = os.path.dirname(__file__) + os.pathsep + os.environ["PATH"]
|
||||||
|
|
||||||
|
|
@ -915,6 +915,25 @@ class CommandTests(MpvTestCase):
|
||||||
|
|
||||||
class RegressionTests(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):
|
def test_unobserve_property_runtime_error(self):
|
||||||
"""
|
"""
|
||||||
Ensure a `RuntimeError` is not thrown within
|
Ensure a `RuntimeError` is not thrown within
|
||||||
|
|
@ -966,3 +985,4 @@ class RegressionTests(MpvTestCase):
|
||||||
m.slang = 'ru'
|
m.slang = 'ru'
|
||||||
m.terminate() # needed for synchronization of event thread
|
m.terminate() # needed for synchronization of event thread
|
||||||
handler.assert_has_calls([mock.call('slang', ['jp']), mock.call('slang', ['ru'])])
|
handler.assert_has_calls([mock.call('slang', ['jp']), mock.call('slang', ['ru'])])
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue