diff --git a/mopidy/core/playback.py b/mopidy/core/playback.py index a8afebd3..4216f349 100644 --- a/mopidy/core/playback.py +++ b/mopidy/core/playback.py @@ -27,7 +27,8 @@ class PlaybackController(object): self._pending_tl_track = None if self._audio: - self._audio.set_about_to_finish_callback(self._on_about_to_finish) + self._audio.set_about_to_finish_callback( + self._on_about_to_finish_callback) def _get_backend(self, tl_track): if tl_track is None: @@ -206,6 +207,19 @@ class PlaybackController(object): self._pending_tl_track = None self._trigger_track_playback_started() + def _on_about_to_finish_callback(self): + """Callback that performs a blocking actor call to the real callback. + + This is passed to audio, which is allowed to call this code from the + audio thread. We pass execution into the core actor to ensure that + there is no unsafe access of state in core. This must block until + we get a response. + """ + self.core.actor_ref.ask({ + 'command': 'pykka_call', 'args': tuple(), 'kwargs': {}, + 'attr_path': ('playback', '_on_about_to_finish'), + }) + def _on_about_to_finish(self): self._trigger_track_playback_ended(self.get_time_position()) diff --git a/tests/core/test_playback.py b/tests/core/test_playback.py index 60a3f612..0869b3ec 100644 --- a/tests/core/test_playback.py +++ b/tests/core/test_playback.py @@ -40,6 +40,10 @@ class BaseTest(unittest.TestCase): audio=self.audio, backends=[self.backend], config=self.config) self.playback = self.core.playback + # We don't have a core actor running, so call about to finish directly. + self.audio.set_about_to_finish_callback( + self.playback._on_about_to_finish) + with deprecation.ignore('core.tracklist.add:tracks_arg'): self.core.tracklist.add(self.tracks)