fix: linux mpv dispose deadlock on wayland
This commit is contained in:
@@ -130,13 +130,27 @@ bool MpvPlayer::InitRenderContext() {
|
||||
}
|
||||
|
||||
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)) {
|
||||
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_);
|
||||
for (auto& pair : pending_commands_) {
|
||||
@@ -145,20 +159,14 @@ void MpvPlayer::Dispose() {
|
||||
pending_commands_.clear();
|
||||
}
|
||||
|
||||
// Clear mpv callbacks BEFORE freeing
|
||||
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
|
||||
// 5. Remove pending idle callbacks
|
||||
if (event_source_id_ != 0) {
|
||||
g_source_remove(event_source_id_);
|
||||
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_) {
|
||||
mpv_render_context_free(mpv_gl_);
|
||||
mpv_gl_ = nullptr;
|
||||
@@ -170,7 +178,6 @@ void MpvPlayer::Dispose() {
|
||||
}
|
||||
|
||||
observed_properties_.clear();
|
||||
redraw_callback_ = nullptr;
|
||||
}
|
||||
|
||||
void MpvPlayer::Command(const std::vector<std::string>& args) {
|
||||
|
||||
@@ -55,6 +55,9 @@ class MpvPlayer {
|
||||
/// Returns true if mpv is initialized (has both mpv handle and render context).
|
||||
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).
|
||||
bool HasMpvHandle() const { return mpv_ != nullptr; }
|
||||
|
||||
|
||||
@@ -123,8 +123,8 @@ static void mpv_plugin_handle_method_call(FlMethodChannel* channel,
|
||||
response = FL_METHOD_RESPONSE(fl_method_success_response_new(
|
||||
fl_value_new_int(mpv_texture_get_id(self->texture))));
|
||||
} else {
|
||||
// Create player if it was disposed
|
||||
if (!self->player) {
|
||||
// Create player if it was disposed or doesn't exist
|
||||
if (!self->player || self->player->IsDisposed()) {
|
||||
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) {
|
||||
if (self->player) {
|
||||
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) {
|
||||
mpv_texture_dispose(self->texture);
|
||||
|
||||
Reference in New Issue
Block a user