[core] allow changing the necessity of a TileSource

This commit is contained in:
Konstantin Käfer
2016-05-23 18:03:49 +02:00
parent 4357fed93b
commit f180179d1b
5 changed files with 131 additions and 13 deletions

View File

@@ -287,6 +287,7 @@ bool Source::update(const UpdateParameters& parameters) {
};
auto createTileDataFn = [this, &parameters](const OverscaledTileID& dataTileID) -> TileData* {
if (auto data = createTile(dataTileID, parameters)) {
data->getTileSource()->setNecessity(TileSource::Necessity::Required);
return tileDataMap.emplace(dataTileID, std::move(data)).first->second.get();
} else {
return nullptr;
@@ -316,6 +317,7 @@ bool Source::update(const UpdateParameters& parameters) {
auto retainIt = retain.begin();
while (dataIt != tileDataMap.end()) {
if (retainIt == retain.end() || dataIt->first < *retainIt) {
dataIt->second->getTileSource()->setNecessity(TileSource::Necessity::Optional);
cache.add(dataIt->first, std::move(dataIt->second));
tileDataMap.erase(dataIt++);
} else {

View File

@@ -6,6 +6,7 @@ namespace mbgl {
class FileSource;
class AsyncRequest;
class Response;
template <typename T, typename I>
class FileBasedTileSource : public T {
@@ -14,7 +15,15 @@ protected:
virtual ~FileBasedTileSource() = default;
protected:
const Resource resource;
void makeRequired() override;
void makeOptional() override;
private:
void loadedData(const Response&);
void loadRequired();
private:
Resource resource;
FileSource& fileSource;
std::unique_ptr<AsyncRequest> request;
};

View File

@@ -3,6 +3,8 @@
#include <mbgl/tile/file_based_tile_source.hpp>
#include <mbgl/storage/file_source.hpp>
#include <cassert>
namespace mbgl {
template <typename T, typename I>
@@ -10,18 +12,64 @@ FileBasedTileSource<T, I>::FileBasedTileSource(typename T::data_type& tileData_,
const Resource& resource_,
FileSource& fileSource_)
: T(tileData_), resource(resource_), fileSource(fileSource_) {
assert(!request);
// The first request is always optional.
resource.necessity = Resource::Optional;
request = fileSource.request(resource, [this](Response res) {
if (res.error) {
T::tileData.setError(std::make_exception_ptr(std::runtime_error(res.error->message)));
} else if (res.notModified) {
return;
} else if (res.noContent) {
T::tileData.setData(nullptr, res.modified, res.expires);
} else {
T::tileData.setData(I::parseData(res.data), res.modified, res.expires);
request.reset();
loadedData(res);
T::loaded = true;
if (T::isRequired()) {
loadRequired();
}
});
}
template <typename T, typename I>
void FileBasedTileSource<T, I>::makeRequired() {
if (T::loaded && !request) {
loadRequired();
}
}
template <typename T, typename I>
void FileBasedTileSource<T, I>::makeOptional() {
if (T::loaded && request) {
// Abort a potential HTTP request.
request.reset();
}
}
template <typename T, typename I>
void FileBasedTileSource<T, I>::loadedData(const Response& res) {
if (res.error && !T::loaded && res.error->reason == Response::Error::Reason::NotFound) {
// When the optional request could not be satisfied, don't treat it as an error. Instead,
// we make sure that the next request knows that there has been an optional request before
// by setting one of the prior* fields.
resource.priorExpires = Timestamp{ Seconds::zero() };
} else if (res.error) {
T::tileData.setError(std::make_exception_ptr(std::runtime_error(res.error->message)));
} else if (res.notModified) {
resource.priorExpires = res.expires;
// Do not notify the TileData object; when we get this message, it already has the current
// version of the data.
} else {
resource.priorModified = res.modified;
resource.priorExpires = res.expires;
resource.priorEtag = res.etag;
T::tileData.setData(res.noContent ? nullptr : I::parseData(res.data), res.modified,
res.expires);
}
}
template <typename T, typename I>
void FileBasedTileSource<T, I>::loadRequired() {
assert(!request);
resource.necessity = Resource::Required;
request = fileSource.request(resource, [this](Response res) {
loadedData(res);
});
}
} // namespace mbgl

View File

@@ -33,6 +33,7 @@ public:
void setObserver(TileDataObserver* observer);
void setTileSource(std::unique_ptr<TileSource>);
TileSource* getTileSource() { return tileSource.get(); }
// Mark this tile as no longer needed and cancel any pending work.
virtual void cancel() = 0;
@@ -63,7 +64,6 @@ public:
return availableData == DataAvailability::Some;
}
void dumpDebugLogs() const;
const OverscaledTileID id;

View File

@@ -5,8 +5,64 @@
namespace mbgl {
class TileSource : private util::noncopyable {
public:
// TileSources can have two states: optional or required.
// - optional means that only low-cost actions should be taken to obtain the data
// (e.g. load from cache, but accept stale data)
// - required means that every effort should be taken to obtain the data (e.g. load
// from internet and keep the data fresh if it expires)
enum class Necessity : bool {
Optional = false,
Required = true,
};
protected:
TileSource(Necessity necessity_ = Necessity::Optional) : necessity(necessity_) {
}
public:
virtual ~TileSource() = default;
bool isOptional() const {
return necessity == Necessity::Optional;
}
bool isRequired() const {
return necessity == Necessity::Required;
}
void setNecessity(Necessity newNecessity) {
if (newNecessity != necessity) {
necessity = newNecessity;
if (necessity == Necessity::Required) {
makeRequired();
} else {
makeOptional();
}
}
}
bool isLoaded() const {
// "loaded" is considered true once the initial load action completed, regardless of whether
// the TileData is required or optional, and whether any actual TileData was loaded in the
// case of optional TileData
return loaded;
}
protected:
// called when the tile is one of the ideal tiles that we want to show definitely. the tile source
// should try to make every effort (e.g. fetch from internet, or revalidate existing resources).
virtual void makeRequired() {}
// called when the zoom level no longer corresponds to the displayed one, but
// we're still interested in retaining the tile, e.g. for backfill.
// subclassed TileSources should cancel actions they are taking to provide
// an up-to-date version or load new data
virtual void makeOptional() {}
protected:
Necessity necessity;
bool loaded = false;
};
class GeometryTileData;
@@ -17,7 +73,9 @@ public:
using data_type = GeometryTileData;
protected:
GeometryTileSource(data_type& tileData_) : tileData(tileData_) {}
GeometryTileSource(data_type& tileData_, Necessity necessity_ = Necessity::Optional)
: TileSource(necessity_), tileData(tileData_) {
}
public:
virtual ~GeometryTileSource() = default;
@@ -31,7 +89,8 @@ public:
using data_type = RasterTileData;
protected:
RasterTileSource(data_type& tileData_) : tileData(tileData_) {};
RasterTileSource(data_type& tileData_, Necessity necessity_ = Necessity::Optional)
: TileSource(necessity_), tileData(tileData_){};
public:
virtual ~RasterTileSource() = default;