diff options
author | Alex Wilson <alex.wilson@nokia.com> | 2012-04-23 16:19:49 +1000 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-04-24 04:01:50 +0200 |
commit | 33ce4c290c12c038b8afec6a62a132bad80770a9 (patch) | |
tree | 68b15a01cb93378be83fc42e63788ddde0d91700 | |
parent | 8655cb5f9738ac03052e83140b6184df40318d73 (diff) | |
download | qtlocation-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.cpp | 81 | ||||
-rw-r--r-- | src/location/maps/qgeomapimages_p.h | 2 | ||||
-rw-r--r-- | src/location/maps/qgeotiledmapdata.cpp | 11 | ||||
-rw-r--r-- | src/location/maps/qgeotiledmapdata_p.h | 2 | ||||
-rw-r--r-- | src/location/maps/qgeotiledmapdata_p_p.h | 4 | ||||
-rw-r--r-- | src/location/maps/qgeotiledmappingmanagerengine.cpp | 21 | ||||
-rw-r--r-- | src/location/maps/qgeotilefetcher.cpp | 2 |
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); } |