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 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
148 : mMutex( QMutex::Recursive )
161 QMutexLocker locker( &mMutex );
165 QgsDebugMsgLevel( QStringLiteral(
"Tile download manager: starting worker thread" ), 2 );
166 mWorkerThread =
new QThread;
168 mWorker->moveToThread( mWorkerThread );
169 QObject::connect( mWorkerThread, &QThread::finished, mWorker, &QObject::deleteLater );
170 mWorkerThread->start();
177 QgsTileDownloadManager::QueueEntry entry = findEntryForRequest( request );
178 if ( !entry.isValid() )
180 QgsDebugMsgLevel( QStringLiteral(
"Tile download manager: get (new entry): " ) + request.url().toString(), 2 );
182 entry.request = request;
184 entry.objWorker->moveToThread( mWorkerThread );
186 QObject::connect( entry.objWorker, &QgsTileDownloadManagerReplyWorkerObject::finished, reply, &QgsTileDownloadManagerReply::requestFinished );
192 QgsDebugMsgLevel( QStringLiteral(
"Tile download manager: get (existing entry): " ) + request.url().toString(), 2 );
194 QObject::connect( entry.objWorker, &QgsTileDownloadManagerReplyWorkerObject::finished, reply, &QgsTileDownloadManagerReply::requestFinished );
199 signalQueueModified();
206 QMutexLocker locker( &mMutex );
208 return !mQueue.isEmpty();
216 while ( msec == -1 || t.elapsed() < msec )
219 QMutexLocker locker( &mMutex );
220 if ( mQueue.isEmpty() )
223 QThread::currentThread()->usleep( 1000 );
232 QMutexLocker locker( &mMutex );
233 if ( !mWorkerThread )
237 mShuttingDown =
true;
238 signalQueueModified();
245 QMutexLocker locker( &mMutex );
246 if ( !mWorkerThread )
250 QThread::currentThread()->usleep( 1000 );
256 return mWorkerThread && mWorkerThread->isRunning();
261 QMutexLocker locker( &mMutex );
265 QgsTileDownloadManager::QueueEntry QgsTileDownloadManager::findEntryForRequest(
const QNetworkRequest &request )
267 for (
auto it = mQueue.constBegin(); it != mQueue.constEnd(); ++it )
269 if ( it->request.url() == request.url() )
272 return QgsTileDownloadManager::QueueEntry();
275 void QgsTileDownloadManager::addEntry(
const QgsTileDownloadManager::QueueEntry &entry )
277 for (
auto it = mQueue.constBegin(); it != mQueue.constEnd(); ++it )
279 Q_ASSERT( entry.request.url() != it->request.url() );
282 mQueue.append( entry );
285 void QgsTileDownloadManager::updateEntry(
const QgsTileDownloadManager::QueueEntry &entry )
287 for (
auto it = mQueue.begin(); it != mQueue.end(); ++it )
289 if ( entry.request.url() == it->request.url() )
298 void QgsTileDownloadManager::removeEntry(
const QNetworkRequest &request )
301 for (
auto it = mQueue.constBegin(); it != mQueue.constEnd(); ++it, ++i )
303 if ( it->request.url() == request.url() )
305 mQueue.removeAt( i );
312 void QgsTileDownloadManager::signalQueueModified()
314 QMetaObject::invokeMethod( mWorker, &QgsTileDownloadManagerWorker::queueUpdated, Qt::QueuedConnection );
321 QgsTileDownloadManagerReply::QgsTileDownloadManagerReply(
QgsTileDownloadManager *manager,
const QNetworkRequest &request )
322 : mManager( manager )
323 , mRequest( request )
329 QMutexLocker locker( &mManager->mMutex );
333 QgsDebugMsgLevel( QStringLiteral(
"Tile download manager: reply deleted before finished: " ) + mRequest.url().toString(), 2 );
339 void QgsTileDownloadManagerReply::requestFinished( QByteArray data, QNetworkReply::NetworkError error,
const QString &errorString )
341 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)