QPixmapCache: fix leaking of QStrings and Keys on clear()

QPixmapCache maintains a mapping from QString to QPixmapCache::Key, in
the form of the cacheKeys QHash, but QPixmapCache::clear() didn't
touch it, leading to the string data (as well as the Keys) being
retained after any possible use. This can lead to memory slowly being
eaten up, as reported in QTBUG-112200, and prevents a periodic calling
of QPixmapCache::clear() from being a work-around for the issue in the
bug report.

Fix by clearing cacheKeys in QPixmapCache::clear().

This is designed as a low-risk enabler of a work-around, not a fix for
the issue. The work-around enabled by this is periodic calling of
QPixmapCache::clear().

[ChangeLog][QtGui][QPixmapCache] Fixed QString key data not being
freed on clear().

Pick-to: 6.6 6.5 6.2 5.15
Task-number: QTBUG-112200
Change-Id: Ica6fa0e27e1b47b8df58d5e996378a2ececa5f9c
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Marc Mutz 2023-06-05 22:15:14 +02:00
parent d390ac99f2
commit 6ab0d25a09
2 changed files with 37 additions and 0 deletions

View File

@ -439,6 +439,7 @@ void QPMCache::clear()
killTimer(theid);
theid = 0;
}
cacheKeys.clear();
}
QPixmapCache::KeyData* QPMCache::getKeyData(QPixmapCache::Key *key)

View File

@ -13,6 +13,8 @@ Q_AUTOTEST_EXPORT int qt_qpixmapcache_qpixmapcache_total_used();
Q_AUTOTEST_EXPORT int q_QPixmapCache_keyHashSize();
QT_END_NAMESPACE
using namespace Qt::StringLiterals;
class tst_QPixmapCache : public QObject
{
Q_OBJECT
@ -34,6 +36,7 @@ private slots:
void clear();
void pixmapKey();
void noLeak();
void clearDoesNotLeakStringKeys();
void strictCacheLimit();
void noCrashOnLargeInsert();
};
@ -478,6 +481,39 @@ void tst_QPixmapCache::noLeak()
QCOMPARE(oldSize, newSize);
}
void tst_QPixmapCache::clearDoesNotLeakStringKeys()
{
QPixmapCache::setCacheLimit(20); // 20KiB
//
// GIVEN: a QPixmap with QString key `key` in QPixmapCache
//
QString key;
{
QPixmap pm(64, 64);
QCOMPARE_LT(pm.width() * pm.height() * std::ceil(pm.depth() / 8.0),
QPixmapCache::cacheLimit() * 1024);
pm.fill(Qt::transparent);
key = u"theKey"_s.repeated(20); // avoid eventual QString SSO
QVERIFY(key.isDetached());
QPixmapCache::insert(key, pm);
}
QVERIFY(!key.isDetached()); // was saved inside QPixmapCache
//
// WHEN: clearing the cache:
//
QPixmapCache::clear();
//
// THEN: `key` is no longer referenced by QPixmapCache:
//
QVERIFY(key.isDetached());
// verify that the pixmap is really gone from the cache
// (do it after the key check, because QPixmapCache cleans up `key` on a failed lookup)
QPixmap r;
QVERIFY(!QPixmapCache::find(key, &r));
}
void tst_QPixmapCache::strictCacheLimit()
{