Chromium Code Reviews| Index: content/browser/loader/resource_scheduler.cc |
| diff --git a/content/browser/loader/resource_scheduler.cc b/content/browser/loader/resource_scheduler.cc |
| index 15c70270033dc8dbf563752b7aa50be12496b047..09dc06243911e9951422a3e3fe0f3cd0fbe0dae7 100644 |
| --- a/content/browser/loader/resource_scheduler.cc |
| +++ b/content/browser/loader/resource_scheduler.cc |
| @@ -2,6 +2,8 @@ |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| +#include <set> |
| + |
| #include "content/browser/loader/resource_scheduler.h" |
| #include "base/stl_util.h" |
| @@ -23,54 +25,49 @@ namespace content { |
| static const size_t kMaxNumDelayableRequestsPerClient = 10; |
| static const size_t kMaxNumDelayableRequestsPerHost = 6; |
| -// A thin wrapper around net::PriorityQueue that deals with |
| -// ScheduledResourceRequests instead of PriorityQueue::Pointers. |
| -class ResourceScheduler::RequestQueue { |
| - private: |
| - typedef net::PriorityQueue<ScheduledResourceRequest*> NetQueue; |
| - public: |
| - class Iterator { |
| - public: |
| - Iterator(NetQueue* queue) : queue_(queue) { |
| - DCHECK(queue != NULL); |
| - current_pointer_ = queue_->FirstMax(); |
| - } |
| +struct ResourceScheduler::RequestPriorityParams { |
| + RequestPriorityParams() |
| + : priority(net::DEFAULT_PRIORITY), |
| + intra_priority(0) |
|
darin (slow to review)
2014/03/24 18:23:33
nit: opening "{" on previous line
shatch
2014/03/24 21:08:44
Done.
|
| + { |
| + } |
| - Iterator& operator++() { |
| - current_pointer_ = queue_->GetNextTowardsLastMin(current_pointer_); |
| - return *this; |
| - } |
| + RequestPriorityParams(net::RequestPriority priority, int intra_priority) |
| + : priority(priority), |
| + intra_priority(intra_priority) { |
| + } |
| - Iterator operator++(int) { |
| - Iterator result(*this); |
| - ++(*this); |
| - return result; |
| - } |
| + bool operator==(const RequestPriorityParams& other) const { |
| + return (priority == other.priority) && |
| + (intra_priority == other.intra_priority); |
| + } |
| - ScheduledResourceRequest* value() { |
| - return current_pointer_.value(); |
| - } |
| + bool operator!=(const RequestPriorityParams& other) const { |
| + return (priority != other.priority) || |
|
darin (slow to review)
2014/03/24 18:23:33
nit: Implement in terms of operator== ? e.g.: ret
shatch
2014/03/24 21:08:44
Done.
|
| + (intra_priority != other.intra_priority); |
| + } |
| - bool is_null() { |
| - return current_pointer_.is_null(); |
| - } |
| + bool GetSortingValue(const RequestPriorityParams& other) const { |
|
darin (slow to review)
2014/03/24 18:23:33
nit: maybe call this function GreaterThan (or inve
shatch
2014/03/24 21:08:44
Done.
|
| + if (priority != other.priority) |
| + return priority > other.priority; |
| + return intra_priority > other.intra_priority; |
| + } |
| - private: |
| - NetQueue* queue_; |
| - NetQueue::Pointer current_pointer_; |
| - }; |
| + net::RequestPriority priority; |
| + int intra_priority; |
| +}; |
| + |
| +class ResourceScheduler::RequestQueue { |
| + public: |
| + typedef std::multiset<ScheduledResourceRequest*, ScheduledResourceSorter> |
| + NetQueue; |
| - RequestQueue() : queue_(net::NUM_PRIORITIES) {} |
| + RequestQueue() : fifo_ordering_ids_(0) {} |
| ~RequestQueue() {} |
| // Adds |request| to the queue with given |priority|. |
| - void Insert(ScheduledResourceRequest* request, |
| - net::RequestPriority priority) { |
| - DCHECK(!ContainsKey(pointers_, request)); |
| - NetQueue::Pointer pointer = queue_.Insert(request, priority); |
| - pointers_[request] = pointer; |
| - } |
| + void Insert(ScheduledResourceRequest* request); |
| // Removes |request| from the queue. |
| void Erase(ScheduledResourceRequest* request) { |
| @@ -78,17 +75,16 @@ class ResourceScheduler::RequestQueue { |
| DCHECK(it != pointers_.end()); |
| if (it == pointers_.end()) |
| return; |
| - queue_.Erase(it->second); |
| + queue_.erase(it->second); |
| pointers_.erase(it); |
| } |
| - // Returns the highest priority request that's queued, or NULL if none are. |
| - ScheduledResourceRequest* FirstMax() { |
| - return queue_.FirstMax().value(); |
| + NetQueue::iterator GetNextHighestIterator() { |
| + return queue_.begin(); |
| } |
| - Iterator GetNextHighestIterator() { |
| - return Iterator(&queue_); |
| + NetQueue::iterator End() { |
| + return queue_.end(); |
| } |
| // Returns true if |request| is queued. |
| @@ -100,7 +96,16 @@ class ResourceScheduler::RequestQueue { |
| bool IsEmpty() const { return queue_.size() == 0; } |
| private: |
| - typedef std::map<ScheduledResourceRequest*, NetQueue::Pointer> PointerMap; |
| + typedef std::map<ScheduledResourceRequest*, NetQueue::iterator> PointerMap; |
| + |
| + uint32 MakeFifoOrderingId() { |
| + fifo_ordering_ids_ += 1; |
| + return fifo_ordering_ids_; |
| + } |
| + |
| + // Used to create an ordering ID for scheduled resources so that resources |
| + // with same priority/intra_priority stay in fifo order. |
| + uint32 fifo_ordering_ids_; |
| NetQueue queue_; |
| PointerMap pointers_; |
| @@ -114,13 +119,16 @@ class ResourceScheduler::ScheduledResourceRequest |
| public: |
| ScheduledResourceRequest(const ClientId& client_id, |
| net::URLRequest* request, |
| - ResourceScheduler* scheduler) |
| + ResourceScheduler* scheduler, |
| + const RequestPriorityParams& priority) |
| : ResourceMessageDelegate(request), |
| client_id_(client_id), |
| request_(request), |
| ready_(false), |
| deferred_(false), |
| - scheduler_(scheduler) { |
| + scheduler_(scheduler), |
| + priority_(priority), |
| + fifo_ordering_(0) { |
| TRACE_EVENT_ASYNC_BEGIN1("net", "URLRequest", request_, |
| "url", request->url().spec()); |
| } |
| @@ -138,9 +146,19 @@ class ResourceScheduler::ScheduledResourceRequest |
| } |
| } |
| + void set_request_priority_params(const RequestPriorityParams& priority) { |
| + priority_ = priority; |
| + } |
| + const RequestPriorityParams& get_request_priority_params() const { |
| + return priority_; |
| + } |
| const ClientId& client_id() const { return client_id_; } |
| net::URLRequest* url_request() { return request_; } |
| const net::URLRequest* url_request() const { return request_; } |
| + uint32 fifo_ordering() const { return fifo_ordering_; } |
| + void set_fifo_ordering(uint32 fifo_ordering) { |
| + fifo_ordering_ = fifo_ordering; |
| + } |
| private: |
| // ResourceMessageDelegate interface: |
| @@ -163,8 +181,9 @@ class ResourceScheduler::ScheduledResourceRequest |
| return "ResourceScheduler"; |
| } |
| - void DidChangePriority(int request_id, net::RequestPriority new_priority) { |
| - scheduler_->ReprioritizeRequest(this, new_priority); |
| + void DidChangePriority(int request_id, net::RequestPriority new_priority, |
| + int intra_priority_value) { |
| + scheduler_->ReprioritizeRequest(this, new_priority, intra_priority_value); |
| } |
| ClientId client_id_; |
| @@ -172,10 +191,35 @@ class ResourceScheduler::ScheduledResourceRequest |
| bool ready_; |
| bool deferred_; |
| ResourceScheduler* scheduler_; |
| + RequestPriorityParams priority_; |
| + uint32 fifo_ordering_; |
| DISALLOW_COPY_AND_ASSIGN(ScheduledResourceRequest); |
| }; |
| +bool ResourceScheduler::ScheduledResourceSorter::operator()( |
| + const ScheduledResourceRequest* a, |
| + const ScheduledResourceRequest* b) const { |
| + // Want the set to be ordered first by decreasing priority, then by |
| + // decreasing intra_priority. |
| + // ie. with (priority, intra_priority) |
| + // [(1, 0), (1, 0), (0, 100), (0, 0)] |
| + if (a->get_request_priority_params() != b->get_request_priority_params()) |
| + return a->get_request_priority_params().GetSortingValue( |
| + b->get_request_priority_params()); |
| + |
| + // If priority/intra_priority is the same, fall back to fifo ordering. |
| + // std::multiset doesn't guarantee this until c++11. |
| + return a->fifo_ordering() < b->fifo_ordering(); |
| +} |
| + |
| +void ResourceScheduler::RequestQueue::Insert( |
| + ScheduledResourceRequest* request) { |
| + DCHECK(!ContainsKey(pointers_, request)); |
| + request->set_fifo_ordering(MakeFifoOrderingId()); |
| + pointers_[request] = queue_.insert(request); |
| +} |
| + |
| // Each client represents a tab. |
| struct ResourceScheduler::Client { |
| Client() : has_body(false), using_spdy_proxy(false) {} |
| @@ -202,7 +246,8 @@ scoped_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest( |
| DCHECK(CalledOnValidThread()); |
| ClientId client_id = MakeClientId(child_id, route_id); |
| scoped_ptr<ScheduledResourceRequest> request( |
| - new ScheduledResourceRequest(client_id, url_request, this)); |
| + new ScheduledResourceRequest(client_id, url_request, this, |
| + RequestPriorityParams(url_request->priority(), 0))); |
| ClientMap::iterator it = client_map_.find(client_id); |
| if (it == client_map_.end()) { |
| @@ -219,7 +264,7 @@ scoped_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest( |
| if (ShouldStartRequest(request.get(), client) == START_REQUEST) { |
| StartRequest(request.get(), client); |
| } else { |
| - client->pending_requests.Insert(request.get(), url_request->priority()); |
| + client->pending_requests.Insert(request.get()); |
| } |
| return request.PassAs<ResourceThrottle>(); |
| } |
| @@ -336,22 +381,33 @@ void ResourceScheduler::StartRequest(ScheduledResourceRequest* request, |
| } |
| void ResourceScheduler::ReprioritizeRequest(ScheduledResourceRequest* request, |
| - net::RequestPriority new_priority) { |
| + net::RequestPriority new_priority, |
| + int new_intra_priority_value) { |
| if (request->url_request()->load_flags() & net::LOAD_IGNORE_LIMITS) { |
| // We should not be re-prioritizing requests with the |
| // IGNORE_LIMITS flag. |
| NOTREACHED(); |
| return; |
| } |
| - net::RequestPriority old_priority = request->url_request()->priority(); |
| - DCHECK_NE(new_priority, old_priority); |
| - request->url_request()->SetPriority(new_priority); |
| + |
| + RequestPriorityParams new_priority_params(new_priority, |
| + new_intra_priority_value); |
| + RequestPriorityParams old_priority_params = |
| + request->get_request_priority_params(); |
| + |
| + DCHECK(old_priority_params != new_priority_params); |
| + |
| + request->url_request()->SetPriority(new_priority_params.priority); |
| + request->set_request_priority_params(new_priority_params); |
| ClientMap::iterator client_it = client_map_.find(request->client_id()); |
| if (client_it == client_map_.end()) { |
| // The client was likely deleted shortly before we received this IPC. |
| return; |
| } |
| + if (old_priority_params == new_priority_params) |
| + return; |
| + |
| Client *client = client_it->second; |
| if (!client->pending_requests.IsQueued(request)) { |
| DCHECK(ContainsKey(client->in_flight_requests, request)); |
| @@ -360,10 +416,9 @@ void ResourceScheduler::ReprioritizeRequest(ScheduledResourceRequest* request, |
| } |
| client->pending_requests.Erase(request); |
| - client->pending_requests.Insert(request, |
| - request->url_request()->priority()); |
| + client->pending_requests.Insert(request); |
| - if (new_priority > old_priority) { |
| + if (new_priority_params.priority > old_priority_params.priority) { |
| // Check if this request is now able to load at its new priority. |
| LoadAnyStartablePendingRequests(client); |
| } |
| @@ -378,12 +433,10 @@ void ResourceScheduler::LoadAnyStartablePendingRequests(Client* client) { |
| // the previous request still in the list. |
| // 3) We do not start the request, same as above, but StartRequest() tells |
| // us there's no point in checking any further requests. |
| - |
| - RequestQueue::Iterator request_iter = |
| + RequestQueue::NetQueue::iterator request_iter = |
| client->pending_requests.GetNextHighestIterator(); |
| - |
| - while (!request_iter.is_null()) { |
| - ScheduledResourceRequest* request = request_iter.value(); |
| + while (request_iter != client->pending_requests.End()) { |
| + ScheduledResourceRequest* request = *request_iter; |
| ShouldStartReqResult query_result = ShouldStartRequest(request, client); |
| if (query_result == START_REQUEST) { |
| @@ -393,7 +446,8 @@ void ResourceScheduler::LoadAnyStartablePendingRequests(Client* client) { |
| // StartRequest can modify the pending list, so we (re)start evaluation |
| // from the currently highest priority request. Avoid copying a singular |
| // iterator, which would trigger undefined behavior. |
| - if (client->pending_requests.GetNextHighestIterator().is_null()) |
| + if (client->pending_requests.GetNextHighestIterator() == |
| + client->pending_requests.End()) |
| break; |
| request_iter = client->pending_requests.GetNextHighestIterator(); |
| } else if (query_result == DO_NOT_START_REQUEST_AND_KEEP_SEARCHING) { |