[core] allow changing the necessity of a TileSource
This commit is contained in:
@@ -287,6 +287,7 @@ bool Source::update(const UpdateParameters& parameters) {
|
||||
};
|
||||
auto createTileDataFn = [this, ¶meters](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 {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user