summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2015-10-15 21:30:21 +0200
committerKonstantin Käfer <mail@kkaefer.com>2015-10-26 15:54:27 +0100
commit6a7334b882a47ca193209f2012843e42aa3ed4e2 (patch)
treef74bf484c4c53efaa657f524f8c8b01211e15b20 /src
parent096a3edf39d23fbd4baa134938c16fed4f2e199c (diff)
downloadqtlocation-mapboxgl-6a7334b882a47ca193209f2012843e42aa3ed4e2.tar.gz
[core] add support for stale responses
We're now returning stale responses from cache. Those responses will have the `stale` flag set to true. Currently, all requesters in the core code discard stale responses, and cancel the request immediately after they got a non-stale response.
Diffstat (limited to 'src')
-rw-r--r--src/mbgl/map/map_context.cpp4
-rw-r--r--src/mbgl/map/raster_tile_data.cpp4
-rw-r--r--src/mbgl/map/source.cpp4
-rw-r--r--src/mbgl/map/sprite.cpp8
-rw-r--r--src/mbgl/map/vector_tile_data.cpp4
-rw-r--r--src/mbgl/storage/default_file_source.cpp78
-rw-r--r--src/mbgl/storage/default_file_source_impl.hpp2
-rw-r--r--src/mbgl/storage/request.cpp1
-rw-r--r--src/mbgl/storage/response.cpp12
-rw-r--r--src/mbgl/text/glyph_pbf.cpp4
10 files changed, 98 insertions, 23 deletions
diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp
index 0888ef2846..b197ea12f3 100644
--- a/src/mbgl/map/map_context.cpp
+++ b/src/mbgl/map/map_context.cpp
@@ -106,6 +106,10 @@ void MapContext::setStyleURL(const std::string& url) {
FileSource* fs = util::ThreadContext::getFileSource();
styleRequest = fs->request({ Resource::Kind::Style, styleURL }, util::RunLoop::getLoop(), [this, base](const Response &res) {
+ if (res.stale) {
+ // Only handle fresh responses.
+ return;
+ }
styleRequest = nullptr;
if (res.status == Response::Successful) {
diff --git a/src/mbgl/map/raster_tile_data.cpp b/src/mbgl/map/raster_tile_data.cpp
index 4ae9189a0b..cc7b6b548f 100644
--- a/src/mbgl/map/raster_tile_data.cpp
+++ b/src/mbgl/map/raster_tile_data.cpp
@@ -31,6 +31,10 @@ void RasterTileData::request(float pixelRatio,
FileSource* fs = util::ThreadContext::getFileSource();
req = fs->request({ Resource::Kind::Tile, url }, util::RunLoop::getLoop(), [url, callback, this](const Response &res) {
+ if (res.stale) {
+ // Only handle fresh responses.
+ return;
+ }
req = nullptr;
if (res.status == Response::NotFound) {
diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp
index d723e308b1..7e30682895 100644
--- a/src/mbgl/map/source.cpp
+++ b/src/mbgl/map/source.cpp
@@ -149,6 +149,10 @@ void Source::load() {
FileSource* fs = util::ThreadContext::getFileSource();
req = fs->request({ Resource::Kind::Source, info.url }, util::RunLoop::getLoop(), [this](const Response &res) {
+ if (res.stale) {
+ // Only handle fresh responses.
+ return;
+ }
req = nullptr;
if (res.status != Response::Successful) {
diff --git a/src/mbgl/map/sprite.cpp b/src/mbgl/map/sprite.cpp
index 9c099b4aa2..a54d96f005 100644
--- a/src/mbgl/map/sprite.cpp
+++ b/src/mbgl/map/sprite.cpp
@@ -44,6 +44,10 @@ Sprite::Sprite(const std::string& baseUrl, float pixelRatio_)
FileSource* fs = util::ThreadContext::getFileSource();
loader->jsonRequest = fs->request({ Resource::Kind::SpriteJSON, jsonURL }, util::RunLoop::getLoop(),
[this, jsonURL](const Response& res) {
+ if (res.stale) {
+ // Only handle fresh responses.
+ return;
+ }
loader->jsonRequest = nullptr;
if (res.status == Response::Successful) {
loader->data->json = res.data;
@@ -60,6 +64,10 @@ Sprite::Sprite(const std::string& baseUrl, float pixelRatio_)
loader->spriteRequest =
fs->request({ Resource::Kind::SpriteImage, spriteURL }, util::RunLoop::getLoop(),
[this, spriteURL](const Response& res) {
+ if (res.stale) {
+ // Only handle fresh responses.
+ return;
+ }
loader->spriteRequest = nullptr;
if (res.status == Response::Successful) {
loader->data->image = res.data;
diff --git a/src/mbgl/map/vector_tile_data.cpp b/src/mbgl/map/vector_tile_data.cpp
index 6aa643b16f..2e683daaff 100644
--- a/src/mbgl/map/vector_tile_data.cpp
+++ b/src/mbgl/map/vector_tile_data.cpp
@@ -43,6 +43,10 @@ void VectorTileData::request(float pixelRatio, const std::function<void()>& call
FileSource* fs = util::ThreadContext::getFileSource();
req = fs->request({ Resource::Kind::Tile, url }, util::RunLoop::getLoop(), [url, callback, this](const Response &res) {
+ if (res.stale) {
+ // Only handle fresh responses.
+ return;
+ }
req = nullptr;
if (res.status == Response::NotFound) {
diff --git a/src/mbgl/storage/default_file_source.cpp b/src/mbgl/storage/default_file_source.cpp
index 7b3cff8253..71c42f6220 100644
--- a/src/mbgl/storage/default_file_source.cpp
+++ b/src/mbgl/storage/default_file_source.cpp
@@ -8,7 +8,6 @@
#include <mbgl/platform/log.hpp>
#include <mbgl/util/uv_detail.hpp>
-#include <mbgl/util/chrono.hpp>
#include <mbgl/util/thread.hpp>
#include <mbgl/util/mapbox.hpp>
#include <mbgl/util/exception.hpp>
@@ -102,43 +101,79 @@ void DefaultFileSource::Impl::add(Request* req) {
const Resource& resource = req->resource;
DefaultFileRequest* request = find(resource);
- if (request) {
- request->observers.insert(req);
- return;
+ if (!request) {
+ request = &pending.emplace(resource, resource).first->second;
}
- request = &pending.emplace(resource, resource).first->second;
+ // Add this request as an observer so that it'll get notified when something about this
+ // request changes.
request->observers.insert(req);
- if (cache) {
- startCacheRequest(request);
+ update(request);
+
+ if (request->response) {
+ // We've got a response, so send the (potentially stale) response to the requester.
+ req->notify(request->response);
+ }
+}
+
+void DefaultFileSource::Impl::update(DefaultFileRequest* request) {
+ if (request->response) {
+ // We've at least obtained a cache value, potentially we also got a final response.
+ // The observers have been notified already; send what we have to the new one as well.
+
+ // Before returning the existing response, make sure that it is still fresh.
+ if (!request->response->stale && request->response->isExpired()) {
+ // Create a new Response object with `stale = true`, but the same data, and
+ // replace the current request object we have.
+ // TODO: Make content shared_ptrs so we won't make copies of the content.
+ auto response = std::make_shared<Response>(*request->response);
+ response->stale = true;
+ request->response = response;
+ }
+
+ if (request->response->stale && !request->realRequest) {
+ // We've returned a stale response; now make sure the requester also gets a fresh
+ // response eventually. It's possible that there's already a request in progress.
+ // Note that this will also trigger updates to all other existing listeners.
+ // Since we already have data, we're going to verify
+ startRealRequest(request, request->response);
+ }
+ } else if (!request->cacheRequest && !request->realRequest) {
+ // There is no request in progress, and we don't have a response yet. This means we'll have
+ // to start the request ourselves.
+ if (cache) {
+ startCacheRequest(request);
+ } else {
+ startRealRequest(request);
+ }
} else {
- startRealRequest(request);
+ // There is a request in progress. We just have to wait.
}
}
void DefaultFileSource::Impl::startCacheRequest(DefaultFileRequest* request) {
// Check the cache for existing data so that we can potentially
// revalidate the information without having to redownload everything.
- request->cacheRequest = cache->get(request->resource, [this, request](std::unique_ptr<Response> response) {
- auto expired = [&response] {
- const int64_t now = std::chrono::duration_cast<std::chrono::seconds>(
- SystemClock::now().time_since_epoch()).count();
- return response->expires <= now;
- };
-
- if (!response || expired()) {
+ request->cacheRequest = cache->get(request->resource, [this, request](std::shared_ptr<Response> response) {
+ request->cacheRequest = nullptr;
+ if (response) {
+ response->stale = response->isExpired();
+
+ // Notify in all cases; requestors can decide whether they want to use stale responses.
+ notify(request, response, FileCache::Hint::No);
+ }
+
+ if (!response || response->stale) {
// No response or stale cache. Run the real request.
- startRealRequest(request, std::move(response));
- } else {
- // The response is fresh. We're good to notify the caller.
- notify(request, std::move(response), FileCache::Hint::No);
+ startRealRequest(request, response);
}
});
}
void DefaultFileSource::Impl::startRealRequest(DefaultFileRequest* request, std::shared_ptr<const Response> response) {
auto callback = [request, this] (std::shared_ptr<const Response> res, FileCache::Hint hint) {
+ request->realRequest = nullptr;
notify(request, res, hint);
};
@@ -181,6 +216,7 @@ void DefaultFileSource::Impl::notify(DefaultFileRequest* request, std::shared_pt
assert(response);
// Notify all observers.
+ request->response = response;
for (auto req : request->observers) {
req->notify(response);
}
@@ -189,8 +225,6 @@ void DefaultFileSource::Impl::notify(DefaultFileRequest* request, std::shared_pt
// Store response in database
cache->put(request->resource, response, hint);
}
-
- pending.erase(request->resource);
}
}
diff --git a/src/mbgl/storage/default_file_source_impl.hpp b/src/mbgl/storage/default_file_source_impl.hpp
index bb28933798..980eedc118 100644
--- a/src/mbgl/storage/default_file_source_impl.hpp
+++ b/src/mbgl/storage/default_file_source_impl.hpp
@@ -15,6 +15,7 @@ class RequestBase;
struct DefaultFileRequest {
const Resource resource;
std::set<Request*> observers;
+ std::shared_ptr<const Response> response;
std::unique_ptr<WorkRequest> cacheRequest;
RequestBase* realRequest = nullptr;
@@ -39,6 +40,7 @@ public:
private:
DefaultFileRequest* find(const Resource&);
+ void update(DefaultFileRequest*);
void startCacheRequest(DefaultFileRequest*);
void startRealRequest(DefaultFileRequest*, std::shared_ptr<const Response> = nullptr);
void notify(DefaultFileRequest*, std::shared_ptr<const Response>, FileCache::Hint);
diff --git a/src/mbgl/storage/request.cpp b/src/mbgl/storage/request.cpp
index f8ad555379..55913846cf 100644
--- a/src/mbgl/storage/request.cpp
+++ b/src/mbgl/storage/request.cpp
@@ -49,7 +49,6 @@ Request::~Request() = default;
// Called in the FileSource thread.
void Request::notify(const std::shared_ptr<const Response> &response_) {
- assert(!std::atomic_load(&response));
assert(response_);
std::atomic_store(&response, response_);
async->send();
diff --git a/src/mbgl/storage/response.cpp b/src/mbgl/storage/response.cpp
new file mode 100644
index 0000000000..628a2a3b99
--- /dev/null
+++ b/src/mbgl/storage/response.cpp
@@ -0,0 +1,12 @@
+#include <mbgl/storage/response.hpp>
+#include <mbgl/util/chrono.hpp>
+
+namespace mbgl {
+
+bool Response::isExpired() const {
+ const int64_t now = std::chrono::duration_cast<std::chrono::seconds>(
+ SystemClock::now().time_since_epoch()).count();
+ return expires <= now;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/text/glyph_pbf.cpp b/src/mbgl/text/glyph_pbf.cpp
index e37e656d91..f351e66c2a 100644
--- a/src/mbgl/text/glyph_pbf.cpp
+++ b/src/mbgl/text/glyph_pbf.cpp
@@ -75,6 +75,10 @@ GlyphPBF::GlyphPBF(GlyphStore* store,
});
auto requestCallback = [this, store, fontStack, url](const Response &res) {
+ if (res.stale) {
+ // Only handle fresh responses.
+ return;
+ }
req = nullptr;
if (res.status != Response::Successful) {