on xenial help sign in
Make CroppedImageSizer async

QImageReader can block waiting on disk I/O so let's not have it on the main thread. CroppedImageSizerAsyncWorker is using a QFuture via QtConcurrent::run to get a thread and do the work. CroppedImageSizerAsyncWorker is created by CroppedImageSizer but deletes itself, either when the work has been finished or when it's aborted by CroppedImageSizer. This is why it's held with a QPointer in CroppedImageSizer. Fixes: #1384374 Approved by: Michał Sawicz, Michael Zanetti

commit54f979a662bbe0c8c01bf08345a87b872e15a3e7 authorAlbert Astals <albert.astals@canonical.com> dateTue 2 Dec 2014, 09:24 (12 years ago) parent422592fb5be6d1
5 files changed+148−31
plugins/Dash/CMakeLists.txt+2−2 plugins/Dash/croppedimagesizer.cpp+21−26 plugins/Dash/croppedimagesizer.h+4−3 plugins/Dash/croppedimagesizerasyncworker.cpp+74 plugins/Dash/croppedimagesizerasyncworker.h+47
plugins/Dash/CMakeLists.txt+2−2
@@ -11,7 +11,6 @@ include_directories(
1111 ${CMAKE_CURRENT_SOURCE_DIR}
1212 ${CMAKE_CURRENT_BINARY_DIR}
1313 ${Qt5Qml_PRIVATE_INCLUDE_DIRS}
14− ${Qt5Quick_INCLUDE_DIRS}
1514 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
1615 ${Qt5V8_PRIVATE_INCLUDE_DIR}
1716)
@@ -26,6 +25,7 @@ set(QMLPLUGIN_SRC
2625 horizontaljournal.cpp
2726 organicgrid.cpp
2827 croppedimagesizer.cpp
28+ croppedimagesizerasyncworker.cpp
2929 )
3030
3131add_library(Dash-qml MODULE
@@ -37,6 +37,6 @@ target_link_libraries(Dash-qml
3737 ${Qt5Quick_LIBRARIES}
3838 )
3939
40−qt5_use_modules(Dash-qml Qml Quick)
40+qt5_use_modules(Dash-qml Qml Quick Concurrent)
4141
4242add_unity8_plugin(Dash 0.1 Dash TARGETS Dash-qml)
plugins/Dash/croppedimagesizer.cpp+21−26
@@ -16,17 +16,18 @@
1616
1717#include "croppedimagesizer.h"
1818
19−#include <QDebug>
20−#include <QNetworkReply>
19+#include "croppedimagesizerasyncworker.h"
20+
21+#include <QNetworkAccessManager>
2122#include <QNetworkRequest>
2223#include <QQmlEngine>
2324#include <QQuickItem>
24−#include <QQuickView>
2525
2626CroppedImageSizer::CroppedImageSizer()
2727 : m_width(0),
2828 m_height(0),
29− m_sourceSize(QSize(-1, -1))
29+ m_sourceSize(QSize(-1, -1)),
30+ m_worker(nullptr)
3031{
3132 connect(this, &CroppedImageSizer::inputParamsChanged, this, &CroppedImageSizer::calculateSourceSize);
3233 connect(this, &CroppedImageSizer::sourceChanged, this, &CroppedImageSizer::requestImage);
@@ -34,8 +35,8 @@ CroppedImageSizer::CroppedImageSizer()
3435
3536CroppedImageSizer::~CroppedImageSizer()
3637{
37− if (m_reply) {
38− m_reply->deleteLater();
38+ if (m_worker) {
39+ m_worker->abort();
3940 }
4041}
4142
@@ -91,38 +92,32 @@ void CroppedImageSizer::setSourceSize(const QSize &sourceSize)
9192 }
9293}
9394
95+void CroppedImageSizer::setImageSize(const QSize &imageSize)
96+{
97+ m_imageSize = imageSize;
98+ m_worker = nullptr;
99+ calculateSourceSize();
100+}
101+
94102void CroppedImageSizer::requestImage()
95103{
96− if (m_reply) {
97− m_reply->disconnect();
98− m_reply->abort();
99− m_reply->deleteLater();
100− m_reply = nullptr;
104+ if (m_worker) {
105+ m_worker->abort();
106+ m_worker = nullptr;
101107 }
102108
103109 if (m_source.isValid() && qmlEngine(this) && qmlEngine(this)->networkAccessManager()) {
104110 QNetworkRequest request(m_source);
105− m_reply = qmlEngine(this)->networkAccessManager()->get(request);
106− connect(m_reply, &QNetworkReply::finished, this, &CroppedImageSizer::requestFinished);
107− } else {
111+ QNetworkReply *reply = qmlEngine(this)->networkAccessManager()->get(request);
112+ m_worker = new CroppedImageSizerAsyncWorker(this, reply);
113+ } else {
108114 setSourceSize(QSize(-1, -1));
109115 }
110116}
111117
112−void CroppedImageSizer::requestFinished()
113−{
114− QImageReader m_reader(m_reply);
115− m_imageSize = m_reader.size();
116−
117− m_reply->deleteLater();
118− m_reply = nullptr;
119−
120− calculateSourceSize();
121−}
122−
123118void CroppedImageSizer::calculateSourceSize()
124119{
125− if (m_source.isValid() && m_width > 0 && m_height > 0 && !m_reply) {
120+ if (m_source.isValid() && m_width > 0 && m_height > 0 && !m_worker) {
126121 if (!m_imageSize.isEmpty()) {
127122 const qreal ar = m_width / m_height;
128123 const qreal ssar = m_imageSize.width() / (qreal)m_imageSize.height();
plugins/Dash/croppedimagesizer.h+4−3
@@ -22,7 +22,7 @@
2222#include <QSize>
2323#include <QUrl>
2424
25−class QNetworkReply;
25+class CroppedImageSizerAsyncWorker;
2626
2727class CroppedImageSizer : public QObject
2828{
@@ -49,6 +49,8 @@ public:
4949 QSize sourceSize() const;
5050 void setSourceSize(const QSize &sourceSize);
5151
52+ Q_INVOKABLE void setImageSize(const QSize &imageSize);
53+
5254Q_SIGNALS:
5355 void inputParamsChanged();
5456 void sourceChanged();
@@ -56,7 +58,6 @@ Q_SIGNALS:
5658
5759private Q_SLOT:
5860 void calculateSourceSize();
59− void requestFinished();
6061 void requestImage();
6162
6263private:
@@ -65,7 +66,7 @@ private:
6566 qreal m_height;
6667 QSize m_sourceSize;
6768 QSize m_imageSize;
68− QNetworkReply *m_reply = nullptr;
69+ QPointer<CroppedImageSizerAsyncWorker> m_worker;
6970};
7071
7172#endif
plugins/Dash/croppedimagesizerasyncworker.cppnew file+74
@@ -0,0 +1,74 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "croppedimagesizerasyncworker.h"
18+
19+#include "croppedimagesizer.h"
20+
21+#include <QNetworkReply>
22+#include <QtConcurrentRun>
23+
24+CroppedImageSizerAsyncWorker::CroppedImageSizerAsyncWorker(CroppedImageSizer *sizer, QNetworkReply *reply)
25+ : m_sizer(sizer),
26+ m_reply(reply),
27+ m_ignoreAbort(false)
28+{
29+ connect(m_reply, &QNetworkReply::finished, this, &CroppedImageSizerAsyncWorker::requestFinished);
30+}
31+
32+void CroppedImageSizerAsyncWorker::abort()
33+{
34+ // This runs on main thread
35+ QMutexLocker locker(&m_mutex);
36+ m_sizer = nullptr;
37+ // If we already started the future run we can't abort the reply
38+ // since we don't know at which stage the future is, just let it finish
39+ if (!m_ignoreAbort) {
40+ QMetaObject::invokeMethod(m_reply, "abort", Qt::QueuedConnection);
41+ }
42+}
43+
44+void CroppedImageSizerAsyncWorker::requestFinished()
45+{
46+ // This runs on main thread
47+ QMutexLocker locker(&m_mutex);
48+ if (m_sizer) {
49+ m_ignoreAbort = true;
50+ QtConcurrent::run(processRequestFinished, this);
51+ } else {
52+ // We were aborted delete ourselves
53+ m_reply->deleteLater();
54+ deleteLater();
55+ }
56+}
57+
58+void CroppedImageSizerAsyncWorker::processRequestFinished(CroppedImageSizerAsyncWorker *worker)
59+{
60+ // This runs on non main thread
61+ // m_reply has finished at this point and is protected against change by m_ignoreAbort
62+ QImageReader reader(worker->m_reply);
63+ const QSize imageSize = reader.size();
64+
65+ worker->m_mutex.lock();
66+ if (worker->m_sizer) {
67+ QMetaObject::invokeMethod(worker->m_sizer, "setImageSize", Qt::QueuedConnection, Q_ARG(QSize, imageSize));
68+ }
69+ worker->m_mutex.unlock();
70+
71+ // All work is done, delete ourselves
72+ worker->m_reply->deleteLater();
73+ worker->deleteLater();
74+}
plugins/Dash/croppedimagesizerasyncworker.hnew file+47
@@ -0,0 +1,47 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef CROPPEDIMAGESIZERASYNCWORKER_H
18+#define CROPPEDIMAGESIZERASYNCWORKER_H
19+
20+#include <QMutex>
21+#include <QObject>
22+
23+class CroppedImageSizer;
24+
25+class QNetworkReply;
26+
27+class CroppedImageSizerAsyncWorker : public QObject
28+{
29+ Q_OBJECT
30+public:
31+ CroppedImageSizerAsyncWorker(CroppedImageSizer *sizer, QNetworkReply *reply);
32+
33+ void abort();
34+
35+private Q_SLOTS:
36+ void requestFinished();
37+
38+private:
39+ static void processRequestFinished(CroppedImageSizerAsyncWorker *worker);
40+
41+ QMutex m_mutex;
42+ CroppedImageSizer *m_sizer;
43+ QNetworkReply *m_reply;
44+ bool m_ignoreAbort;
45+};
46+
47+#endif