Fix thread-unsafe QStandardPaths calls in dataDir/configDir

QStandardPaths::writableLocation() is not thread-safe in Qt5.
ElementsCollectionModel::reload() launches:

  QtConcurrent::map(m_items_list_to_setUp, setUpData)

Each worker calls FileElementCollectionItem::setUpData()
→ collectionPath() → isCollectionRoot()
→ QETApp::userMacrosDir() → QETApp::dataDir()
→ QStandardPaths::writableLocation()  ← SIGSEGV (null deref)

The crash was confirmed by Valgrind (address 0x0, inside
libQt5Core's writableLocation internals).

Fix: replace the bare QStandardPaths calls in dataDir() and
configDir() with a C++11 static-local lambda.  The compiler
guarantees the lambda body runs exactly once across all threads
(magic statics, ISO C++11 §6.7).  After the first (main-thread)
call the result is returned lock-free.

Relates-to: #492 (same QtConcurrent lifetime pattern fixed in
QETProject::writeBackup by PR #512).
This commit is contained in:
Shane Ringrose
2026-06-20 20:09:48 +12:00
parent a8e2a7acff
commit f2d297b0d8
+16 -10
View File
@@ -887,11 +887,14 @@ QString QETApp::configDir()
#ifdef QET_ALLOW_OVERRIDE_CD_OPTION #ifdef QET_ALLOW_OVERRIDE_CD_OPTION
if (config_dir != QString()) return(config_dir); if (config_dir != QString()) return(config_dir);
#endif #endif
QString configdir = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); // C++11 static-local init runs exactly once across all threads — safe to
while (configdir.endsWith('/')) { // call from QtConcurrent background threads (QStandardPaths is not).
configdir.remove(configdir.length()-1, 1); static const QString cached = []() {
} QString d = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
return configdir; while (d.endsWith('/')) d.chop(1);
return d;
}();
return cached;
} }
/** /**
@@ -911,11 +914,14 @@ QString QETApp::dataDir()
#ifdef QET_ALLOW_OVERRIDE_DD_OPTION #ifdef QET_ALLOW_OVERRIDE_DD_OPTION
if (data_dir != QString()) return(data_dir); if (data_dir != QString()) return(data_dir);
#endif #endif
QString datadir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); // C++11 static-local init runs exactly once across all threads — safe to
while (datadir.endsWith('/')) { // call from QtConcurrent background threads (QStandardPaths is not).
datadir.remove(datadir.length()-1, 1); static const QString cached = []() {
} QString d = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
return datadir; while (d.endsWith('/')) d.chop(1);
return d;
}();
return cached;
} }
/** /**