fix: linux mpv dispose deadlock on wayland

This commit is contained in:
edde746
2026-02-19 21:50:29 +01:00
parent df09d2249d
commit 582809427e
3 changed files with 27 additions and 16 deletions
+20 -13
View File
@@ -130,13 +130,27 @@ bool MpvPlayer::InitRenderContext() {
} }
void MpvPlayer::Dispose() { void MpvPlayer::Dispose() {
std::lock_guard<std::mutex> lock(callback_mutex_); // 1. Set disposed flag atomically FIRST — all callback paths check this
if (disposed_.exchange(true)) { if (disposed_.exchange(true)) {
return; return;
} }
// Cancel pending async commands // 2. Clear mpv's native callbacks to prevent new ones from firing
if (mpv_gl_) {
mpv_render_context_set_update_callback(mpv_gl_, nullptr, nullptr);
}
if (mpv_) {
mpv_set_wakeup_callback(mpv_, nullptr, nullptr);
}
// 3. Briefly hold mutex to null our callbacks
{
std::lock_guard<std::mutex> lock(callback_mutex_);
redraw_callback_ = nullptr;
event_callback_ = nullptr;
}
// 4. Cancel pending async commands
{ {
std::lock_guard<std::mutex> cmd_lock(pending_commands_mutex_); std::lock_guard<std::mutex> cmd_lock(pending_commands_mutex_);
for (auto& pair : pending_commands_) { for (auto& pair : pending_commands_) {
@@ -145,20 +159,14 @@ void MpvPlayer::Dispose() {
pending_commands_.clear(); pending_commands_.clear();
} }
// Clear mpv callbacks BEFORE freeing // 5. Remove pending idle callbacks
if (mpv_gl_) {
mpv_render_context_set_update_callback(mpv_gl_, nullptr, nullptr);
}
if (mpv_) {
mpv_set_wakeup_callback(mpv_, nullptr, nullptr);
}
// Remove pending idle callbacks
if (event_source_id_ != 0) { if (event_source_id_ != 0) {
g_source_remove(event_source_id_); g_source_remove(event_source_id_);
event_source_id_ = 0; event_source_id_ = 0;
} }
// 6. Free render context and mpv handle WITHOUT holding callback_mutex_
// (mpv_render_context_free can block waiting for render thread)
if (mpv_gl_) { if (mpv_gl_) {
mpv_render_context_free(mpv_gl_); mpv_render_context_free(mpv_gl_);
mpv_gl_ = nullptr; mpv_gl_ = nullptr;
@@ -170,7 +178,6 @@ void MpvPlayer::Dispose() {
} }
observed_properties_.clear(); observed_properties_.clear();
redraw_callback_ = nullptr;
} }
void MpvPlayer::Command(const std::vector<std::string>& args) { void MpvPlayer::Command(const std::vector<std::string>& args) {
+3
View File
@@ -55,6 +55,9 @@ class MpvPlayer {
/// Returns true if mpv is initialized (has both mpv handle and render context). /// Returns true if mpv is initialized (has both mpv handle and render context).
bool IsInitialized() const { return mpv_ != nullptr && mpv_gl_ != nullptr; } bool IsInitialized() const { return mpv_ != nullptr && mpv_gl_ != nullptr; }
/// Returns true if this player has been disposed.
bool IsDisposed() const { return disposed_.load(); }
/// Returns true if mpv handle exists (even without render context). /// Returns true if mpv handle exists (even without render context).
bool HasMpvHandle() const { return mpv_ != nullptr; } bool HasMpvHandle() const { return mpv_ != nullptr; }
+4 -3
View File
@@ -123,8 +123,8 @@ static void mpv_plugin_handle_method_call(FlMethodChannel* channel,
response = FL_METHOD_RESPONSE(fl_method_success_response_new( response = FL_METHOD_RESPONSE(fl_method_success_response_new(
fl_value_new_int(mpv_texture_get_id(self->texture)))); fl_value_new_int(mpv_texture_get_id(self->texture))));
} else { } else {
// Create player if it was disposed // Create player if it was disposed or doesn't exist
if (!self->player) { if (!self->player || self->player->IsDisposed()) {
self->player = std::make_unique<mpv::MpvPlayer>(); self->player = std::make_unique<mpv::MpvPlayer>();
} }
@@ -161,7 +161,8 @@ static void mpv_plugin_handle_method_call(FlMethodChannel* channel,
} else if (strcmp(method, "dispose") == 0) { } else if (strcmp(method, "dispose") == 0) {
if (self->player) { if (self->player) {
self->player->Dispose(); self->player->Dispose();
self->player.reset(); // Don't reset player here — stray g_idle callbacks still reference it.
// It will be replaced on next initialize() call.
} }
if (self->texture) { if (self->texture) {
mpv_texture_dispose(self->texture); mpv_texture_dispose(self->texture);