Skip to content

Fix macOS threading hang in wait_for_playback (issue #61) - #316

Open
18680368135 wants to merge 1 commit into
jaseg:mainfrom
18680368135:fix/macos-threading-issue-61
Open

Fix macOS threading hang in wait_for_playback (issue #61)#316
18680368135 wants to merge 1 commit into
jaseg:mainfrom
18680368135:fix/macos-threading-issue-61

Conversation

@18680368135

Copy link
Copy Markdown

Summary

This PR fixes issue #61 where wait_for_playback() hangs indefinitely on macOS. The fix addresses three root causes:

1. Race condition in prepare_and_wait_for_event()

The event callback was registered before result.set_running_or_notify_cancel() was called. If an event fired in that window, set_result() raised InvalidStateError which was silently caught, causing the event to be lost forever and the wait to hang indefinitely.

This matches the ordering already used in prepare_and_wait_for_property() (lines 1070-1072), where set_running_or_notify_cancel() is called before observe_property().

2. Blocking deadlock in _event_generator() on macOS

_mpv_wait_event(handle, -1) blocks forever on macOS when the Cocoa main-loop is not running (because mpv cannot initialize its Cocoa-based video output drivers without an NSApplication event loop).

Replaced with a pipe-based wakeup mechanism using mpv_set_wakeup_callback and select() with a 100ms timeout, then drain pending events with non-blocking _mpv_wait_event(handle, 0). This makes the event thread responsive to shutdown and prevents the deadlock. The wakeup callback infrastructure (WakeupCallback, _mpv_set_wakeup_callback) was already defined in the codebase but unused.

3. macOS platform detection and warning

Added a RuntimeWarning when creating an MPV instance on macOS without a wid parameter, explaining that a running NSApplication event loop is required. The docstring includes a code example showing how to set up the Cocoa event loop.

Testing

  • test_event_fired_during_registration_is_not_lost: Verifies that events fired during callback registration are captured (not lost due to InvalidStateError)
  • test_future_in_running_state_before_callback_registration: Verifies that set_running_or_notify_cancel() is called before callback registration
  • test_macos_warning_without_wid: Verifies the macOS warning is emitted (skipped on non-macOS)
  • test_no_warning_on_non_darwin: Verifies no spurious warning on non-macOS platforms

All tests pass on Linux. The race condition tests use mocks and do not require a running libmpv instance.

Fixes #61

Three changes address the root causes of issue jaseg#61 where
wait_for_playback() hangs indefinitely on macOS:

1. Fix race condition in prepare_and_wait_for_event():
   The event callback was registered before
   result.set_running_or_notify_cancel() was called. If an event fired
   in that window, set_result() raised InvalidStateError which was
   silently caught, causing the event to be lost and the wait to hang
   forever. This matches the ordering already used in
   prepare_and_wait_for_property().

2. Fix _event_generator() blocking deadlock on macOS:
   _mpv_wait_event(handle, -1) blocks forever on macOS when the Cocoa
   main-loop is not running. Replace with a pipe-based wakeup mechanism
   using mpv_set_wakeup_callback and select() with a 100ms timeout,
   then drain pending events with non-blocking _mpv_wait_event(handle,
   0). This makes the event thread responsive to shutdown and prevents
   the deadlock.

3. Add macOS platform detection:
   Warn users on macOS who create an MPV instance without a wid
   parameter that they need a running NSApplication event loop,
   with a code example in the docstring.

Includes unit tests for the race condition fix that do not require a
running libmpv instance.

Fixes jaseg#61.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stuck in threading on macOS

1 participant