diff options
author | Thomas Watson <twatson52@icloud.com> | 2022-10-20 20:55:29 -0500 |
---|---|---|
committer | Thomas Watson <twatson52@icloud.com> | 2022-10-20 22:15:04 -0500 |
commit | 8fb885a19e05bcdebc0215a85cbd623c4227d25b (patch) | |
tree | 9667cef2cea866845dfab7f0d07e1ddfcd221957 | |
parent | 8ad88f68a17a55e37b87f6be5b40f7e2ef9223d1 (diff) |
qt{5,6}: disable QML disk cache by default
Qt QML is a language for designing user interfaces. QML (and related JavaScript) source files are compiled into bytecode at runtime before execution. To save time, this bytecode is cached on disk. The cache is keyed primarily off the application name and mtime of the source file. Since application names rarely change, source files that have passed through the store have their mtimes fixed at 1, and SOURCE_DATE_EPOCH is also set to 1 during builds, compiled bytecode in this cache for Qt programs built by Nix is rarely or never invalidated. This results in programs running with QML bytecode that does not match their source code, which manifests as a constant source of random and unreproducible glitches, oddities, and crashes in Qt applications when they are upgraded. For applications like SDDM and Plasma, the user may be left with a completely broken system, and sometimes even rolling back doesn't fix the issue. This patch resolves the issue by simply patching Qt's QML module in all supported versions to disable the disk cache by default as if the environment variable QML_DISABLE_DISK_CACHE were always 1. Patching the code ensures consistent behavior for all Qt applications in all environments, including non-NixOS uses. Simple benchmarking suggests an approximate 10% CPU time penalty when launching QML-heavy applications, and no measurable penalty to booting into Plasma. This is considerably more benign than the earlier behavior. For testing or the performance conscious, use of the cache can be re-enabled after understanding the risks by setting the environment variable QML_FORCE_DISK_CACHE to 1. This can be done system-wide using e.g. the `environment.sessionVariables` NixOS option. Future work could change the cache key through source code changes or automatic generation of an appropriate SOURCE_DATE_EPOCH. Until then, this is a simple change which removes a large class of user frustration and headache for little penalty.
8 files changed, 98 insertions, 3 deletions
diff --git a/pkgs/development/libraries/qt-5/5.12/default.nix b/pkgs/development/libraries/qt-5/5.12/default.nix index 5d7b7202945f3..a93c2b67d43f5 100644 --- a/pkgs/development/libraries/qt-5/5.12/default.nix +++ b/pkgs/development/libraries/qt-5/5.12/default.nix @@ -81,7 +81,11 @@ let sha256 = "0crkw3j1iwdc1pbf5dhar0b4q3h5gs2q1sika8m12y02yk3ns697"; }) ]; - qtdeclarative = [ ./qtdeclarative.patch ]; + qtdeclarative = [ + ./qtdeclarative.patch + # prevent headaches from stale qmlcache data + ./qtdeclarative-default-disable-qmlcache.patch + ]; qtlocation = [ ./qtlocation-gcc-9.patch ]; qtscript = [ ./qtscript.patch ]; qtserialport = [ ./qtserialport.patch ]; diff --git a/pkgs/development/libraries/qt-5/5.12/qtdeclarative-default-disable-qmlcache.patch b/pkgs/development/libraries/qt-5/5.12/qtdeclarative-default-disable-qmlcache.patch new file mode 100644 index 0000000000000..e9c3b7e228427 --- /dev/null +++ b/pkgs/development/libraries/qt-5/5.12/qtdeclarative-default-disable-qmlcache.patch @@ -0,0 +1,40 @@ +diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp +index 9e5bc0b0..9219def6 100644 +--- a/src/qml/qml/qqmltypeloader.cpp ++++ b/src/qml/qml/qqmltypeloader.cpp +@@ -2151,7 +2151,7 @@ void QQmlTypeData::unregisterCallback(TypeDataCallback *callback) + + bool QQmlTypeData::tryLoadFromDiskCache() + { +- if (disableDiskCache() && !forceDiskCache()) ++ if (!forceDiskCache()) + return false; + + if (isDebugging()) +@@ -2658,7 +2658,7 @@ void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCach + return; + } + +- const bool trySaveToDisk = (!disableDiskCache() || forceDiskCache()) && !m_document->jsModule.debugMode && !typeRecompilation; ++ const bool trySaveToDisk = (forceDiskCache()) && !m_document->jsModule.debugMode && !typeRecompilation; + if (trySaveToDisk) { + QString errorString; + if (m_compiledData->saveToDisk(url(), &errorString)) { +@@ -3014,7 +3014,7 @@ QQmlRefPointer<QQmlScriptData> QQmlScriptBlob::scriptData() const + + void QQmlScriptBlob::dataReceived(const SourceCodeData &data) + { +- if (!disableDiskCache() || forceDiskCache()) { ++ if (forceDiskCache()) { + QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading(); + QString error; + if (unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) { +@@ -3077,7 +3077,7 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data) + qmlGenerator.generate(irUnit); + } + +- if ((!disableDiskCache() || forceDiskCache()) && !isDebugging()) { ++ if ((forceDiskCache()) && !isDebugging()) { + QString errorString; + if (unit->saveToDisk(url(), &errorString)) { + QString error; diff --git a/pkgs/development/libraries/qt-5/5.14/default.nix b/pkgs/development/libraries/qt-5/5.14/default.nix index 32630810b49cb..808562772f9ec 100644 --- a/pkgs/development/libraries/qt-5/5.14/default.nix +++ b/pkgs/development/libraries/qt-5/5.14/default.nix @@ -68,7 +68,11 @@ let ./qtbase.patch.d/0010-qtbase-assert.patch ./qtbase.patch.d/0011-fix-header_module.patch ]; - qtdeclarative = [ ./qtdeclarative.patch ]; + qtdeclarative = [ + ./qtdeclarative.patch + # prevent headaches from stale qmlcache data + ./qtdeclarative-default-disable-qmlcache.patch + ]; qtlocation = [ ./qtlocation-gcc-9.patch ]; qtscript = [ ./qtscript.patch ]; qtserialport = [ ./qtserialport.patch ]; diff --git a/pkgs/development/libraries/qt-5/5.14/qtdeclarative-default-disable-qmlcache.patch b/pkgs/development/libraries/qt-5/5.14/qtdeclarative-default-disable-qmlcache.patch new file mode 100644 index 0000000000000..8ef5ef53dea37 --- /dev/null +++ b/pkgs/development/libraries/qt-5/5.14/qtdeclarative-default-disable-qmlcache.patch @@ -0,0 +1,13 @@ +diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp +index 6c12de92..fc67dc07 100644 +--- a/src/qml/qml/qqmltypeloader.cpp ++++ b/src/qml/qml/qqmltypeloader.cpp +@@ -705,7 +705,7 @@ bool QQmlTypeLoader::Blob::isDebugging() const + + bool QQmlTypeLoader::Blob::diskCacheEnabled() const + { +- return (!disableDiskCache() || forceDiskCache()) && !isDebugging(); ++ return (forceDiskCache()) && !isDebugging(); + } + + bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &data, QList<QQmlError> *errors) diff --git a/pkgs/development/libraries/qt-5/5.15/default.nix b/pkgs/development/libraries/qt-5/5.15/default.nix index e3f94ba8ffefc..616bf22ff13a9 100644 --- a/pkgs/development/libraries/qt-5/5.15/default.nix +++ b/pkgs/development/libraries/qt-5/5.15/default.nix @@ -56,7 +56,11 @@ let ./qtbase.patch.d/0010-qtbase-assert.patch ./qtbase.patch.d/0011-fix-header_module.patch ]; - qtdeclarative = [ ./qtdeclarative.patch ]; + qtdeclarative = [ + ./qtdeclarative.patch + # prevent headaches from stale qmlcache data + ./qtdeclarative-default-disable-qmlcache.patch + ]; qtscript = [ ./qtscript.patch ]; qtserialport = [ ./qtserialport.patch ]; qtwebengine = lib.optionals stdenv.isDarwin [ diff --git a/pkgs/development/libraries/qt-5/5.15/qtdeclarative-default-disable-qmlcache.patch b/pkgs/development/libraries/qt-5/5.15/qtdeclarative-default-disable-qmlcache.patch new file mode 100644 index 0000000000000..03668d960c78d --- /dev/null +++ b/pkgs/development/libraries/qt-5/5.15/qtdeclarative-default-disable-qmlcache.patch @@ -0,0 +1,13 @@ +diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp +index 1d66e75..827567a 100644 +--- a/src/qml/qml/qqmltypeloader.cpp ++++ b/src/qml/qml/qqmltypeloader.cpp +@@ -727,7 +727,7 @@ bool QQmlTypeLoader::Blob::isDebugging() const + + bool QQmlTypeLoader::Blob::diskCacheEnabled() const + { +- return (!disableDiskCache() && !isDebugging()) || forceDiskCache(); ++ return forceDiskCache(); + } + + bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &data, QList<QQmlError> *errors) diff --git a/pkgs/development/libraries/qt-6/modules/qtdeclarative.nix b/pkgs/development/libraries/qt-6/modules/qtdeclarative.nix index c8abc71a22359..76231f866efee 100644 --- a/pkgs/development/libraries/qt-6/modules/qtdeclarative.nix +++ b/pkgs/development/libraries/qt-6/modules/qtdeclarative.nix @@ -16,6 +16,10 @@ qtModule { "-DQT6_INSTALL_PREFIX=${placeholder "out"}" "-DQT_INSTALL_PREFIX=${placeholder "out"}" ]; + patches = [ + # prevent headaches from stale qmlcache data + ../patches/qtdeclarative-default-disable-qmlcache.patch + ]; postInstall = '' substituteInPlace "$out/lib/cmake/Qt6Qml/Qt6QmlMacros.cmake" \ --replace ''\'''${QT6_INSTALL_PREFIX}' "$dev" diff --git a/pkgs/development/libraries/qt-6/patches/qtdeclarative-default-disable-qmlcache.patch b/pkgs/development/libraries/qt-6/patches/qtdeclarative-default-disable-qmlcache.patch new file mode 100644 index 0000000000000..02e578f2d523d --- /dev/null +++ b/pkgs/development/libraries/qt-6/patches/qtdeclarative-default-disable-qmlcache.patch @@ -0,0 +1,13 @@ +diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp +index 852cde9e..165f1b57 100644 +--- a/src/qml/jsruntime/qv4engine.cpp ++++ b/src/qml/jsruntime/qv4engine.cpp +@@ -2093,7 +2093,7 @@ void ExecutionEngine::registerModule(const QString &_name, const QJSValue &modul + + bool ExecutionEngine::diskCacheEnabled() const + { +- return (!disableDiskCache() && !debugger()) || forceDiskCache(); ++ return forceDiskCache(); + } + + void ExecutionEngine::callInContext(QV4::Function *function, QObject *self, |