From 01f8be7267b13412ed2b73a034d1fdfdb04d58dd Mon Sep 17 00:00:00 2001 From: Mike Solar Date: Sun, 12 Jul 2026 21:41:27 +0800 Subject: [PATCH] Fix crash when closing export dialog while export is running TaskDialog::closeEvent() was calling deleteLater() immediately when the user closed the window. The ExportTask was a child QObject of the dialog, so it got destroyed while its Run() method was still executing in the worker thread, leading to a use-after-free on encoder_->Close(). Defer deletion until the QFutureWatcher reports that the task has actually finished. A new task_finished_ flag tracks this so closeEvent only deletes when it is safe, and TaskFinished() closes the dialog (which now deletes instead of calling close() again while the task is still alive). --- app/dialog/task/task.cpp | 10 ++++++++-- app/dialog/task/task.h | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/dialog/task/task.cpp b/app/dialog/task/task.cpp index 7aa1fdb44..0b8774f6c 100644 --- a/app/dialog/task/task.cpp +++ b/app/dialog/task/task.cpp @@ -34,6 +34,7 @@ TaskDialog::TaskDialog(Task *task, const QString &title, QWidget *parent) , task_(task) , destroy_on_close_(true) , already_shown_(false) + , task_finished_(false) { // Clear task when this dialog is destroyed task_->setParent(this); @@ -84,8 +85,11 @@ void TaskDialog::closeEvent(QCloseEvent *e) // Reset shown already_shown_ = false; - // Clean up this task and dialog - if (destroy_on_close_) { + // Clean up this task and dialog, but only if the task has actually finished. + // If the user closes the window while the task is still running, deleting now + // would destroy the Task object out from under the worker thread and crash + // when the task later touches its own members (e.g. ExportTask::encoder_). + if (destroy_on_close_ && task_finished_) { deleteLater(); } } @@ -95,6 +99,8 @@ void TaskDialog::TaskFinished() QFutureWatcher *task_watcher = static_cast *>(sender()); + task_finished_ = true; + if (task_watcher->result()) { emit TaskSucceeded(task_); } else { diff --git a/app/dialog/task/task.h b/app/dialog/task/task.h index ded311dbf..d01fddced 100644 --- a/app/dialog/task/task.h +++ b/app/dialog/task/task.h @@ -75,6 +75,8 @@ private: bool already_shown_; + bool task_finished_; + private slots: void TaskFinished(); };