23 #include <QElapsedTimer>
24 #include <QNetworkReply>
29 QgsTileDownloadManagerWorker::QgsTileDownloadManagerWorker(
QgsTileDownloadManager *manager, QObject *parent )
34 connect( &mIdleTimer, &QTimer::timeout,
this, &QgsTileDownloadManagerWorker::idleTimerTimeout );
37 void QgsTileDownloadManagerWorker::startIdleTimer()
39 if ( !mIdleTimer.isActive() )
41 mIdleTimer.start( mManager->mIdleThreadTimeoutMs );
45 void QgsTileDownloadManagerWorker::queueUpdated()
47 const QMutexLocker locker( &mManager->mMutex );
49 if ( mManager->mShuttingDown )
51 for (
auto it = mManager->mQueue.begin(); it != mManager->mQueue.end(); ++it )
53 it->networkReply->abort();
60 if ( mIdleTimer.isActive() && !mManager->mQueue.isEmpty() )
66 for (
auto it = mManager->mQueue.begin(); it != mManager->mQueue.end(); ++it )
68 if ( !it->networkReply )
70 QgsDebugMsgLevel( QStringLiteral(
"Tile download manager: starting request: " ) + it->request.url().toString(), 2 );
74 connect( it->networkReply, &QNetworkReply::finished, it->objWorker, &QgsTileDownloadManagerReplyWorkerObject::replyFinished );
76 ++mManager->mStats.networkRequestsStarted;
81 void QgsTileDownloadManagerWorker::quitThread()
83 QgsDebugMsgLevel( QStringLiteral(
"Tile download manager: stopping worker thread" ), 2 );
85 mManager->mWorker->deleteLater();
86 mManager->mWorker =
nullptr;
89 mManager->mWorkerThread->quit();
90 mManager->mWorkerThread =
nullptr;
91 mManager->mShuttingDown =
false;
94 void QgsTileDownloadManagerWorker::idleTimerTimeout()
96 const QMutexLocker locker( &mManager->mMutex );
97 Q_ASSERT( mManager->mQueue.isEmpty() );
105 void QgsTileDownloadManagerReplyWorkerObject::replyFinished()
107 const QMutexLocker locker( &mManager->mMutex );
109 QgsDebugMsgLevel( QStringLiteral(
"Tile download manager: internal reply finished: " ) + mRequest.url().toString(), 2 );
111 QNetworkReply *reply = qobject_cast<QNetworkReply *>( sender() );
114 if ( reply->error() == QNetworkReply::NoError )
116 ++mManager->mStats.networkRequestsOk;
117 data = reply->readAll();
121 ++mManager->mStats.networkRequestsFailed;
122 const QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
123 if ( contentType.startsWith( QLatin1String(
"text/plain" ) ) )
124 data = reply->readAll();
127 emit finished( data, reply->error(), reply->errorString() );
129 reply->deleteLater();
134 mManager->removeEntry( mRequest );
136 if ( mManager->mQueue.isEmpty() )
139 mManager->mWorker->startIdleTimer();
149 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
150 : mMutex( QMutex::Recursive )
163 const QMutexLocker locker( &mMutex );
167 QgsDebugMsgLevel( QStringLiteral(
"Tile download manager: starting worker thread" ), 2 );
168 mWorkerThread =
new QThread;
170 mWorker->moveToThread( mWorkerThread );
171 QObject::connect( mWorkerThread, &QThread::finished, mWorker, &QObject::deleteLater );
172 mWorkerThread->start();
179 QgsTileDownloadManager::QueueEntry entry = findEntryForRequest( request );
180 if ( !entry.isValid() )
182 QgsDebugMsgLevel( QStringLiteral(
"Tile download manager: get (new entry): " ) + request.url().toString(), 2 );
184 entry.request = request;
186 entry.objWorker->moveToThread( mWorkerThread );
188 QObject::connect( entry.objWorker, &QgsTileDownloadManagerReplyWorkerObject::finished, reply, &QgsTileDownloadManagerReply::requestFinished );
194 QgsDebugMsgLevel( QStringLiteral(
"Tile download manager: get (existing entry): " ) + request.url().toString(), 2 );
196 QObject::connect( entry.objWorker, &QgsTileDownloadManagerReplyWorkerObject::finished, reply, &QgsTileDownloadManagerReply::requestFinished );
201 signalQueueModified();
208 const QMutexLocker locker( &mMutex );
210 return !mQueue.isEmpty();
218 while ( msec == -1 || t.elapsed() < msec )
221 const QMutexLocker locker( &mMutex );
222 if ( mQueue.isEmpty() )
225 QThread::usleep( 1000 );
234 const QMutexLocker locker( &mMutex );
235 if ( !mWorkerThread )
239 mShuttingDown =
true;
240 signalQueueModified();
247 const QMutexLocker locker( &mMutex );
248 if ( !mWorkerThread )
252 QThread::usleep( 1000 );
258 return mWorkerThread && mWorkerThread->isRunning();
263 const QMutexLocker locker( &mMutex );
267 QgsTileDownloadManager::QueueEntry QgsTileDownloadManager::findEntryForRequest(
const QNetworkRequest &request )
269 for (
auto it = mQueue.constBegin(); it != mQueue.constEnd(); ++it )
271 if ( it->request.url() == request.url() )
274 return QgsTileDownloadManager::QueueEntry();
277 void QgsTileDownloadManager::addEntry(
const QgsTileDownloadManager::QueueEntry &entry )
279 for (
auto it = mQueue.constBegin(); it != mQueue.constEnd(); ++it )
281 Q_ASSERT( entry.request.url() != it->request.url() );
284 mQueue.append( entry );
287 void QgsTileDownloadManager::updateEntry(
const QgsTileDownloadManager::QueueEntry &entry )
289 for (
auto it = mQueue.begin(); it != mQueue.end(); ++it )
291 if ( entry.request.url() == it->request.url() )
300 void QgsTileDownloadManager::removeEntry(
const QNetworkRequest &request )
303 for (
auto it = mQueue.constBegin(); it != mQueue.constEnd(); ++it, ++i )
305 if ( it->request.url() == request.url() )
307 mQueue.removeAt( i );
314 void QgsTileDownloadManager::signalQueueModified()
316 QMetaObject::invokeMethod( mWorker, &QgsTileDownloadManagerWorker::queueUpdated, Qt::QueuedConnection );
323 QgsTileDownloadManagerReply::QgsTileDownloadManagerReply(
QgsTileDownloadManager *manager,
const QNetworkRequest &request )
324 : mManager( manager )
325 , mRequest( request )
331 const QMutexLocker locker( &mManager->mMutex );
335 QgsDebugMsgLevel( QStringLiteral(
"Tile download manager: reply deleted before finished: " ) + mRequest.url().toString(), 2 );
341 void QgsTileDownloadManagerReply::requestFinished( QByteArray data, QNetworkReply::NetworkError error,
const QString &errorString )
343 QgsDebugMsgLevel( QStringLiteral(
"Tile download manager: reply finished: " ) + mRequest.url().toString(), 2 );
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
Reply object for tile download manager requests returned from calls to QgsTileDownloadManager::get().
QString errorString() const
Returns error string (only valid when already finished)
~QgsTileDownloadManagerReply()
QByteArray data() const
Returns binary data returned in the reply (only valid when already finished)
QNetworkReply::NetworkError error() const
Returns error code (only valid when already finished)
void finished()
Emitted when the reply has finished (either with a success or with a failure)
Encapsulates any statistics we would like to keep about requests.
int requestsMerged
How many requests were same as some other pending request and got "merged".
int requestsEarlyDeleted
How many requests were deleted early by the client (i.e. lost interest)
int requestsTotal
How many requests were done through the download manager.
Tile download manager handles downloads of map tiles for the purpose of map rendering.
bool hasWorkerThreadRunning() const
Returns whether the worker thread is running currently (it may be stopped if there were no requests r...
friend class QgsTileDownloadManagerReplyWorkerObject
bool waitForPendingRequests(int msec=-1)
Blocks the current thread until the queue is empty.
QgsTileDownloadManagerReply * get(const QNetworkRequest &request)
Starts a request.
friend class QgsTileDownloadManagerReply
bool hasPendingRequests() const
Returns whether there are any pending requests in the queue.
void resetStatistics()
Resets statistics of numbers of queries handled by this class.
friend class QgsTileDownloadManagerWorker
~QgsTileDownloadManager()
void shutdown()
Asks the worker thread to stop and blocks until it is not stopped.
#define QgsDebugMsgLevel(str, level)