From f8cc50683ad52cff702fb3b324502c5e792917a0 Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Wed, 27 May 2015 18:34:06 +0300 Subject: [PATCH] Notify tile parsing errors The tile parsing is done in a worker thread and if it fails, we set the tile state to "obsolete" and set an error message. When the completion callback is fired, we check for the error message and if it is set, we emit the error signal. The trick here is the signal cannot be emitted directly from the worker thread because that would violate data ownership rules. --- src/mbgl/map/source.cpp | 30 ++++++++++++++++++++++++------ src/mbgl/map/source.hpp | 2 ++ src/mbgl/map/tile_data.cpp | 15 ++++++++++----- src/mbgl/map/tile_data.hpp | 16 +++++++++++----- src/mbgl/map/vector_tile_data.cpp | 6 +++--- 5 files changed, 50 insertions(+), 19 deletions(-) diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp index eb273d785..faae3285f 100644 --- a/src/mbgl/map/source.cpp +++ b/src/mbgl/map/source.cpp @@ -287,26 +287,25 @@ TileData::State Source::addTile(MapData& data, new_tile.data = cache.get(normalized_id.to_uint64()); } - auto successCallback = std::bind(&Source::emitTileLoaded, this, true); - auto failureCallback = std::bind(&Source::emitTileLoadingFailed, this, std::placeholders::_1); - if (!new_tile.data) { + auto callback = std::bind(&Source::tileLoadingCompleteCallback, this, normalized_id); + // If we don't find working tile data, we're just going to load it. if (info.type == SourceType::Vector) { new_tile.data = std::make_shared(normalized_id, data.transform.getMaxZoom(), style, glyphAtlas, glyphStore, spriteAtlas, sprite, info); new_tile.data->request( - style.workers, transformState.getPixelRatio(), successCallback, failureCallback); + style.workers, transformState.getPixelRatio(), callback); } else if (info.type == SourceType::Raster) { new_tile.data = std::make_shared(normalized_id, texturePool, info); new_tile.data->request( - style.workers, transformState.getPixelRatio(), successCallback, failureCallback); + style.workers, transformState.getPixelRatio(), callback); } else if (info.type == SourceType::Annotations) { new_tile.data = std::make_shared(normalized_id, data.annotationManager, data.transform.getMaxZoom(), style, glyphAtlas, glyphStore, spriteAtlas, sprite, info); - new_tile.data->reparse(style.workers, successCallback); + new_tile.data->reparse(style.workers, callback); } else { throw std::runtime_error("source type not implemented"); } @@ -539,6 +538,25 @@ void Source::setObserver(Observer* observer) { observer_ = observer; } +void Source::tileLoadingCompleteCallback(const TileID& normalized_id) { + auto it = tile_data.find(normalized_id); + if (it == tile_data.end()) { + return; + } + + util::ptr data = it->second.lock(); + if (!data) { + return; + } + + if (data->getState() == TileData::State::obsolete && !data->getError().empty()) { + emitTileLoadingFailed(data->getError()); + return; + } + + emitTileLoaded(true); +} + void Source::emitSourceLoaded() { if (observer_) { observer_->onSourceLoaded(); diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp index d4c29b3e7..174dd996b 100644 --- a/src/mbgl/map/source.hpp +++ b/src/mbgl/map/source.hpp @@ -104,6 +104,8 @@ public: bool enabled; private: + void tileLoadingCompleteCallback(const TileID& normalized_id); + void emitSourceLoaded(); void emitSourceLoadingFailed(const std::string& message); void emitTileLoaded(bool isNewTile); diff --git a/src/mbgl/map/tile_data.cpp b/src/mbgl/map/tile_data.cpp index be0d7d32f..fc2d48bb5 100644 --- a/src/mbgl/map/tile_data.cpp +++ b/src/mbgl/map/tile_data.cpp @@ -38,18 +38,18 @@ void TileData::setState(const State& state_) { void TileData::request(Worker& worker, float pixelRatio, - const std::function& successCallback, - const std::function& failureCallback) { + const std::function& callback) { std::string url = source.tileURL(id, pixelRatio); state = State::loading; - req = env.request({ Resource::Kind::Tile, url }, [url, successCallback, failureCallback, &worker, this](const Response &res) { + req = env.request({ Resource::Kind::Tile, url }, [url, callback, &worker, this](const Response &res) { req = nullptr; if (res.status != Response::Successful) { std::stringstream message; message << "Failed to load [" << url << "]: " << res.message; - failureCallback(message.str()); + setError(message.str()); + callback(); return; } @@ -57,7 +57,7 @@ void TileData::request(Worker& worker, data = res.data; // Schedule tile parsing in another thread - reparse(worker, successCallback); + reparse(worker, callback); }); } @@ -88,3 +88,8 @@ bool TileData::reparse(Worker& worker, std::function callback) { workRequest = worker.send([this] { parse(); endParsing(); }, callback); return true; } + +void TileData::setError(const std::string& message) { + error = message; + setState(State::obsolete); +} diff --git a/src/mbgl/map/tile_data.hpp b/src/mbgl/map/tile_data.hpp index 0d34b2df7..d2a91b880 100644 --- a/src/mbgl/map/tile_data.hpp +++ b/src/mbgl/map/tile_data.hpp @@ -44,10 +44,7 @@ public: TileData(const TileID&, const SourceInfo&); ~TileData(); - void request(Worker&, - float pixelRatio, - const std::function& successCallback, - const std::function& failureCallback); + void request(Worker&, float pixelRatio, const std::function& callback); // Schedule a tile reparse on a worker thread and call the callback on // completion. It will return true if the work was schedule or false it was @@ -71,13 +68,20 @@ public: // We let subclasses override setState() so they // can intercept the state change and react accordingly. virtual void setState(const State& state); - inline State getState() const { return state; } void endParsing(); + // Error message to be set in case of request + // and parsing errors. + void setError(const std::string& message); + + std::string getError() const { + return error; + } + // Override this in the child class. virtual void parse() = 0; virtual Bucket* getBucket(StyleLayer const &layer_desc) = 0; @@ -106,6 +110,8 @@ protected: private: std::atomic state; + std::string error; + protected: // Contains the tile ID string for painting debug information. DebugFontBuffer debugFontBuffer; diff --git a/src/mbgl/map/vector_tile_data.cpp b/src/mbgl/map/vector_tile_data.cpp index 4d77c8ef8..e464c55c9 100644 --- a/src/mbgl/map/vector_tile_data.cpp +++ b/src/mbgl/map/vector_tile_data.cpp @@ -59,9 +59,9 @@ void VectorTileData::parse() { setState(State::parsed); } } catch (const std::exception& ex) { - Log::Error(Event::ParseTile, "Parsing [%d/%d/%d] failed: %s", id.z, id.x, id.y, ex.what()); - setState(State::obsolete); - return; + std::stringstream message; + message << "Failed to parse [" << int(id.z) << "/" << id.x << "/" << id.y << "]: " << ex.what(); + setError(message.str()); } }