fix: linux mpv dispose deadlock on wayland
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user