mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-06-20 05:34:12 +02:00
Fix #492: wait for async backup before destroying QETProject
writeBackup() fires QtConcurrent::run(QET::writeToFile, ..., &m_backup_file) fire-and-forget: the QFuture was discarded and nothing kept m_backup_file alive until the worker finished. If the QETProject was destroyed first, the worker wrote through the freed member -> use-after-free crash in QET::writeToFile (intermittent; ~1/6 on short-lived CLI runs). Store the QFuture and waitForFinished() in ~QETProject (and before setFilePath() re-points the managed backup file). Also skip launching a new backup while one is still running, so two threads never write m_backup_file at once. The Qt6 path is still a TODO stub and the QtConcurrent block is KF5-only, so this affects only the Qt5/KF5 build that actually has the backup code.
This commit is contained in:
+12
-1
@@ -137,6 +137,11 @@ QETProject::QETProject(KAutoSaveFile *backup, QObject *parent) :
|
|||||||
*/
|
*/
|
||||||
QETProject::~QETProject()
|
QETProject::~QETProject()
|
||||||
{
|
{
|
||||||
|
//Wait for any in-flight async crash-recovery backup to finish: the worker
|
||||||
|
//writes through &m_backup_file, a member that would otherwise be destroyed
|
||||||
|
//under it (issue #492).
|
||||||
|
m_backup_future.waitForFinished();
|
||||||
|
|
||||||
//We block database signal to avoid hundreds of unnecessary emitted signal
|
//We block database signal to avoid hundreds of unnecessary emitted signal
|
||||||
//due to deletion (diagram, item, etc...) and as much update made in the not yet deleted things.
|
//due to deletion (diagram, item, etc...) and as much update made in the not yet deleted things.
|
||||||
m_data_base.blockSignals(true);
|
m_data_base.blockSignals(true);
|
||||||
@@ -339,6 +344,8 @@ void QETProject::setFilePath(const QString &filepath)
|
|||||||
}
|
}
|
||||||
#ifdef BUILD_WITHOUT_KF5
|
#ifdef BUILD_WITHOUT_KF5
|
||||||
#else
|
#else
|
||||||
|
//Don't close/re-point the backup file while a backup is still writing it.
|
||||||
|
m_backup_future.waitForFinished();
|
||||||
if (m_backup_file.isOpen()) {
|
if (m_backup_file.isOpen()) {
|
||||||
m_backup_file.close();
|
m_backup_file.close();
|
||||||
}
|
}
|
||||||
@@ -1809,8 +1816,12 @@ void QETProject::writeBackup()
|
|||||||
#ifdef BUILD_WITHOUT_KF5
|
#ifdef BUILD_WITHOUT_KF5
|
||||||
#else
|
#else
|
||||||
# if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
|
# if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove
|
||||||
|
//Don't launch a new backup while the previous one is still writing:
|
||||||
|
//both would write through &m_backup_file on different threads.
|
||||||
|
if (m_backup_future.isRunning())
|
||||||
|
return;
|
||||||
QDomDocument xml_project(toXml());
|
QDomDocument xml_project(toXml());
|
||||||
QtConcurrent::run(
|
m_backup_future = QtConcurrent::run(
|
||||||
QET::writeToFile,xml_project,&m_backup_file,nullptr);
|
QET::writeToFile,xml_project,&m_backup_file,nullptr);
|
||||||
# else
|
# else
|
||||||
# if TODO_LIST
|
# if TODO_LIST
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QFuture>
|
||||||
|
|
||||||
class Diagram;
|
class Diagram;
|
||||||
class ElementsLocation;
|
class ElementsLocation;
|
||||||
@@ -295,6 +296,7 @@ class QETProject : public QObject
|
|||||||
bool m_freeze_new_conductors = false;
|
bool m_freeze_new_conductors = false;
|
||||||
QTimer m_save_backup_timer,
|
QTimer m_save_backup_timer,
|
||||||
m_autosave_timer;
|
m_autosave_timer;
|
||||||
|
QFuture<bool> m_backup_future;
|
||||||
#ifdef BUILD_WITHOUT_KF5
|
#ifdef BUILD_WITHOUT_KF5
|
||||||
#else
|
#else
|
||||||
KAutoSaveFile m_backup_file;
|
KAutoSaveFile m_backup_file;
|
||||||
|
|||||||
Reference in New Issue
Block a user