diff options
| author | Shawn Rutledge <shawn.rutledge@qt.io> | 2024-09-17 09:11:52 +0200 |
|---|---|---|
| committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2024-10-03 21:46:02 +0200 |
| commit | c28fa0f768d512ca52dfd44249a19e99f55427db (patch) | |
| tree | 1301eb0fb35df7fd417fc5a49734db97badf323a /src/quick/util/qquickpixmapcache.cpp | |
| parent | de22477708ed3700d2f36d0121008b1cac4b837e (diff) | |
Use QPointer for QQuickPixmapData::specialDevice; abort if deleted
QQuickPdfPageImage::load() calls carrierFile() to get a QPdfFile
instance, a subclass of QIODevice, and calls
QQuickPixmap::loadImageFromDevice() which saves the device to
QQuickPixmapData::specialDevice, then calls
QQuickPixmapReader::startJob() which sends a ProcessJobs event.
The worker thread handles it in QQuickPixmapReader::processJob()
and calls readImage(), passing a simple QIODevice *dev. readImage()
relies on that pointer being valid until the work is done. However,
QQuickPdfDocument::setSource() may have called deleteLater().
We can detect it, and in that case there is no point in continuing this
render job. Also, change QPdfFile's thread affinity to the renderer
thread, so that deletion cannot happen until after readImage() is done
and the renderer thread has returned to its event loop.
Fixes: QTBUG-128875
Pick-to: 6.8 6.7 6.5
Change-Id: I1e8bf526c5596ce561d7bf4f7dea8d50f3c0eb18
Reviewed-by: Axel Spoerl <axel.spoerl@qt.io>
Diffstat (limited to 'src/quick/util/qquickpixmapcache.cpp')
| -rw-r--r-- | src/quick/util/qquickpixmapcache.cpp | 29 |
1 files changed, 22 insertions, 7 deletions
diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index 0bd7476465..5d7ff6efdd 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -303,7 +303,7 @@ class QQuickPixmapData public: QQuickPixmapData(const QUrl &u, const QRect &r, const QSize &rs, const QQuickImageProviderOptions &po, const QString &e) - : refCount(1), frameCount(1), frame(0), inCache(false), pixmapStatus(QQuickPixmap::Error), + : refCount(1), frameCount(1), frame(0), inCache(false), fromSpecialDevice(false), pixmapStatus(QQuickPixmap::Error), url(u), errorString(e), requestRegion(r), requestSize(rs), providerOptions(po), appliedTransform(QQuickImageProviderOptions::UsePluginDefaultTransform), textureFactory(nullptr), reply(nullptr), prevUnreferenced(nullptr), @@ -316,7 +316,7 @@ public: QQuickPixmapData(const QUrl &u, const QRect &r, const QSize &s, const QQuickImageProviderOptions &po, QQuickImageProviderOptions::AutoTransform aTransform, int frame=0, int frameCount=1) - : refCount(1), frameCount(frameCount), frame(frame), inCache(false), pixmapStatus(QQuickPixmap::Loading), + : refCount(1), frameCount(frameCount), frame(frame), inCache(false), fromSpecialDevice(false), pixmapStatus(QQuickPixmap::Loading), url(u), requestRegion(r), requestSize(s), providerOptions(po), appliedTransform(aTransform), textureFactory(nullptr), reply(nullptr), prevUnreferenced(nullptr), prevUnreferencedPtr(nullptr), @@ -330,7 +330,7 @@ public: QQuickPixmapData(const QUrl &u, QQuickTextureFactory *texture, const QSize &s, const QRect &r, const QSize &rs, const QQuickImageProviderOptions &po, QQuickImageProviderOptions::AutoTransform aTransform, int frame=0, int frameCount=1) - : refCount(1), frameCount(frameCount), frame(frame), inCache(false), pixmapStatus(QQuickPixmap::Ready), + : refCount(1), frameCount(frameCount), frame(frame), inCache(false), fromSpecialDevice(false), pixmapStatus(QQuickPixmap::Ready), url(u), implicitSize(s), requestRegion(r), requestSize(rs), providerOptions(po), appliedTransform(aTransform), textureFactory(texture), reply(nullptr), prevUnreferenced(nullptr), @@ -342,7 +342,7 @@ public: } QQuickPixmapData(QQuickTextureFactory *texture) - : refCount(1), frameCount(1), frame(0), inCache(false), pixmapStatus(QQuickPixmap::Ready), + : refCount(1), frameCount(1), frame(0), inCache(false), fromSpecialDevice(false), pixmapStatus(QQuickPixmap::Ready), appliedTransform(QQuickImageProviderOptions::UsePluginDefaultTransform), textureFactory(texture), reply(nullptr), prevUnreferenced(nullptr), prevUnreferencedPtr(nullptr), nextUnreferenced(nullptr) @@ -370,6 +370,7 @@ public: int frame; bool inCache:1; + bool fromSpecialDevice:1; QQuickPixmap::Status pixmapStatus; QUrl url; @@ -381,7 +382,7 @@ public: QQuickImageProviderOptions::AutoTransform appliedTransform; QColorSpace targetColorSpace; - QIODevice *specialDevice = nullptr; + QPointer<QIODevice> specialDevice; // actual image data, after loading QQuickTextureFactory *textureFactory; @@ -1005,9 +1006,22 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u QString errorStr; QSize readSize; - if (runningJob->data && runningJob->data->specialDevice) { + if (runningJob->data && runningJob->data->fromSpecialDevice) { + auto specialDevice = runningJob->data->specialDevice; + if (specialDevice.isNull() || QObjectPrivate::get(specialDevice.data())->deleteLaterCalled) { + qCDebug(lcImg) << "readImage job aborted" << url; + return; + } int frameCount; - if (!readImage(url, runningJob->data->specialDevice, &image, &errorStr, &readSize, &frameCount, + // Ensure that specialDevice's thread affinity is _this_ thread, to avoid deleteLater() + // deleting prematurely, before readImage() is done. But this is only possible if it has already + // relinquished its initial thread affinity. + if (!specialDevice->thread()) { + qCDebug(lcQsgLeak) << specialDevice.data() << ": changing thread affinity so that" + << QThread::currentThread() << "will handle any deleteLater() calls"; + specialDevice->moveToThread(QThread::currentThread()); + } + if (!readImage(url, specialDevice.data(), &image, &errorStr, &readSize, &frameCount, runningJob->requestRegion, runningJob->requestSize, runningJob->providerOptions, nullptr, runningJob->data->frame)) { errorCode = QQuickPixmapReply::Loading; @@ -1958,6 +1972,7 @@ void QQuickPixmap::loadImageFromDevice(QQmlEngine *engine, QIODevice *device, co d = new QQuickPixmapData(url, requestRegion, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame, frameCount); d->specialDevice = device; + d->fromSpecialDevice = true; d->addToCache(); QQuickPixmapReader::readerMutex.lock(); |
