summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Wilson <alex.wilson@nokia.com>2012-04-23 16:19:49 +1000
committerQt by Nokia <qt-info@nokia.com>2012-04-24 04:01:50 +0200
commit33ce4c290c12c038b8afec6a62a132bad80770a9 (patch)
tree68b15a01cb93378be83fc42e63788ddde0d91700
parent8655cb5f9738ac03052e83140b6184df40318d73 (diff)
downloadqtlocation-33ce4c290c12c038b8afec6a62a132bad80770a9.tar.gz
Retry failed tile loads 5 times (with exp backoff) before giving up
This allows us to fetch tiles over unreliable network links a bit better -- burst errors of only a few seconds will no longer drop tiles. Change-Id: I00c33424b58e032b1fb74c89052ea7f5cbd97d30 Reviewed-by: Thomas Lowe <thomas.lowe@nokia.com> Reviewed-by: Ian Chen <ian.1.chen@nokia.com> Reviewed-by: Alex Wilson <alex.wilson@nokia.com>
-rw-r--r--src/location/maps/qgeomapimages.cpp81
-rw-r--r--src/location/maps/qgeomapimages_p.h2
-rw-r--r--src/location/maps/qgeotiledmapdata.cpp11
-rw-r--r--src/location/maps/qgeotiledmapdata_p.h2
-rw-r--r--src/location/maps/qgeotiledmapdata_p_p.h4
-rw-r--r--src/location/maps/qgeotiledmappingmanagerengine.cpp21
-rw-r--r--src/location/maps/qgeotilefetcher.cpp2
7 files changed, 123 insertions, 0 deletions
diff --git a/src/location/maps/qgeomapimages.cpp b/src/location/maps/qgeomapimages.cpp
index 3f38a898..3997865a 100644
--- a/src/location/maps/qgeomapimages.cpp
+++ b/src/location/maps/qgeomapimages.cpp
@@ -40,6 +40,7 @@
****************************************************************************/
#include "qgeomapimages_p.h"
+#include <QSharedPointer>
#include "qgeotilespec.h"
#include "qgeotiledmapdata_p.h"
#include "qgeotiledmappingmanagerengine.h"
@@ -47,6 +48,8 @@
QT_BEGIN_NAMESPACE
+class RetryFuture;
+
class QGeoMapImagesPrivate
{
public:
@@ -58,7 +61,10 @@ public:
void setVisibleTiles(const QSet<QGeoTileSpec> &tiles);
void tileFetched(const QGeoTileSpec &tile);
+ void tileError(const QGeoTileSpec &tile, const QString &errorString);
+ QHash<QGeoTileSpec, int> retries_;
+ QHash<QGeoTileSpec, QSharedPointer<RetryFuture> > futures_;
QSet<QGeoTileSpec> visible_;
QList<QSharedPointer<QGeoTileTexture> > cachedTex_;
QSet<QGeoTileSpec> requested_;
@@ -78,6 +84,12 @@ void QGeoMapImages::setVisibleTiles(const QSet<QGeoTileSpec> &tiles)
d->setVisibleTiles(tiles);
}
+void QGeoMapImages::tileError(const QGeoTileSpec &tile, const QString &errorString)
+{
+ Q_D(QGeoMapImages);
+ d->tileError(tile, errorString);
+}
+
QList<QSharedPointer<QGeoTileTexture> > QGeoMapImages::cachedTiles() const
{
Q_D(const QGeoMapImages);
@@ -132,6 +144,71 @@ void QGeoMapImagesPrivate::setVisibleTiles(const QSet<QGeoTileSpec> &tiles)
if (!requestTiles.isEmpty() || !cancelTiles.isEmpty()) {
if (map_) {
map_->updateTileRequests(requestTiles, cancelTiles);
+
+ // Remove any cancelled tiles from the error retry hash to avoid
+ // re-using the numbers for a totally different request cycle.
+ iter i = cancelTiles.constBegin();
+ iter end = cancelTiles.constEnd();
+ for (; i != end; ++i) {
+ retries_.remove(*i);
+ futures_.remove(*i);
+ }
+ }
+ }
+}
+
+// Represents a tile that needs to be retried after a certain period of time
+class RetryFuture : public QObject
+{
+ Q_OBJECT
+public:
+ RetryFuture(const QGeoTileSpec &tile, QGeoTiledMapData *map, QObject *parent=0);
+
+public slots:
+ void retry();
+
+private:
+ QGeoTileSpec tile_;
+ QGeoTiledMapData *map_;
+};
+
+RetryFuture::RetryFuture(const QGeoTileSpec &tile, QGeoTiledMapData *map, QObject *parent)
+ : QObject(parent), tile_(tile), map_(map)
+{}
+
+void RetryFuture::retry()
+{
+ QSet<QGeoTileSpec> requestTiles;
+ QSet<QGeoTileSpec> cancelTiles;
+ requestTiles.insert(tile_);
+ if (map_)
+ map_->updateTileRequests(requestTiles, cancelTiles);
+}
+
+void QGeoMapImagesPrivate::tileError(const QGeoTileSpec &tile, const QString &errorString)
+{
+ if (requested_.contains(tile)) {
+ int count = retries_.value(tile, 0);
+ retries_.insert(tile, count + 1);
+
+ if (count >= 5) {
+ qWarning("QGeoMapImages: Failed to fetch tile (%d,%d,%d) 5 times, giving up. "
+ "Last error message was: '%s'",
+ tile.x(), tile.y(), tile.zoom(), qPrintable(errorString));
+ requested_.remove(tile);
+ retries_.remove(tile);
+ futures_.remove(tile);
+
+ } else {
+ // Exponential time backoff when retrying
+ int delay = (1 << count) * 500;
+
+ QSharedPointer<RetryFuture> future(new RetryFuture(tile, map_));
+ futures_.insert(tile, future);
+
+ QTimer::singleShot(delay, future.data(), SLOT(retry()));
+ // Passing .data() to singleShot is ok -- Qt will clean up the
+ // connection if the target qobject is deleted
}
}
}
@@ -139,6 +216,10 @@ void QGeoMapImagesPrivate::setVisibleTiles(const QSet<QGeoTileSpec> &tiles)
void QGeoMapImagesPrivate::tileFetched(const QGeoTileSpec &tile)
{
requested_.remove(tile);
+ retries_.remove(tile);
+ futures_.remove(tile);
}
+#include "qgeomapimages.moc"
+
QT_END_NAMESPACE
diff --git a/src/location/maps/qgeomapimages_p.h b/src/location/maps/qgeomapimages_p.h
index 02ceb15e..f5adea20 100644
--- a/src/location/maps/qgeomapimages_p.h
+++ b/src/location/maps/qgeomapimages_p.h
@@ -55,6 +55,7 @@
#include <QSet>
#include <QList>
#include <QSharedPointer>
+#include <QString>
QT_BEGIN_NAMESPACE
@@ -76,6 +77,7 @@ public:
QList<QSharedPointer<QGeoTileTexture> > cachedTiles() const;
void tileFetched(const QGeoTileSpec &tile);
+ void tileError(const QGeoTileSpec &tile, const QString &errorString);
private:
QGeoMapImagesPrivate *d_ptr;
diff --git a/src/location/maps/qgeotiledmapdata.cpp b/src/location/maps/qgeotiledmapdata.cpp
index 89eec6c4..4e9eec29 100644
--- a/src/location/maps/qgeotiledmapdata.cpp
+++ b/src/location/maps/qgeotiledmapdata.cpp
@@ -150,6 +150,12 @@ void QGeoTiledMapData::tileFetched(const QGeoTileSpec &spec)
d->tileFetched(spec);
}
+void QGeoTiledMapData::tileError(const QGeoTileSpec &spec, const QString &errorString)
+{
+ Q_D(QGeoTiledMapData);
+ d->tileError(spec, errorString);
+}
+
QGeoTileCache* QGeoTiledMapData::tileCache()
{
Q_D(QGeoTiledMapData);
@@ -349,6 +355,11 @@ void QGeoTiledMapDataPrivate::tileFetched(const QGeoTileSpec &spec)
map_->update();
}
+void QGeoTiledMapDataPrivate::tileError(const QGeoTileSpec &spec, const QString &errorString)
+{
+ mapImages_->tileError(spec, errorString);
+}
+
QSet<QGeoTileSpec> QGeoTiledMapDataPrivate::visibleTiles()
{
return visibleTiles_;
diff --git a/src/location/maps/qgeotiledmapdata_p.h b/src/location/maps/qgeotiledmapdata_p.h
index 4313f1c1..521c5131 100644
--- a/src/location/maps/qgeotiledmapdata_p.h
+++ b/src/location/maps/qgeotiledmapdata_p.h
@@ -53,6 +53,7 @@
//
#include <QObject>
+#include <QString>
#include "qgeomapdata_p.h"
#include "qgeocameradata_p.h"
@@ -80,6 +81,7 @@ public:
void paintGL(QGLPainter *painter);
void tileFetched(const QGeoTileSpec &spec);
+ void tileError(const QGeoTileSpec &spec, const QString &errorString);
QGeoCoordinate screenPositionToCoordinate(const QPointF &pos, bool clipToViewport = true) const;
QPointF coordinateToScreenPosition(const QGeoCoordinate &coordinate, bool clipToViewport = true) const;
diff --git a/src/location/maps/qgeotiledmapdata_p_p.h b/src/location/maps/qgeotiledmapdata_p_p.h
index 97f83949..f2091667 100644
--- a/src/location/maps/qgeotiledmapdata_p_p.h
+++ b/src/location/maps/qgeotiledmapdata_p_p.h
@@ -78,6 +78,9 @@ class QGeoProjection;
class QGeoCameraTiles;
class QGeoMapImages;
class QGeoMapGeometry;
+class QGeoTiledMapData;
+class QGeoTiledMappingManagerEngine;
+class QGLPainter;
class QGLSceneNode;
@@ -99,6 +102,7 @@ public:
QPointF coordinateToScreenPosition(const QGeoCoordinate &coordinate) const;
void tileFetched(const QGeoTileSpec &spec);
+ void tileError(const QGeoTileSpec &spec, const QString &errorString);
QSet<QGeoTileSpec> visibleTiles();
QGeoTiledMappingManagerEngine *engine() const;
diff --git a/src/location/maps/qgeotiledmappingmanagerengine.cpp b/src/location/maps/qgeotiledmappingmanagerengine.cpp
index 4d05951e..1f44d640 100644
--- a/src/location/maps/qgeotiledmappingmanagerengine.cpp
+++ b/src/location/maps/qgeotiledmappingmanagerengine.cpp
@@ -264,6 +264,27 @@ void QGeoTiledMappingManagerEngine::engineTileFinished(const QGeoTileSpec &spec,
void QGeoTiledMappingManagerEngine::engineTileError(const QGeoTileSpec &spec, const QString &errorString)
{
+ Q_D(QGeoTiledMappingManagerEngine);
+
+ QSet<QGeoTiledMapData*> maps = d->tileHash_.value(spec);
+ typedef QSet<QGeoTiledMapData*>::const_iterator map_iter;
+ map_iter map = maps.constBegin();
+ map_iter mapEnd = maps.constEnd();
+ for (; map != mapEnd; ++map) {
+ QSet<QGeoTileSpec> tileSet = d->mapHash_.value(*map);
+
+ tileSet.remove(spec);
+ if (tileSet.isEmpty())
+ d->mapHash_.remove(*map);
+ else
+ d->mapHash_.insert(*map, tileSet);
+ }
+ d->tileHash_.remove(spec);
+
+ for (map = maps.constBegin(); map != mapEnd; ++map) {
+ (*map)->tileError(spec, errorString);
+ }
+
emit tileError(spec, errorString);
}
diff --git a/src/location/maps/qgeotilefetcher.cpp b/src/location/maps/qgeotilefetcher.cpp
index 8080706c..5f9fc479 100644
--- a/src/location/maps/qgeotilefetcher.cpp
+++ b/src/location/maps/qgeotilefetcher.cpp
@@ -130,6 +130,8 @@ void QGeoTileFetcher::cancelTileRequests(const QSet<QGeoTileSpec> &tiles)
if (reply) {
d->invmap_.remove(*tile);
reply->abort();
+ if (reply->isFinished())
+ reply->deleteLater();
}
d->queue_.removeAll(*tile);
}