From 2d597c0e1a32dda3f4d2f1c941b8a0d2a4be4735 Mon Sep 17 00:00:00 2001 From: Eirik Aavitsland Date: Mon, 17 Dec 2018 11:12:50 +0100 Subject: QPixmapCache: guard against usage from non-main threads Depending on the active QPA plugin, QPixmaps may now be created and used also in non-main threads. But QPixmapCache is not designed to be used from such threads, so add guards to ignore such access attempts, both from application code and from Qt library code. Such unsafe access would often cause a cryptical "~QObject: Timers cannot be stopped from another thread" warning; that also disappears with this fix. [ChangeLog][QtGui][QPixmapCache] Ignore unsafe access from non-main threads Task-number: QTBUG-76694 Task-number: QTBUG-72523 Change-Id: Ia2db37e528aec08bfb48808630bdf5e543689039 Reviewed-by: Volker Hilsheimer --- src/gui/image/qpixmapcache.cpp | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/gui/image/qpixmapcache.cpp b/src/gui/image/qpixmapcache.cpp index 3d1652f68b..e01a245cff 100644 --- a/src/gui/image/qpixmapcache.cpp +++ b/src/gui/image/qpixmapcache.cpp @@ -42,6 +42,8 @@ #include "qobject.h" #include "qdebug.h" #include "qpixmapcache_p.h" +#include "qthread.h" +#include "qcoreapplication.h" QT_BEGIN_NAMESPACE @@ -83,6 +85,9 @@ QT_BEGIN_NAMESPACE with QPixmapCache} explains how to use QPixmapCache to speed up applications by caching the results of painting. + \note QPixmapCache is only usable from the application's main thread. + Access from other threads will be ignored and return failure. + \sa QCache, QPixmap */ @@ -98,6 +103,14 @@ static inline int cost(const QPixmap &pixmap) return static_cast(qBound(1LL, costKb, costMax)); } +static inline bool qt_pixmapcache_thread_test() +{ + if (Q_LIKELY(QCoreApplication::instance() && QThread::currentThread() == QCoreApplication::instance()->thread())) + return true; + + return false; +} + /*! \class QPixmapCache::Key \brief The QPixmapCache::Key class can be used for efficient access @@ -487,6 +500,8 @@ QPixmapCacheEntry::~QPixmapCacheEntry() QPixmap *QPixmapCache::find(const QString &key) { + if (!qt_pixmapcache_thread_test()) + return nullptr; return pm_cache()->object(key); } @@ -515,6 +530,8 @@ bool QPixmapCache::find(const QString &key, QPixmap& pixmap) bool QPixmapCache::find(const QString &key, QPixmap* pixmap) { + if (!qt_pixmapcache_thread_test()) + return false; QPixmap *ptr = pm_cache()->object(key); if (ptr && pixmap) *pixmap = *ptr; @@ -532,6 +549,8 @@ bool QPixmapCache::find(const QString &key, QPixmap* pixmap) */ bool QPixmapCache::find(const Key &key, QPixmap* pixmap) { + if (!qt_pixmapcache_thread_test()) + return false; //The key is not valid anymore, a flush happened before probably if (!key.d || !key.d->isValid) return false; @@ -563,6 +582,8 @@ bool QPixmapCache::find(const Key &key, QPixmap* pixmap) bool QPixmapCache::insert(const QString &key, const QPixmap &pixmap) { + if (!qt_pixmapcache_thread_test()) + return false; return pm_cache()->insert(key, pixmap, cost(pixmap)); } @@ -583,6 +604,8 @@ bool QPixmapCache::insert(const QString &key, const QPixmap &pixmap) */ QPixmapCache::Key QPixmapCache::insert(const QPixmap &pixmap) { + if (!qt_pixmapcache_thread_test()) + return QPixmapCache::Key(); return pm_cache()->insert(pixmap, cost(pixmap)); } @@ -597,6 +620,8 @@ QPixmapCache::Key QPixmapCache::insert(const QPixmap &pixmap) */ bool QPixmapCache::replace(const Key &key, const QPixmap &pixmap) { + if (!qt_pixmapcache_thread_test()) + return false; //The key is not valid anymore, a flush happened before probably if (!key.d || !key.d->isValid) return false; @@ -626,6 +651,8 @@ int QPixmapCache::cacheLimit() void QPixmapCache::setCacheLimit(int n) { + if (!qt_pixmapcache_thread_test()) + return; pm_cache()->setMaxCost(n); } @@ -634,6 +661,8 @@ void QPixmapCache::setCacheLimit(int n) */ void QPixmapCache::remove(const QString &key) { + if (!qt_pixmapcache_thread_test()) + return; pm_cache()->remove(key); } @@ -645,6 +674,8 @@ void QPixmapCache::remove(const QString &key) */ void QPixmapCache::remove(const Key &key) { + if (!qt_pixmapcache_thread_test()) + return; //The key is not valid anymore, a flush happened before probably if (!key.d || !key.d->isValid) return; @@ -657,6 +688,8 @@ void QPixmapCache::remove(const Key &key) void QPixmapCache::clear() { + if (!QCoreApplication::closingDown() && !qt_pixmapcache_thread_test()) + return; QT_TRY { if (pm_cache.exists()) pm_cache->clear(); -- cgit v1.2.3