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 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 QMutexLocker locker( &mManager->mMutex );
97 Q_ASSERT( mManager->mQueue.isEmpty() );
105 void QgsTileDownloadManagerReplyWorkerObject::replyFinished()
107 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;
118 data = reply->readAll();
122 ++mManager->mStats.networkRequestsFailed;
125 emit finished( data, reply->error(), reply->errorString() );
127 reply->deleteLater();
132 mManager->removeEntry( mRequest );
134 if ( mManager->mQueue.isEmpty() )
137 mManager->mWorker->startIdleTimer();
147 : mMutex( QMutex::Recursive )
159 QMutexLocker locker( &mMutex );
163 QgsDebugMsgLevel( QStringLiteral(
"Tile download manager: starting worker thread" ), 2 );
164 mWorkerThread =
new QThread;
166 mWorker->moveToThread( mWorkerThread );
167 QObject::connect( mWorkerThread, &QThread::finished, mWorker, &QObject::deleteLater );
168 mWorkerThread->start();
175 QgsTileDownloadManager::QueueEntry entry = findEntryForRequest( request );
176 if ( !entry.isValid() )
178 QgsDebugMsgLevel( QStringLiteral(
"Tile download manager: get (new entry): " ) + request.url().toString(), 2 );
180 entry.request = request;
182 entry.objWorker->moveToThread( mWorkerThread );
184 QObject::connect( entry.objWorker, &QgsTileDownloadManagerReplyWorkerObject::finished, reply, &QgsTileDownloadManagerReply::requestFinished );
190 QgsDebugMsgLevel( QStringLiteral(
"Tile download manager: get (existing entry): " ) + request.url().toString(), 2 );
192 QObject::connect( entry.objWorker, &QgsTileDownloadManagerReplyWorkerObject::finished, reply, &QgsTileDownloadManagerReply::requestFinished );
197 signalQueueModified();
204 QMutexLocker locker( &mMutex );
206 return !mQueue.isEmpty();
214 while ( msec == -1 || t.elapsed() < msec )
217 QMutexLocker locker( &mMutex );
218 if ( mQueue.isEmpty() )
221 QThread::currentThread()->usleep( 1000 );
230 QMutexLocker locker( &mMutex );
231 if ( !mWorkerThread )
235 mShuttingDown =
true;
236 signalQueueModified();
243 QMutexLocker locker( &mMutex );
244 if ( !mWorkerThread )
248 QThread::currentThread()->usleep( 1000 );
254 return mWorkerThread && mWorkerThread->isRunning();
259 QMutexLocker locker( &mMutex );
263 QgsTileDownloadManager::QueueEntry QgsTileDownloadManager::findEntryForRequest(
const QNetworkRequest &request )
265 for (
auto it = mQueue.constBegin(); it != mQueue.constEnd(); ++it )
267 if ( it->request.url() == request.url() )
270 return QgsTileDownloadManager::QueueEntry();
273 void QgsTileDownloadManager::addEntry(
const QgsTileDownloadManager::QueueEntry &entry )
275 for (
auto it = mQueue.constBegin(); it != mQueue.constEnd(); ++it )
277 Q_ASSERT( entry.request.url() != it->request.url() );
280 mQueue.append( entry );
283 void QgsTileDownloadManager::updateEntry(
const QgsTileDownloadManager::QueueEntry &entry )
285 for (
auto it = mQueue.begin(); it != mQueue.end(); ++it )
287 if ( entry.request.url() == it->request.url() )
296 void QgsTileDownloadManager::removeEntry(
const QNetworkRequest &request )
299 for (
auto it = mQueue.constBegin(); it != mQueue.constEnd(); ++it, ++i )
301 if ( it->request.url() == request.url() )
303 mQueue.removeAt( i );
310 void QgsTileDownloadManager::signalQueueModified()
312 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
313 QMetaObject::invokeMethod( mWorker,
"queueUpdated", Qt::QueuedConnection );
315 QMetaObject::invokeMethod( mWorker, &QgsTileDownloadManagerWorker::queueUpdated, Qt::QueuedConnection );
323 QgsTileDownloadManagerReply::QgsTileDownloadManagerReply(
QgsTileDownloadManager *manager,
const QNetworkRequest &request )
324 : mManager( manager )
325 , mRequest( request )
331 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)