From 0e3c592205d867b881af3bccb20fed4e0fb95100 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Thu, 2 Jul 2026 12:43:10 +0200 Subject: [PATCH] fix(windows): marshal mpv events onto the platform thread close #1453 Events were delivered to the Flutter event sink directly on the mpv event thread while method replies were already marshaled; concurrent messenger use from two threads intermittently crashed guests during the event burst of an in-place media switch. Also joins the event thread before draining platform tasks in the plugin destructor (reverse member destruction order would otherwise let the event thread enqueue into a destroyed queue) and coalesces the wakeup PostMessage behind a retry-safe flag. --- windows/runner/mpv/mpv_plugin.cpp | 32 ++++++++++++++++++++++++++----- windows/runner/mpv/mpv_plugin.h | 1 + 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/windows/runner/mpv/mpv_plugin.cpp b/windows/runner/mpv/mpv_plugin.cpp index c732c2ad..71b01f94 100644 --- a/windows/runner/mpv/mpv_plugin.cpp +++ b/windows/runner/mpv/mpv_plugin.cpp @@ -55,6 +55,14 @@ MpvPlayerPlugin::MpvPlayerPlugin(flutter::PluginRegistrarWindows* registrar) } MpvPlayerPlugin::~MpvPlayerPlugin() { + // Join the mpv event thread before draining: it enqueues platform tasks, + // and platform_tasks_/platform_tasks_mutex_ are destroyed before player_ + // (reverse declaration order). + if (player_) { + player_->Dispose(); + player_.reset(); + } + DrainPlatformTasks(); // Unregister window proc delegate. @@ -74,13 +82,21 @@ void MpvPlayerPlugin::PostToPlatformThread(std::function task) { return; } + bool post_wakeup = false; { std::lock_guard lock(platform_tasks_mutex_); platform_tasks_.push(std::move(task)); + if (!wakeup_posted_ && flutter_window_) { + wakeup_posted_ = true; + post_wakeup = true; + } } - if (flutter_window_) { - ::PostMessage(flutter_window_, kPlatformTaskMessage, 0, 0); + if (post_wakeup && !::PostMessage(flutter_window_, kPlatformTaskMessage, 0, 0)) { + // Wakeup lost (e.g. message queue full during a log storm); let the next + // enqueue retry instead of stranding the queue. + std::lock_guard lock(platform_tasks_mutex_); + wakeup_posted_ = false; } } @@ -89,6 +105,7 @@ void MpvPlayerPlugin::DrainPlatformTasks() { { std::lock_guard lock(platform_tasks_mutex_); tasks.swap(platform_tasks_); + wakeup_posted_ = false; } while (!tasks.empty()) { @@ -450,9 +467,14 @@ void MpvPlayerPlugin::HandleMethodCall( } void MpvPlayerPlugin::SendEvent(const flutter::EncodableValue& event) { - if (event_sink_) { - event_sink_->Success(event); - } + // mpv events arrive on the mpv event thread; Flutter channel APIs are + // platform-thread-only, so marshal onto the platform thread (the sink + // null-check then also runs on the same thread as onListen/onCancel). + PostToPlatformThread([this, event]() { + if (event_sink_) { + event_sink_->Success(event); + } + }); } } // namespace mpv diff --git a/windows/runner/mpv/mpv_plugin.h b/windows/runner/mpv/mpv_plugin.h index 72430f3d..376f1531 100644 --- a/windows/runner/mpv/mpv_plugin.h +++ b/windows/runner/mpv/mpv_plugin.h @@ -53,6 +53,7 @@ class MpvPlayerPlugin : public flutter::Plugin { std::optional proc_id_; std::mutex platform_tasks_mutex_; std::queue> platform_tasks_; + bool wakeup_posted_ = false; // guarded by platform_tasks_mutex_ }; } // namespace mpv