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:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user