diff options
author | John Firebaugh <john.firebaugh@gmail.com> | 2016-02-08 16:52:22 -0800 |
---|---|---|
committer | John Firebaugh <john.firebaugh@gmail.com> | 2016-02-10 15:40:20 -0800 |
commit | ff15bd51b94c96f169f0b3800fa04a368c5834c6 (patch) | |
tree | bff20aad89ea7427803246dd2e0e322146ee2b4e | |
parent | 5d42321b73dcafbfd76d38331558e5e60f7d9f1e (diff) | |
download | qtlocation-mapboxgl-ff15bd51b94c96f169f0b3800fa04a368c5834c6.tar.gz |
[core] Throttle the number of concurrent requests to 20
-rw-r--r-- | platform/android/src/http_request_android.cpp | 4 | ||||
-rw-r--r-- | platform/darwin/http_request_nsurl.mm | 4 | ||||
-rw-r--r-- | platform/default/http_request_curl.cpp | 4 | ||||
-rw-r--r-- | platform/default/online_file_source.cpp | 147 | ||||
-rw-r--r-- | src/mbgl/storage/http_context_base.hpp | 1 |
5 files changed, 115 insertions, 45 deletions
diff --git a/platform/android/src/http_request_android.cpp b/platform/android/src/http_request_android.cpp index 023e0cde97..f9be01639d 100644 --- a/platform/android/src/http_request_android.cpp +++ b/platform/android/src/http_request_android.cpp @@ -247,6 +247,10 @@ std::unique_ptr<HTTPContextBase> HTTPContextBase::createContext() { return std::make_unique<HTTPAndroidContext>(); } +uint32_t HTTPContextBase::maximumConcurrentRequests() { + return 20; +} + void JNICALL nativeOnFailure(JNIEnv* env, jobject, jlong nativePtr, jint type, jstring message) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeOnFailure"); assert(nativePtr != 0); diff --git a/platform/darwin/http_request_nsurl.mm b/platform/darwin/http_request_nsurl.mm index bc368f8724..90b2ab9131 100644 --- a/platform/darwin/http_request_nsurl.mm +++ b/platform/darwin/http_request_nsurl.mm @@ -246,4 +246,8 @@ std::unique_ptr<HTTPContextBase> HTTPContextBase::createContext() { return std::make_unique<HTTPNSURLContext>(); } +uint32_t HTTPContextBase::maximumConcurrentRequests() { + return 20; +} + } diff --git a/platform/default/http_request_curl.cpp b/platform/default/http_request_curl.cpp index 5b092573c6..58c574fee1 100644 --- a/platform/default/http_request_curl.cpp +++ b/platform/default/http_request_curl.cpp @@ -542,4 +542,8 @@ std::unique_ptr<HTTPContextBase> HTTPContextBase::createContext() { return std::make_unique<HTTPCURLContext>(); } +uint32_t HTTPContextBase::maximumConcurrentRequests() { + return 20; +} + } // namespace mbgl diff --git a/platform/default/online_file_source.cpp b/platform/default/online_file_source.cpp index 3614501eed..c4a0be2afc 100644 --- a/platform/default/online_file_source.cpp +++ b/platform/default/online_file_source.cpp @@ -24,14 +24,14 @@ class OnlineFileRequestImpl : public util::noncopyable { public: using Callback = std::function<void (Response)>; - OnlineFileRequestImpl(const Resource&, Callback, OnlineFileSource::Impl&); + OnlineFileRequestImpl(FileRequest*, const Resource&, Callback, OnlineFileSource::Impl&); ~OnlineFileRequestImpl(); void networkIsReachableAgain(OnlineFileSource::Impl&); - -private: void schedule(OnlineFileSource::Impl&, bool forceImmediate = false); + void completed(OnlineFileSource::Impl&, Response); + FileRequest* key; Resource resource; HTTPRequestBase* request = nullptr; util::Timer timer; @@ -54,24 +54,81 @@ public: NetworkStatus::Unsubscribe(&reachability); } - void request(FileRequest* req, Resource resource, Callback callback) { - pending[req] = std::make_unique<OnlineFileRequestImpl>(resource, callback, *this); + void request(FileRequest* key, Resource resource, Callback callback) { + allRequests[key] = std::make_unique<OnlineFileRequestImpl>(key, resource, callback, *this); } - void cancel(FileRequest* req) { - pending.erase(req); + void cancel(FileRequest* key) { + allRequests.erase(key); + if (activeRequests.erase(key)) { + activatePendingRequest(); + } } -private: - friend OnlineFileRequestImpl; + void activateOrQueueRequest(OnlineFileRequestImpl* impl) { + assert(allRequests.find(impl->key) != allRequests.end()); + assert(activeRequests.find(impl->key) == activeRequests.end()); + assert(!impl->request); + + if (activeRequests.size() >= HTTPContextBase::maximumConcurrentRequests()) { + queueRequest(impl); + } else { + activateRequest(impl); + } + } + + void queueRequest(OnlineFileRequestImpl* impl) { + pendingRequests.push(impl->key); + } + + void activateRequest(OnlineFileRequestImpl* impl) { + activeRequests.insert(impl->key); + impl->request = httpContext->createRequest(impl->resource, [=] (Response response) { + impl->request = nullptr; + activeRequests.erase(impl->key); + activatePendingRequest(); + impl->completed(*this, response); + }); + } + + void activatePendingRequest() { + while (!pendingRequests.empty()) { + FileRequest* key = pendingRequests.front(); + pendingRequests.pop(); + + auto it = allRequests.find(key); + if (it == allRequests.end()) { + // It must have been cancelled. + continue; + } + + activateRequest(it->second.get()); + return; + } + } +private: void networkIsReachableAgain() { - for (auto& req : pending) { + for (auto& req : allRequests) { req.second->networkIsReachableAgain(*this); } } - std::unordered_map<FileRequest*, std::unique_ptr<OnlineFileRequestImpl>> pending; + /** + * The lifetime of a request is: + * + * 1. Waiting for timeout (revalidation or retry) + * 2. Pending (waiting for room in the active set) + * 3. Active (open network connection) + * 4. Back to #1 + * + * Requests in any state are in `allRequests`. Requests in the pending state are in + * `pendingRequests`. Requests in the active state are in `activeRequests`. + */ + std::unordered_map<FileRequest*, std::unique_ptr<OnlineFileRequestImpl>> allRequests; + std::queue<FileRequest*> pendingRequests; + std::set<FileRequest*> activeRequests; + const std::unique_ptr<HTTPContextBase> httpContext { HTTPContextBase::createContext() }; util::AsyncTask reachability { std::bind(&Impl::networkIsReachableAgain, this) }; }; @@ -130,8 +187,9 @@ std::unique_ptr<FileRequest> OnlineFileSource::request(const Resource& resource, return std::make_unique<OnlineFileRequest>(res, callback, *thread); } -OnlineFileRequestImpl::OnlineFileRequestImpl(const Resource& resource_, Callback callback_, OnlineFileSource::Impl& impl) - : resource(resource_), +OnlineFileRequestImpl::OnlineFileRequestImpl(FileRequest* key_, const Resource& resource_, Callback callback_, OnlineFileSource::Impl& impl) + : key(key_), + resource(resource_), callback(callback_) { // Force an immediate first request if we don't have an expiration time. schedule(impl, !resource.priorExpires); @@ -182,44 +240,43 @@ void OnlineFileRequestImpl::schedule(OnlineFileSource::Impl& impl, bool forceImm return; } - timer.start(timeout, Duration::zero(), [this, &impl] { - assert(!request); - request = impl.httpContext->createRequest(resource, [this, &impl](Response response) { - request = nullptr; + timer.start(timeout, Duration::zero(), [&] { + impl.activateOrQueueRequest(this); + }); +} - // If we didn't get various caching headers in the response, continue using the - // previous values. Otherwise, update the previous values to the new values. +void OnlineFileRequestImpl::completed(OnlineFileSource::Impl& impl, Response response) { + // If we didn't get various caching headers in the response, continue using the + // previous values. Otherwise, update the previous values to the new values. - if (!response.modified) { - response.modified = resource.priorModified; - } else { - resource.priorModified = response.modified; - } + if (!response.modified) { + response.modified = resource.priorModified; + } else { + resource.priorModified = response.modified; + } - if (!response.expires) { - response.expires = resource.priorExpires; - } else { - resource.priorExpires = response.expires; - } + if (!response.expires) { + response.expires = resource.priorExpires; + } else { + resource.priorExpires = response.expires; + } - if (!response.etag) { - response.etag = resource.priorEtag; - } else { - resource.priorEtag = response.etag; - } + if (!response.etag) { + response.etag = resource.priorEtag; + } else { + resource.priorEtag = response.etag; + } - if (response.error) { - failedRequests++; - failedRequestReason = response.error->reason; - } else { - failedRequests = 0; - failedRequestReason = Response::Error::Reason::Success; - } + if (response.error) { + failedRequests++; + failedRequestReason = response.error->reason; + } else { + failedRequests = 0; + failedRequestReason = Response::Error::Reason::Success; + } - callback(response); - schedule(impl); - }); - }); + callback(response); + schedule(impl); } void OnlineFileRequestImpl::networkIsReachableAgain(OnlineFileSource::Impl& impl) { diff --git a/src/mbgl/storage/http_context_base.hpp b/src/mbgl/storage/http_context_base.hpp index b50bf11ec7..6615ea00cf 100644 --- a/src/mbgl/storage/http_context_base.hpp +++ b/src/mbgl/storage/http_context_base.hpp @@ -11,6 +11,7 @@ namespace mbgl { class HTTPContextBase { public: static std::unique_ptr<HTTPContextBase> createContext(); + static uint32_t maximumConcurrentRequests(); virtual ~HTTPContextBase() = default; virtual HTTPRequestBase* createRequest(const Resource&, HTTPRequestBase::Callback) = 0; |