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).
This commit is contained in:
2026-07-13 10:19:30 +08:00
parent 8d3bde5a50
commit 01f8be7267
2 changed files with 10 additions and 2 deletions
+8 -2
View File
@@ -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<bool> *task_watcher =
static_cast<QFutureWatcher<bool> *>(sender());
task_finished_ = true;
if (task_watcher->result()) {
emit TaskSucceeded(task_);
} else {
+2
View File
@@ -75,6 +75,8 @@ private:
bool already_shown_;
bool task_finished_;
private slots:
void TaskFinished();
};