diff options
author | John Firebaugh <john.firebaugh@gmail.com> | 2015-04-17 09:58:24 -0700 |
---|---|---|
committer | John Firebaugh <john.firebaugh@gmail.com> | 2015-04-28 09:16:26 -0400 |
commit | cdd8f3ac3235e1d5aab47a6d32c48a0b82a3a1b9 (patch) | |
tree | bcfa045fa4527a6f3fba094b49ecb981f671425d | |
parent | 359aca6624f183099a9afe58aae5a41bd2cb3772 (diff) | |
download | qtlocation-mapboxgl-cdd8f3ac3235e1d5aab47a6d32c48a0b82a3a1b9.tar.gz |
Separate RequestBase from DefaultFileSource internals
SharedRequestBase was trying to do two different jobs: be a base class
for Asset/HttpRequest and track internal state for DefaultFileSource.
Now we have RequestBase for the former, and DefaultFileRequest for the
latter.
-rw-r--r-- | platform/darwin/http_request_nsurl.mm | 13 | ||||
-rw-r--r-- | platform/default/asset_request_fs.cpp | 11 | ||||
-rw-r--r-- | platform/default/asset_request_zip.cpp | 13 | ||||
-rw-r--r-- | platform/default/http_request_curl.cpp | 13 | ||||
-rw-r--r-- | src/mbgl/storage/asset_request.hpp | 6 | ||||
-rw-r--r-- | src/mbgl/storage/default_file_source.cpp | 208 | ||||
-rw-r--r-- | src/mbgl/storage/default_file_source_impl.hpp | 37 | ||||
-rw-r--r-- | src/mbgl/storage/http_request.hpp | 8 | ||||
-rw-r--r-- | src/mbgl/storage/request_base.hpp | 39 | ||||
-rw-r--r-- | src/mbgl/storage/shared_request_base.hpp | 95 |
10 files changed, 191 insertions, 252 deletions
diff --git a/platform/darwin/http_request_nsurl.mm b/platform/darwin/http_request_nsurl.mm index 24b31ae0a9..07ac16e058 100644 --- a/platform/darwin/http_request_nsurl.mm +++ b/platform/darwin/http_request_nsurl.mm @@ -1,5 +1,6 @@ #include <mbgl/storage/http_request.hpp> #include <mbgl/storage/http_context.hpp> +#include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> #include <mbgl/util/uv.hpp> @@ -397,36 +398,28 @@ void HTTPRequestImpl::restart(uv_timer_t *timer, int) { // ------------------------------------------------------------------------------------------------- -HTTPRequest::HTTPRequest(DefaultFileSource::Impl &source, const Resource &resource) - : SharedRequestBase(source, resource) { +HTTPRequest::HTTPRequest(const Resource& resource, Callback callback) + : RequestBase(resource, callback) { } HTTPRequest::~HTTPRequest() { - MBGL_VERIFY_THREAD(tid); - if (ptr) { reinterpret_cast<HTTPRequestImpl *>(ptr)->cancel(); } } void HTTPRequest::start(uv_loop_t *loop, std::shared_ptr<const Response> response) { - MBGL_VERIFY_THREAD(tid); - assert(!ptr); ptr = new HTTPRequestImpl(this, loop, response); } void HTTPRequest::retryImmediately() { - MBGL_VERIFY_THREAD(tid); - if (ptr) { reinterpret_cast<HTTPRequestImpl *>(ptr)->retryImmediately(); } } void HTTPRequest::cancel() { - MBGL_VERIFY_THREAD(tid); - delete this; } diff --git a/platform/default/asset_request_fs.cpp b/platform/default/asset_request_fs.cpp index 446f2f3d52..9580c2f5bc 100644 --- a/platform/default/asset_request_fs.cpp +++ b/platform/default/asset_request_fs.cpp @@ -1,4 +1,5 @@ #include <mbgl/storage/asset_request.hpp> +#include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> #include <mbgl/util/std.hpp> #include <mbgl/util/util.hpp> @@ -207,23 +208,19 @@ void AssetRequestImpl::cleanup(uv_fs_t *req) { // ------------------------------------------------------------------------------------------------- -AssetRequest::AssetRequest(DefaultFileSource::Impl &source_, const Resource &resource_, const std::string& assetRoot_) - : SharedRequestBase(source_, resource_) +AssetRequest::AssetRequest(const Resource& resource_, Callback callback_, const std::string& assetRoot_) + : RequestBase(resource_, callback_) , assetRoot(assetRoot_) { assert(algo::starts_with(resource.url, "asset://")); } AssetRequest::~AssetRequest() { - MBGL_VERIFY_THREAD(tid); - if (ptr) { reinterpret_cast<AssetRequestImpl *>(ptr)->request = nullptr; } } void AssetRequest::start(uv_loop_t *loop, std::shared_ptr<const Response> response) { - MBGL_VERIFY_THREAD(tid); - // We're ignoring the existing response if any. (void(response)); @@ -233,8 +230,6 @@ void AssetRequest::start(uv_loop_t *loop, std::shared_ptr<const Response> respon } void AssetRequest::cancel() { - MBGL_VERIFY_THREAD(tid); - if (ptr) { reinterpret_cast<AssetRequestImpl *>(ptr)->canceled = true; diff --git a/platform/default/asset_request_zip.cpp b/platform/default/asset_request_zip.cpp index 8a471a4fc4..69de1c742c 100644 --- a/platform/default/asset_request_zip.cpp +++ b/platform/default/asset_request_zip.cpp @@ -1,6 +1,7 @@ #include <mbgl/storage/asset_request.hpp> #include <mbgl/storage/thread_context.hpp> #include <mbgl/android/jni.hpp> +#include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> #include <mbgl/platform/log.hpp> #include <mbgl/util/std.hpp> @@ -107,7 +108,7 @@ AssetRequestImpl::~AssetRequestImpl() { MBGL_VERIFY_THREAD(tid); if (request) { - request->ptr = nullptr; + request->impl = nullptr; } } @@ -274,23 +275,19 @@ void AssetRequestImpl::cancel() { // ------------------------------------------------------------------------------------------------- -AssetRequest::AssetRequest(DefaultFileSource::Impl &source_, const Resource &resource_, const std::string& assetRoot_) - : SharedRequestBase(source_, resource_) +AssetRequest::AssetRequest(const Resource& resource_, Callback callback_, const std::string& assetRoot_) + : RequestBase(resource_, callback_) , assetRoot(assetRoot_) { assert(algo::starts_with(resource.url, "asset://")); } AssetRequest::~AssetRequest() { - MBGL_VERIFY_THREAD(tid); - if (ptr) { reinterpret_cast<AssetRequestImpl *>(ptr)->cancel(); } } void AssetRequest::start(uv_loop_t *loop, std::shared_ptr<const Response> response) { - MBGL_VERIFY_THREAD(tid); - // We're ignoring the existing response if any. (void(response)); @@ -300,8 +297,6 @@ void AssetRequest::start(uv_loop_t *loop, std::shared_ptr<const Response> respon } void AssetRequest::cancel() { - MBGL_VERIFY_THREAD(tid); - if (ptr) { reinterpret_cast<AssetRequestImpl *>(ptr)->cancel(); } diff --git a/platform/default/http_request_curl.cpp b/platform/default/http_request_curl.cpp index a5de61104a..ffe3b525ab 100644 --- a/platform/default/http_request_curl.cpp +++ b/platform/default/http_request_curl.cpp @@ -1,5 +1,6 @@ #include <mbgl/storage/http_request.hpp> #include <mbgl/storage/http_context.hpp> +#include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> #include <mbgl/util/chrono.hpp> #include <mbgl/platform/log.hpp> @@ -722,36 +723,28 @@ void HTTPRequestImpl::handleResult(CURLcode code) { // ------------------------------------------------------------------------------------------------- -HTTPRequest::HTTPRequest(DefaultFileSource::Impl &source_, const Resource &resource_) - : SharedRequestBase(source_, resource_) { +HTTPRequest::HTTPRequest(const Resource& resource_, Callback callback_) + : RequestBase(resource_, callback_) { } HTTPRequest::~HTTPRequest() { - MBGL_VERIFY_THREAD(tid); - if (ptr) { reinterpret_cast<HTTPRequestImpl *>(ptr)->abandon(); } } void HTTPRequest::start(uv_loop_t *loop, std::shared_ptr<const Response> response) { - MBGL_VERIFY_THREAD(tid); - assert(!ptr); ptr = new HTTPRequestImpl(this, loop, response); } void HTTPRequest::retryImmediately() { - MBGL_VERIFY_THREAD(tid); - if (ptr) { reinterpret_cast<HTTPRequestImpl *>(ptr)->retryImmediately(); } } void HTTPRequest::cancel() { - MBGL_VERIFY_THREAD(tid); - if (ptr) { delete reinterpret_cast<HTTPRequestImpl *>(ptr); ptr = nullptr; diff --git a/src/mbgl/storage/asset_request.hpp b/src/mbgl/storage/asset_request.hpp index 2a571d2093..e20e1d818a 100644 --- a/src/mbgl/storage/asset_request.hpp +++ b/src/mbgl/storage/asset_request.hpp @@ -1,13 +1,13 @@ #ifndef MBGL_STORAGE_DEFAULT_ASSET_REQUEST #define MBGL_STORAGE_DEFAULT_ASSET_REQUEST -#include "shared_request_base.hpp" +#include "request_base.hpp" namespace mbgl { -class AssetRequest : public SharedRequestBase { +class AssetRequest : public RequestBase { public: - AssetRequest(DefaultFileSource::Impl&, const Resource&, const std::string& assetRoot); + AssetRequest(const Resource&, Callback, const std::string& assetRoot); void start(uv_loop_t *loop, std::shared_ptr<const Response> response = nullptr); void cancel(); diff --git a/src/mbgl/storage/default_file_source.cpp b/src/mbgl/storage/default_file_source.cpp index f4c220e36f..c510b5c7f1 100644 --- a/src/mbgl/storage/default_file_source.cpp +++ b/src/mbgl/storage/default_file_source.cpp @@ -28,10 +28,6 @@ namespace algo = boost::algorithm; namespace mbgl { -DefaultFileSource::Impl::Impl(FileCache* cache_, const std::string& root) - : assetRoot(root.empty() ? platform::assetRoot() : root), cache(cache_) { -} - DefaultFileSource::DefaultFileSource(FileCache* cache, const std::string& root) : thread(util::make_unique<util::Thread<Impl>>("FileSource", cache, root)) { } @@ -40,18 +36,6 @@ DefaultFileSource::~DefaultFileSource() { MBGL_VERIFY_THREAD(tid); } -SharedRequestBase *DefaultFileSource::Impl::find(const Resource &resource) { - // We're using a set of pointers here instead of a map between url and SharedRequestBase because - // we need to find the requests both by pointer and by URL. Given that the number of requests - // is generally very small (typically < 10 at a time), hashing by URL incurs too much overhead - // anyway. - const auto it = pending.find(resource); - if (it != pending.end()) { - return it->second; - } - return nullptr; -} - Request* DefaultFileSource::request(const Resource& resource, uv_loop_t* l, const Environment& env, @@ -81,52 +65,95 @@ void DefaultFileSource::abort(const Environment &env) { thread->invoke(&Impl::abort, std::ref(env)); } +// ----- Impl ----- + +DefaultFileSource::Impl::Impl(FileCache* cache_, const std::string& root) + : cache(cache_), assetRoot(root.empty() ? platform::assetRoot() : root) { +} + +DefaultFileRequest* DefaultFileSource::Impl::find(const Resource& resource) { + const auto it = pending.find(resource); + if (it != pending.end()) { + return &it->second; + } + return nullptr; +} + void DefaultFileSource::Impl::add(Request* req, uv_loop_t* loop) { - const Resource &resource = req->resource; - - // We're adding a new Request. - SharedRequestBase *sharedRequest = find(resource); - if (!sharedRequest) { - // There is no request for this URL yet. Create a new one and start it. - if (algo::starts_with(resource.url, "asset://")) { - sharedRequest = new AssetRequest(*this, resource, assetRoot); - } else { - sharedRequest = new HTTPRequest(*this, resource); + const Resource& resource = req->resource; + DefaultFileRequest* request = find(resource); + + if (request) { + request->observers.insert(req); + return; + } + + request = &pending.emplace(resource, DefaultFileRequest(resource, loop)).first->second; + request->observers.insert(req); + + if (cache) { + startCacheRequest(resource); + } else { + startRealRequest(resource); + } +} + +void DefaultFileSource::Impl::startCacheRequest(const Resource& resource) { + // Check the cache for existing data so that we can potentially + // revalidate the information without having to redownload everything. + cache->get(resource, [this, resource](std::unique_ptr<Response> response) { + DefaultFileRequest* request = find(resource); + + if (!request) { + // There is no request for this URL anymore. Likely, the request was canceled + // before we got around to process the cache result. + return; } - const bool inserted = pending.emplace(resource, sharedRequest).second; - assert(inserted); - (void (inserted)); // silence unused variable warning on Release builds. + auto expired = [&response] { + const int64_t now = std::chrono::duration_cast<std::chrono::seconds>( + SystemClock::now().time_since_epoch()).count(); + return response->expires <= now; + }; - // But first, we're going to start querying the database if it exists. - if (!cache) { - sharedRequest->start(loop); + if (!response || expired()) { + // No response or stale cache. Run the real request. + startRealRequest(resource, std::move(response)); } else { - // Otherwise, first check the cache for existing data so that we can potentially - // revalidate the information without having to redownload everything. - cache->get(resource, [this, resource, loop](std::unique_ptr<Response> response) { - processResult(resource, std::move(response), loop); - }); + // The response is fresh. We're good to notify the caller. + notify(request, std::move(response), FileCache::Hint::No); } + }); +} + +void DefaultFileSource::Impl::startRealRequest(const Resource& resource, std::shared_ptr<const Response> response) { + DefaultFileRequest* request = find(resource); + + auto callback = [request, this] (std::shared_ptr<const Response> res, FileCache::Hint hint) { + notify(request, res, hint); + }; + + if (algo::starts_with(resource.url, "asset://")) { + request->request = new AssetRequest(resource, callback, assetRoot); + } else { + request->request = new HTTPRequest(resource, callback); } - sharedRequest->subscribe(req); + + request->request->start(request->loop, response); } void DefaultFileSource::Impl::cancel(Request* req) { - SharedRequestBase *sharedRequest = find(req->resource); - if (sharedRequest) { - // If the number of dependent requests of the SharedRequestBase drops to zero, the - // unsubscribe callback triggers the removal of the SharedRequestBase pointer from the list - // of pending requests and initiates cancelation. - sharedRequest->unsubscribe(req); - - if (sharedRequest->abandoned()) { - // There are no observers anymore. We are initiating cancelation. - // First, remove this SharedRequestBase from the source. - pending.erase(sharedRequest->resource); - - // Then, initiate cancelation of this request - sharedRequest->cancel(); + DefaultFileRequest* request = find(req->resource); + + if (request) { + // If the number of dependent requests of the DefaultFileRequest drops to zero, + // cancel the request and remove it from the pending list. + request->observers.erase(req); + if (request->observers.empty()) { + if (request->request) { + request->request->cancel(); + } + pending.erase(request->resource); } } else { // There is no request for this URL anymore. Likely, the request already completed @@ -138,70 +165,51 @@ void DefaultFileSource::Impl::cancel(Request* req) { req->destruct(); } -void DefaultFileSource::Impl::processResult(const Resource& resource, std::shared_ptr<const Response> response, uv_loop_t* loop) { - SharedRequestBase *sharedRequest = find(resource); - if (sharedRequest) { - if (response) { - // This entry was stored in the cache. Now determine if we need to revalidate. - const int64_t now = std::chrono::duration_cast<std::chrono::seconds>( - SystemClock::now().time_since_epoch()).count(); - if (response->expires > now) { - // The response is fresh. We're good to notify the caller. - sharedRequest->notify(response, FileCache::Hint::No); - sharedRequest->cancel(); - return; - } else { - // The cached response is stale. Now run the real request. - sharedRequest->start(loop, response); - } - } else { - // There is no response. Now run the real request. - sharedRequest->start(loop); - } - } else { - // There is no request for this URL anymore. Likely, the request was canceled - // before we got around to process the cache result. - } -} - // Aborts all requests that are part of the current environment. void DefaultFileSource::Impl::abort(const Environment& env) { // Construct a cancellation response. - auto res = util::make_unique<Response>(); - res->status = Response::Error; - res->message = "Environment is terminating"; - std::shared_ptr<const Response> response = std::move(res); + auto response = std::make_shared<Response>(); + response->status = Response::Error; + response->message = "Environment is terminating"; // Iterate through all pending requests and remove them in case they're abandoned. - util::erase_if(pending, [&](const std::pair<Resource, SharedRequestBase *> &it) -> bool { - // Obtain all pending requests that are in the current environment. - const auto aborted = it.second->removeAllInEnvironment(env); + util::erase_if(pending, [&](std::pair<const Resource, DefaultFileRequest>& it) -> bool { + // Notify all pending requests that are in the current environment. + util::erase_if(it.second.observers, [&](Request* req) -> bool { + if (&req->env == &env) { + req->notify(response); + return true; + } else { + return false; + } + }); - // Notify all observers. - for (auto req : aborted) { - req->notify(response); - } + bool abandoned = it.second.observers.empty(); - // Finally, remove all requests that are now abandoned. - if (it.second->abandoned()) { - it.second->cancel(); - return true; - } else { - return false; + if (abandoned && it.second.request) { + it.second.request->cancel(); } + + return abandoned; }); } -void DefaultFileSource::Impl::notify(SharedRequestBase *sharedRequest, - std::shared_ptr<const Response> response, FileCache::Hint hint) { +void DefaultFileSource::Impl::notify(DefaultFileRequest* request, std::shared_ptr<const Response> response, FileCache::Hint hint) { // First, remove the request, since it might be destructed at any point now. - assert(find(sharedRequest->resource) == sharedRequest); - pending.erase(sharedRequest->resource); + assert(find(request->resource) == request); + assert(response); + + // Notify all observers. + for (auto req : request->observers) { + req->notify(response); + } if (cache) { // Store response in database - cache->put(sharedRequest->resource, response, hint); + 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 7a673b6ced..d3f83af38a 100644 --- a/src/mbgl/storage/default_file_source_impl.hpp +++ b/src/mbgl/storage/default_file_source_impl.hpp @@ -8,27 +8,36 @@ namespace mbgl { -class SharedRequestBase; +class RequestBase; -class DefaultFileSource::Impl { -public: - Impl(FileCache *cache, const std::string &root = ""); +struct DefaultFileRequest { + const Resource resource; + uv_loop_t* loop; + std::set<Request*> observers; + RequestBase* request = nullptr; - void notify(SharedRequestBase *sharedRequest, - std::shared_ptr<const Response> response, FileCache::Hint hint); - SharedRequestBase *find(const Resource &resource); + DefaultFileRequest(const Resource& resource_, uv_loop_t* loop_) + : resource(resource_), loop(loop_) {} +}; - void add(Request* request, uv_loop_t* loop); - void cancel(Request* request); - void abort(const Environment& env); +class DefaultFileSource::Impl { +public: + Impl(FileCache*, const std::string& = ""); - const std::string assetRoot; + void add(Request*, uv_loop_t*); + void cancel(Request*); + void abort(const Environment&); private: - void processResult(const Resource& resource, std::shared_ptr<const Response> response, uv_loop_t* loop); + DefaultFileRequest* find(const Resource&); - std::unordered_map<Resource, SharedRequestBase *, Resource::Hash> pending; - FileCache *cache = nullptr; + void startCacheRequest(const Resource&); + void startRealRequest(const Resource&, std::shared_ptr<const Response> = nullptr); + void notify(DefaultFileRequest*, std::shared_ptr<const Response>, FileCache::Hint); + + std::unordered_map<Resource, DefaultFileRequest, Resource::Hash> pending; + FileCache* cache = nullptr; + const std::string assetRoot; }; } diff --git a/src/mbgl/storage/http_request.hpp b/src/mbgl/storage/http_request.hpp index 6232bd22e9..faf6ee2f1c 100644 --- a/src/mbgl/storage/http_request.hpp +++ b/src/mbgl/storage/http_request.hpp @@ -1,13 +1,15 @@ #ifndef MBGL_STORAGE_DEFAULT_HTTP_REQUEST #define MBGL_STORAGE_DEFAULT_HTTP_REQUEST -#include "shared_request_base.hpp" +#include "request_base.hpp" namespace mbgl { -class HTTPRequest : public SharedRequestBase { +struct Resource; + +class HTTPRequest : public RequestBase { public: - HTTPRequest(DefaultFileSource::Impl &source, const Resource &resource); + HTTPRequest(const Resource&, Callback); void start(uv_loop_t *loop, std::shared_ptr<const Response> response = nullptr); void cancel(); diff --git a/src/mbgl/storage/request_base.hpp b/src/mbgl/storage/request_base.hpp new file mode 100644 index 0000000000..281e76a02c --- /dev/null +++ b/src/mbgl/storage/request_base.hpp @@ -0,0 +1,39 @@ +#ifndef MBGL_STORAGE_REQUEST +#define MBGL_STORAGE_REQUEST + +#include <mbgl/util/noncopyable.hpp> +#include <mbgl/storage/file_cache.hpp> + +#include <memory> +#include <functional> + +typedef struct uv_loop_s uv_loop_t; + +namespace mbgl { + +struct Resource; +class Response; + +class RequestBase : private util::noncopyable { +public: + using Callback = std::function<void (std::shared_ptr<const Response> response, FileCache::Hint hint)>; + + RequestBase(const Resource& resource_, Callback notify_) + : notify(notify_) + , resource(resource_) { + } + + virtual ~RequestBase() = default; + + virtual void start(uv_loop_t *loop, std::shared_ptr<const Response> response = nullptr) = 0; + virtual void cancel() = 0; + + Callback notify; + +protected: + const Resource& resource; +}; + +} + +#endif diff --git a/src/mbgl/storage/shared_request_base.hpp b/src/mbgl/storage/shared_request_base.hpp deleted file mode 100644 index cf70ac0f04..0000000000 --- a/src/mbgl/storage/shared_request_base.hpp +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef MBGL_STORAGE_DEFAULT_SHARED_REQUEST_BASE -#define MBGL_STORAGE_DEFAULT_SHARED_REQUEST_BASE - -#include <mbgl/storage/resource.hpp> -#include <mbgl/storage/file_cache.hpp> -#include <mbgl/storage/default_file_source_impl.hpp> -#include <mbgl/storage/request.hpp> -#include <mbgl/util/util.hpp> -#include <mbgl/util/noncopyable.hpp> - -#include <string> -#include <set> -#include <vector> -#include <cassert> - -typedef struct uv_loop_s uv_loop_t; - -namespace mbgl { - -class Request; -class Response; -class DefaultFileSource; - -class SharedRequestBase : private util::noncopyable { -protected: - MBGL_STORE_THREAD(tid) - -public: - SharedRequestBase(DefaultFileSource::Impl &source_, const Resource &resource_) - : resource(resource_), source(source_) {} - - virtual void start(uv_loop_t *loop, std::shared_ptr<const Response> response = nullptr) = 0; - virtual void cancel() = 0; - - void notify(std::shared_ptr<const Response> response, FileCache::Hint hint) { - MBGL_VERIFY_THREAD(tid); - assert(response); - - source.notify(this, response, hint); - - // Notify all observers. - for (auto req : observers) { - req->notify(response); - } - } - - void subscribe(Request *request) { - MBGL_VERIFY_THREAD(tid); - observers.insert(request); - } - - void unsubscribe(Request *request) { - MBGL_VERIFY_THREAD(tid); - observers.erase(request); - } - - bool abandoned() const { - return observers.empty(); - } - - std::vector<Request *> removeAllInEnvironment(const Environment &env) { - MBGL_VERIFY_THREAD(tid); - - std::vector<Request *> result; - - // Removes all Requests in the supplied environment and returns a list - // of them. - util::erase_if(observers, [&](Request *req) -> bool { - if (&req->env == &env) { - result.push_back(req); - return true; - } else { - return false; - } - }); - - return result; - } - -protected: - virtual ~SharedRequestBase() { - MBGL_VERIFY_THREAD(tid); - } - -public: - const Resource resource; - -private: - DefaultFileSource::Impl& source; - std::set<Request *> observers; -}; - -} - -#endif |