MapFetcher queuing refactoring
authorSami Rämö <sami.ramo@ixonos.com>
Fri, 30 Apr 2010 06:35:37 +0000 (09:35 +0300)
committerSami Rämö <sami.ramo@ixonos.com>
Fri, 30 Apr 2010 06:35:37 +0000 (09:35 +0300)
 - set MapFetcher queue size based on amount of tiles in the grid

 - re-factored MapFetcher queuing system

src/map/mapengine.cpp
src/map/mapfetcher.cpp
src/map/mapfetcher.h
src/src.pro

index 251a24f..f73cc09 100644 (file)
@@ -131,6 +131,8 @@ QRect MapEngine::calculateTileGrid(QPoint sceneCoordinate)
     int topLeftX = tileCoordinate.x() - (gridWidth/2);
     int topLeftY = tileCoordinate.y() - (gridHeight/2);
 
+    m_mapFetcher->setDownloadQueueSize(gridWidth * gridHeight);
+
     return QRect(topLeftX, topLeftY, gridWidth, gridHeight);
 }
 
index 0df9c75..e6e7416 100644 (file)
@@ -34,7 +34,8 @@
 
 MapFetcher::MapFetcher(QNetworkAccessManager *manager, QObject *parent)
     : QObject(parent)
-    , m_isFetchMapImagesTimerRunning(false)
+    , m_pendingRequestsSize(0)
+    , m_fetchMapImagesTimerRunning(false)
     , m_manager(manager)
 {
     QNetworkDiskCache *diskCache = new QNetworkDiskCache(this);
@@ -46,41 +47,89 @@ MapFetcher::MapFetcher(QNetworkAccessManager *manager, QObject *parent)
             downloadFinished(QNetworkReply*)));
 }
 
-void MapFetcher::enqueueFetchMapImage(QUrl url)
+void MapFetcher::downloadFinished(QNetworkReply *reply)
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    // check if new url is already in the list and move it to end
-    // or append new url if not found
-    int index;
-    if ((index = m_fetchMapImagesList.indexOf(url)) != -1)
-        m_fetchMapImagesList.move(index, m_fetchMapImagesList.size() - 1);
-    else
-        m_fetchMapImagesList << url;
+    if (reply->error() == QNetworkReply::NoError) {
+        QImage image;
+        QUrl url = reply->url();
 
-    // limit list size
-    if (m_fetchMapImagesList.size() > DOWNLOAD_QUEUE_SIZE)
-        m_fetchMapImagesList.removeFirst();
+        if (!image.load(reply, 0))
+            image = QImage();
 
-    if (!m_isFetchMapImagesTimerRunning) {
-        m_isFetchMapImagesTimerRunning = true;
-        QTimer::singleShot(0, this, SLOT(fetchMapImage()));
+        emit mapImageReceived(url, QPixmap::fromImage(image));
+    }
+    else {
+        emit error(reply->errorString());
     }
+
+    m_currentDownloads.removeAll(reply);
+    reply->deleteLater();
+    startNextDownload();
 }
 
-void MapFetcher::fetchMapImage()
+void MapFetcher::enqueueFetchMapImage(QUrl url)
+{
+    qDebug() << __PRETTY_FUNCTION__ << "url:" << url.toString();
+
+    // check if new request is already in the list and move it to the begin of the list...
+    bool found = false;
+    for (int i = 0; i < m_pendingRequests.size(); i++) {
+        if (m_pendingRequests[i].url == url) {
+            m_pendingRequests.move(i, 0);
+            found = true;
+            qDebug() << __PRETTY_FUNCTION__ << "URL was already found, moved to begin";
+            break;
+        }
+    }
+    // ...or add new request to the begining of the list
+    if (!found) {
+        qDebug() << __PRETTY_FUNCTION__ << "URL was added";
+        Request request;
+        request.cacheChecked = false;
+        request.url = url;
+        m_pendingRequests.prepend(request);
+    }
+
+    limitPendingRequestsListSize();
+
+    if (!m_fetchMapImagesTimerRunning) {
+        m_fetchMapImagesTimerRunning = true;
+        QTimer::singleShot(0, this, SLOT(checkNextRequestFromCache()));
+    }
+}
+
+int MapFetcher::newestRequestIndex(bool cacheChecked)
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    if (!m_fetchMapImagesList.isEmpty()) {
-        QUrl url = m_fetchMapImagesList.takeLast();
+    if (!m_pendingRequests.isEmpty()) {
+        for (int i = 0; i < m_pendingRequests.size(); i++) {
+            if (m_pendingRequests[i].cacheChecked == cacheChecked) {
+                return i;
+            }
+        }
+    }
+
+    return -1;
+}
+
+void MapFetcher::checkNextRequestFromCache()
+{
+    qDebug() << __PRETTY_FUNCTION__;
 
-        if (!url.isEmpty() && url.isValid()) {
-            if (!loadImageFromCache(url)) {
-                if (m_downloadQueue.size() >= DOWNLOAD_QUEUE_SIZE)
-                    m_downloadQueue.dequeue();
+    int i = newestRequestIndex(false);
 
-                m_downloadQueue.enqueue(url);
+    if (i != -1) {
+        if (!m_pendingRequests[i].url.isEmpty() && m_pendingRequests[i].url.isValid()) {
+            if (loadImageFromCache(m_pendingRequests[i].url)) {
+                // was found, remove from the list
+                m_pendingRequests.removeAt(i);
+            }
+            else {
+                // didn't found from cache so mark cache checked and leave to queue
+                m_pendingRequests[i].cacheChecked = true;
 
                 if (m_currentDownloads.size() < MAX_PARALLEL_DOWNLOADS)
                     startNextDownload();
@@ -88,11 +137,20 @@ void MapFetcher::fetchMapImage()
         }
     }
 
-    // schedule fetching of the next tile if the list is not empty
-    if (!m_fetchMapImagesList.isEmpty())
-        QTimer::singleShot(0, this, SLOT(fetchMapImage()));
+    // schedule checking of the next request if the list is not empty
+    if (newestRequestIndex(false) != -1)
+        QTimer::singleShot(0, this, SLOT(checkNextRequestFromCache()));
     else
-        m_isFetchMapImagesTimerRunning = false;
+        m_fetchMapImagesTimerRunning = false;
+}
+
+void MapFetcher::limitPendingRequestsListSize()
+{
+    qDebug() << __PRETTY_FUNCTION__;
+
+    while (m_pendingRequests.size() > m_pendingRequestsSize) {
+        m_pendingRequests.removeLast();
+    }
 }
 
 bool MapFetcher::loadImageFromCache(const QUrl &url)
@@ -132,40 +190,27 @@ bool MapFetcher::loadImageFromCache(const QUrl &url)
     return imageFound;
 }
 
-void MapFetcher::startNextDownload()
+void MapFetcher::setDownloadQueueSize(int size)
 {
-    qDebug() << __PRETTY_FUNCTION__;
+    qDebug() << __PRETTY_FUNCTION__ << "size:" << size;
 
-    if (m_downloadQueue.isEmpty())
-        return;
-
-    QUrl url = m_downloadQueue.dequeue();
-
-    QNetworkRequest request(url);
-    request.setRawHeader("User-Agent", "Situare");
-    QNetworkReply *reply = m_manager->get(request);
-
-    m_currentDownloads.append(reply);
+    m_pendingRequestsSize = size;
+    limitPendingRequestsListSize();
 }
 
-void MapFetcher::downloadFinished(QNetworkReply *reply)
+void MapFetcher::startNextDownload()
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    if (reply->error() == QNetworkReply::NoError) {
-        QImage image;
-        QUrl url = reply->url();
+    int i = newestRequestIndex(true);
 
-        if (!image.load(reply, 0))
-            image = QImage();
+    if (i != -1) {
+        QUrl url = m_pendingRequests.takeAt(i).url;
 
-        emit mapImageReceived(url, QPixmap::fromImage(image));
-    }
-    else {
-        emit error(reply->errorString());
-    }
+        QNetworkRequest request(url);
+        request.setRawHeader("User-Agent", "Situare");
+        QNetworkReply *reply = m_manager->get(request);
 
-    m_currentDownloads.removeAll(reply);
-    reply->deleteLater();
-    startNextDownload();
+        m_currentDownloads.append(reply);
+    }
 }
index f2f5c70..ede0ee0 100644 (file)
@@ -26,8 +26,6 @@
 #include <QtCore>
 #include <QNetworkAccessManager>
 
-#include "mapfetcher.h"
-
 class QNetworkReply;
 class QUrl;
 
@@ -40,6 +38,16 @@ class MapFetcher : public QObject
 {
     Q_OBJECT
 
+    /**
+    * @brief Struct for download requests
+    *
+    * @typedef Request
+    */
+    typedef struct _Request {
+        bool cacheChecked; ///< Is this request already checked from the cache
+        QUrl url; ///< URL
+    } Request;
+
 public:
     /**
     * @brief Constructor for MapFetcher.
@@ -52,6 +60,14 @@ public:
 /*******************************************************************************
  * CLASS SPECIFIC MEMBER FUNCTIONS AND SLOTS
  ******************************************************************************/
+public:
+    /**
+    * @brief Set size of the download queue
+    *
+    * @param size New size
+    */
+    void setDownloadQueueSize(int size);
+
 public slots:
 
     /**
@@ -65,6 +81,13 @@ public slots:
     void enqueueFetchMapImage(QUrl url);
 
 private:
+
+    /**
+    * @brief Limit pending requests list size to m_pendingRequestsSize
+    *
+    */
+    void limitPendingRequestsListSize();
+
     /**
     * @brief Loads image from cache if it's available and emits imageReveived
     * signal with url and image. If image is in cache returns true, false
@@ -75,6 +98,18 @@ private:
     */
     bool loadImageFromCache(const QUrl &url);
 
+    /**
+    * @brief Find first item based on criteria if the request is already checked from the cache
+    *
+    * If cacheChecked is true, then returns index of the first request which is already
+    * checked from the cache. If cacheChecked is false then returns first item which
+    * isn't checked from the cache. Returns -1 if the item is not found.
+    *
+    * @param cacheChecked Search criteria
+    * @return Index of the first matching request, or -1 if not found.
+    */
+    int newestRequestIndex(bool cacheChecked);
+
 private slots:
 
     /**
@@ -87,11 +122,14 @@ private slots:
     void downloadFinished(QNetworkReply *reply);
 
     /**
-    * @brief Fetch next map image.
+    * @brief Check next request if it is found from cache
     *
-    * Next queued request is taken from the queue and fetching is started
+    * Next queued request, which is not already checked against cache, is taken
+    * from the queue and checked against cache. If not found from cache, then
+    * cache checked flag is set. New download is started if there aren't too
+    * many simultaneus downloads already running.
     */
-    void fetchMapImage();
+    void checkNextRequestFromCache();
 
     /**
     * @brief This slot is called when next download is started. Takes url
@@ -125,13 +163,12 @@ signals:
  ******************************************************************************/
 private:
     static const int MAX_PARALLEL_DOWNLOADS = 2; ///< Max simultaneous parallel downloads
-    static const int DOWNLOAD_QUEUE_SIZE = 50; ///< Max downloads waiting in queue
 
     QList<QNetworkReply*> m_currentDownloads; ///< List of current downloads
-    QQueue<QUrl> m_downloadQueue;             ///< Queue of pending requests
-    QList<QUrl> m_fetchMapImagesList; ///< "Queue" for map image fetching requests
-    bool m_isFetchMapImagesTimerRunning; ///< is the singleshot timer already running
-    QNetworkAccessManager *m_manager;       ///< Network access manager
+    int m_pendingRequestsSize; ///< Max number of pending requests
+    bool m_fetchMapImagesTimerRunning; ///< is the singleshot timer already running
+    QNetworkAccessManager *m_manager; ///< Network access manager
+    QList<Request> m_pendingRequests; ///< List of map image fetching requests
 };
 
 #endif
index f2606f4..3a4eace 100644 (file)
@@ -61,13 +61,15 @@ HEADERS += ui/mainwindow.h \
 QT += network \
     webkit
 
+DEFINES += QT_NO_DEBUG_OUTPUT
+
 !maemo5 { 
     message(QJson built in)
     message(Make sure you have QJson development headers installed)
     message(install headers with: sudo apt-get install libqjson-dev)
 }
 
-maemo5 {
+maemo5 { 
     message(QJson built in)
     message(Make sure you have QJson development headers installed)
     message(add: deb http://repository.maemo.org/extras-devel fremantle free non-free)