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.
This commit is contained in:
edde746
2026-07-02 12:43:10 +02:00
parent 5f49dddb4d
commit 0e3c592205
2 changed files with 28 additions and 5 deletions
+27 -5
View File
@@ -55,6 +55,14 @@ MpvPlayerPlugin::MpvPlayerPlugin(flutter::PluginRegistrarWindows* registrar)
} }
MpvPlayerPlugin::~MpvPlayerPlugin() { 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(); DrainPlatformTasks();
// Unregister window proc delegate. // Unregister window proc delegate.
@@ -74,13 +82,21 @@ void MpvPlayerPlugin::PostToPlatformThread(std::function<void()> task) {
return; return;
} }
bool post_wakeup = false;
{ {
std::lock_guard<std::mutex> lock(platform_tasks_mutex_); std::lock_guard<std::mutex> lock(platform_tasks_mutex_);
platform_tasks_.push(std::move(task)); platform_tasks_.push(std::move(task));
if (!wakeup_posted_ && flutter_window_) {
wakeup_posted_ = true;
post_wakeup = true;
}
} }
if (flutter_window_) { if (post_wakeup && !::PostMessage(flutter_window_, kPlatformTaskMessage, 0, 0)) {
::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<std::mutex> lock(platform_tasks_mutex_);
wakeup_posted_ = false;
} }
} }
@@ -89,6 +105,7 @@ void MpvPlayerPlugin::DrainPlatformTasks() {
{ {
std::lock_guard<std::mutex> lock(platform_tasks_mutex_); std::lock_guard<std::mutex> lock(platform_tasks_mutex_);
tasks.swap(platform_tasks_); tasks.swap(platform_tasks_);
wakeup_posted_ = false;
} }
while (!tasks.empty()) { while (!tasks.empty()) {
@@ -450,9 +467,14 @@ void MpvPlayerPlugin::HandleMethodCall(
} }
void MpvPlayerPlugin::SendEvent(const flutter::EncodableValue& event) { void MpvPlayerPlugin::SendEvent(const flutter::EncodableValue& event) {
if (event_sink_) { // mpv events arrive on the mpv event thread; Flutter channel APIs are
event_sink_->Success(event); // 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 } // namespace mpv
+1
View File
@@ -53,6 +53,7 @@ class MpvPlayerPlugin : public flutter::Plugin {
std::optional<int32_t> proc_id_; std::optional<int32_t> proc_id_;
std::mutex platform_tasks_mutex_; std::mutex platform_tasks_mutex_;
std::queue<std::function<void()>> platform_tasks_; std::queue<std::function<void()>> platform_tasks_;
bool wakeup_posted_ = false; // guarded by platform_tasks_mutex_
}; };
} // namespace mpv } // namespace mpv