1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <set>
6
7 #include "content/browser/loader/resource_scheduler.h"
8
9 #include "base/stl_util.h"
10 #include "content/common/resource_messages.h"
11 #include "content/browser/loader/resource_message_delegate.h"
12 #include "content/public/browser/resource_controller.h"
13 #include "content/public/browser/resource_request_info.h"
14 #include "content/public/browser/resource_throttle.h"
15 #include "ipc/ipc_message_macros.h"
16 #include "net/base/host_port_pair.h"
17 #include "net/base/load_flags.h"
18 #include "net/base/request_priority.h"
19 #include "net/http/http_server_properties.h"
20 #include "net/url_request/url_request.h"
21 #include "net/url_request/url_request_context.h"
22
23 namespace content {
24
25 static const size_t kMaxNumDelayableRequestsPerClient = 10;
26 static const size_t kMaxNumDelayableRequestsPerHost = 6;
27
28
29 struct ResourceScheduler::RequestPriorityParams {
RequestPriorityParamscontent::ResourceScheduler::RequestPriorityParams30 RequestPriorityParams()
31 : priority(net::DEFAULT_PRIORITY),
32 intra_priority(0) {
33 }
34
RequestPriorityParamscontent::ResourceScheduler::RequestPriorityParams35 RequestPriorityParams(net::RequestPriority priority, int intra_priority)
36 : priority(priority),
37 intra_priority(intra_priority) {
38 }
39
operator ==content::ResourceScheduler::RequestPriorityParams40 bool operator==(const RequestPriorityParams& other) const {
41 return (priority == other.priority) &&
42 (intra_priority == other.intra_priority);
43 }
44
operator !=content::ResourceScheduler::RequestPriorityParams45 bool operator!=(const RequestPriorityParams& other) const {
46 return !(*this == other);
47 }
48
GreaterThancontent::ResourceScheduler::RequestPriorityParams49 bool GreaterThan(const RequestPriorityParams& other) const {
50 if (priority != other.priority)
51 return priority > other.priority;
52 return intra_priority > other.intra_priority;
53 }
54
55 net::RequestPriority priority;
56 int intra_priority;
57 };
58
59 class ResourceScheduler::RequestQueue {
60 public:
61 typedef std::multiset<ScheduledResourceRequest*, ScheduledResourceSorter>
62 NetQueue;
63
RequestQueue()64 RequestQueue() : fifo_ordering_ids_(0) {}
~RequestQueue()65 ~RequestQueue() {}
66
67 // Adds |request| to the queue with given |priority|.
68 void Insert(ScheduledResourceRequest* request);
69
70 // Removes |request| from the queue.
Erase(ScheduledResourceRequest * request)71 void Erase(ScheduledResourceRequest* request) {
72 PointerMap::iterator it = pointers_.find(request);
73 DCHECK(it != pointers_.end());
74 if (it == pointers_.end())
75 return;
76 queue_.erase(it->second);
77 pointers_.erase(it);
78 }
79
GetNextHighestIterator()80 NetQueue::iterator GetNextHighestIterator() {
81 return queue_.begin();
82 }
83
End()84 NetQueue::iterator End() {
85 return queue_.end();
86 }
87
88 // Returns true if |request| is queued.
IsQueued(ScheduledResourceRequest * request) const89 bool IsQueued(ScheduledResourceRequest* request) const {
90 return ContainsKey(pointers_, request);
91 }
92
93 // Returns true if no requests are queued.
IsEmpty() const94 bool IsEmpty() const { return queue_.size() == 0; }
95
96 private:
97 typedef std::map<ScheduledResourceRequest*, NetQueue::iterator> PointerMap;
98
MakeFifoOrderingId()99 uint32 MakeFifoOrderingId() {
100 fifo_ordering_ids_ += 1;
101 return fifo_ordering_ids_;
102 }
103
104 // Used to create an ordering ID for scheduled resources so that resources
105 // with same priority/intra_priority stay in fifo order.
106 uint32 fifo_ordering_ids_;
107
108 NetQueue queue_;
109 PointerMap pointers_;
110 };
111
112 // This is the handle we return to the ResourceDispatcherHostImpl so it can
113 // interact with the request.
114 class ResourceScheduler::ScheduledResourceRequest
115 : public ResourceMessageDelegate,
116 public ResourceThrottle {
117 public:
ScheduledResourceRequest(const ClientId & client_id,net::URLRequest * request,ResourceScheduler * scheduler,const RequestPriorityParams & priority)118 ScheduledResourceRequest(const ClientId& client_id,
119 net::URLRequest* request,
120 ResourceScheduler* scheduler,
121 const RequestPriorityParams& priority)
122 : ResourceMessageDelegate(request),
123 client_id_(client_id),
124 request_(request),
125 ready_(false),
126 deferred_(false),
127 scheduler_(scheduler),
128 priority_(priority),
129 fifo_ordering_(0),
130 accounted_as_delayable_request_(false) {
131 TRACE_EVENT_ASYNC_BEGIN1("net", "URLRequest", request_,
132 "url", request->url().spec());
133 }
134
~ScheduledResourceRequest()135 virtual ~ScheduledResourceRequest() {
136 scheduler_->RemoveRequest(this);
137 }
138
Start()139 void Start() {
140 TRACE_EVENT_ASYNC_STEP_PAST0("net", "URLRequest", request_, "Queued");
141 ready_ = true;
142 if (deferred_ && request_->status().is_success()) {
143 deferred_ = false;
144 controller()->Resume();
145 }
146 }
147
set_request_priority_params(const RequestPriorityParams & priority)148 void set_request_priority_params(const RequestPriorityParams& priority) {
149 priority_ = priority;
150 }
get_request_priority_params() const151 const RequestPriorityParams& get_request_priority_params() const {
152 return priority_;
153 }
client_id() const154 const ClientId& client_id() const { return client_id_; }
url_request()155 net::URLRequest* url_request() { return request_; }
url_request() const156 const net::URLRequest* url_request() const { return request_; }
fifo_ordering() const157 uint32 fifo_ordering() const { return fifo_ordering_; }
set_fifo_ordering(uint32 fifo_ordering)158 void set_fifo_ordering(uint32 fifo_ordering) {
159 fifo_ordering_ = fifo_ordering;
160 }
accounted_as_delayable_request() const161 bool accounted_as_delayable_request() const {
162 return accounted_as_delayable_request_;
163 }
set_accounted_as_delayable_request(bool accounted)164 void set_accounted_as_delayable_request(bool accounted) {
165 accounted_as_delayable_request_ = accounted;
166 }
167
168 private:
169 // ResourceMessageDelegate interface:
OnMessageReceived(const IPC::Message & message)170 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
171 bool handled = true;
172 IPC_BEGIN_MESSAGE_MAP(ScheduledResourceRequest, message)
173 IPC_MESSAGE_HANDLER(ResourceHostMsg_DidChangePriority, DidChangePriority)
174 IPC_MESSAGE_UNHANDLED(handled = false)
175 IPC_END_MESSAGE_MAP()
176 return handled;
177 }
178
179 // ResourceThrottle interface:
WillStartRequest(bool * defer)180 virtual void WillStartRequest(bool* defer) OVERRIDE {
181 deferred_ = *defer = !ready_;
182 }
183
GetNameForLogging() const184 virtual const char* GetNameForLogging() const OVERRIDE {
185 return "ResourceScheduler";
186 }
187
DidChangePriority(int request_id,net::RequestPriority new_priority,int intra_priority_value)188 void DidChangePriority(int request_id, net::RequestPriority new_priority,
189 int intra_priority_value) {
190 scheduler_->ReprioritizeRequest(this, new_priority, intra_priority_value);
191 }
192
193 ClientId client_id_;
194 net::URLRequest* request_;
195 bool ready_;
196 bool deferred_;
197 ResourceScheduler* scheduler_;
198 RequestPriorityParams priority_;
199 uint32 fifo_ordering_;
200 // True if the request is delayable in |in_flight_requests_|.
201 bool accounted_as_delayable_request_;
202
203 DISALLOW_COPY_AND_ASSIGN(ScheduledResourceRequest);
204 };
205
operator ()(const ScheduledResourceRequest * a,const ScheduledResourceRequest * b) const206 bool ResourceScheduler::ScheduledResourceSorter::operator()(
207 const ScheduledResourceRequest* a,
208 const ScheduledResourceRequest* b) const {
209 // Want the set to be ordered first by decreasing priority, then by
210 // decreasing intra_priority.
211 // ie. with (priority, intra_priority)
212 // [(1, 0), (1, 0), (0, 100), (0, 0)]
213 if (a->get_request_priority_params() != b->get_request_priority_params())
214 return a->get_request_priority_params().GreaterThan(
215 b->get_request_priority_params());
216
217 // If priority/intra_priority is the same, fall back to fifo ordering.
218 // std::multiset doesn't guarantee this until c++11.
219 return a->fifo_ordering() < b->fifo_ordering();
220 }
221
Insert(ScheduledResourceRequest * request)222 void ResourceScheduler::RequestQueue::Insert(
223 ScheduledResourceRequest* request) {
224 DCHECK(!ContainsKey(pointers_, request));
225 request->set_fifo_ordering(MakeFifoOrderingId());
226 pointers_[request] = queue_.insert(request);
227 }
228
229 // Each client represents a tab.
230 class ResourceScheduler::Client {
231 public:
Client()232 Client()
233 : has_body_(false),
234 using_spdy_proxy_(false),
235 total_delayable_count_(0) {}
~Client()236 ~Client() {}
237
ScheduleRequest(net::URLRequest * url_request,ScheduledResourceRequest * request)238 void ScheduleRequest(
239 net::URLRequest* url_request,
240 ScheduledResourceRequest* request) {
241 if (ShouldStartRequest(request) == START_REQUEST) {
242 StartRequest(request);
243 } else {
244 pending_requests_.Insert(request);
245 }
246 }
247
RemoveRequest(ScheduledResourceRequest * request)248 void RemoveRequest(ScheduledResourceRequest* request) {
249 if (pending_requests_.IsQueued(request)) {
250 pending_requests_.Erase(request);
251 DCHECK(!ContainsKey(in_flight_requests_, request));
252 } else {
253 EraseInFlightRequest(request);
254
255 // Removing this request may have freed up another to load.
256 LoadAnyStartablePendingRequests();
257 }
258 }
259
RemoveAllRequests()260 RequestSet RemoveAllRequests() {
261 RequestSet unowned_requests;
262 for (RequestSet::iterator it = in_flight_requests_.begin();
263 it != in_flight_requests_.end(); ++it) {
264 unowned_requests.insert(*it);
265 (*it)->set_accounted_as_delayable_request(false);
266 }
267 ClearInFlightRequests();
268 return unowned_requests;
269 }
270
OnNavigate()271 void OnNavigate() {
272 has_body_ = false;
273 }
274
OnWillInsertBody()275 void OnWillInsertBody() {
276 has_body_ = true;
277 LoadAnyStartablePendingRequests();
278 }
279
OnReceivedSpdyProxiedHttpResponse()280 void OnReceivedSpdyProxiedHttpResponse() {
281 if (!using_spdy_proxy_) {
282 using_spdy_proxy_ = true;
283 LoadAnyStartablePendingRequests();
284 }
285 }
286
ReprioritizeRequest(ScheduledResourceRequest * request,RequestPriorityParams old_priority_params,RequestPriorityParams new_priority_params)287 void ReprioritizeRequest(ScheduledResourceRequest* request,
288 RequestPriorityParams old_priority_params,
289 RequestPriorityParams new_priority_params) {
290 request->url_request()->SetPriority(new_priority_params.priority);
291 request->set_request_priority_params(new_priority_params);
292 if (!pending_requests_.IsQueued(request)) {
293 DCHECK(ContainsKey(in_flight_requests_, request));
294 // The priority and SPDY support may have changed, so update the
295 // delayable count.
296 SetRequestDelayable(request, IsDelayableRequest(request));
297 // Request has already started.
298 return;
299 }
300
301 pending_requests_.Erase(request);
302 pending_requests_.Insert(request);
303
304 if (new_priority_params.priority > old_priority_params.priority) {
305 // Check if this request is now able to load at its new priority.
306 LoadAnyStartablePendingRequests();
307 }
308 }
309
310 private:
311 enum ShouldStartReqResult {
312 DO_NOT_START_REQUEST_AND_STOP_SEARCHING = -2,
313 DO_NOT_START_REQUEST_AND_KEEP_SEARCHING = -1,
314 START_REQUEST = 1,
315 };
316
InsertInFlightRequest(ScheduledResourceRequest * request)317 void InsertInFlightRequest(ScheduledResourceRequest* request) {
318 in_flight_requests_.insert(request);
319 if (IsDelayableRequest(request))
320 SetRequestDelayable(request, true);
321 }
322
EraseInFlightRequest(ScheduledResourceRequest * request)323 void EraseInFlightRequest(ScheduledResourceRequest* request) {
324 size_t erased = in_flight_requests_.erase(request);
325 DCHECK_EQ(1u, erased);
326 SetRequestDelayable(request, false);
327 DCHECK_LE(total_delayable_count_, in_flight_requests_.size());
328 }
329
ClearInFlightRequests()330 void ClearInFlightRequests() {
331 in_flight_requests_.clear();
332 total_delayable_count_ = 0;
333 }
334
IsDelayableRequest(ScheduledResourceRequest * request)335 bool IsDelayableRequest(ScheduledResourceRequest* request) {
336 if (request->url_request()->priority() < net::LOW) {
337 net::HostPortPair host_port_pair =
338 net::HostPortPair::FromURL(request->url_request()->url());
339 net::HttpServerProperties& http_server_properties =
340 *request->url_request()->context()->http_server_properties();
341 if (!http_server_properties.SupportsSpdy(host_port_pair)) {
342 return true;
343 }
344 }
345 return false;
346 }
347
SetRequestDelayable(ScheduledResourceRequest * request,bool delayable)348 void SetRequestDelayable(ScheduledResourceRequest* request,
349 bool delayable) {
350 if (request->accounted_as_delayable_request() == delayable)
351 return;
352 if (delayable)
353 total_delayable_count_++;
354 else
355 total_delayable_count_--;
356 request->set_accounted_as_delayable_request(delayable);
357 }
358
ShouldKeepSearching(const net::HostPortPair & active_request_host) const359 bool ShouldKeepSearching(
360 const net::HostPortPair& active_request_host) const {
361 size_t same_host_count = 0;
362 for (RequestSet::const_iterator it = in_flight_requests_.begin();
363 it != in_flight_requests_.end(); ++it) {
364 net::HostPortPair host_port_pair =
365 net::HostPortPair::FromURL((*it)->url_request()->url());
366 if (active_request_host.Equals(host_port_pair)) {
367 same_host_count++;
368 if (same_host_count >= kMaxNumDelayableRequestsPerHost)
369 return true;
370 }
371 }
372 return false;
373 }
374
StartRequest(ScheduledResourceRequest * request)375 void StartRequest(ScheduledResourceRequest* request) {
376 InsertInFlightRequest(request);
377 request->Start();
378 }
379
380 // ShouldStartRequest is the main scheduling algorithm.
381 //
382 // Requests are categorized into two categories:
383 //
384 // 1. Immediately issued requests, which are:
385 //
386 // * Higher priority requests (>= net::LOW).
387 // * Synchronous requests.
388 // * Requests to SPDY-capable origin servers.
389 // * Non-HTTP[S] requests.
390 //
391 // 2. The remainder are delayable requests, which follow these rules:
392 //
393 // * If no high priority requests are in flight, start loading low priority
394 // requests.
395 // * Once the renderer has a <body>, start loading delayable requests.
396 // * Never exceed 10 delayable requests in flight per client.
397 // * Never exceed 6 delayable requests for a given host.
398 // * Prior to <body>, allow one delayable request to load at a time.
ShouldStartRequest(ScheduledResourceRequest * request) const399 ShouldStartReqResult ShouldStartRequest(
400 ScheduledResourceRequest* request) const {
401 const net::URLRequest& url_request = *request->url_request();
402 // TODO(simonjam): This may end up causing disk contention. We should
403 // experiment with throttling if that happens.
404 if (!url_request.url().SchemeIsHTTPOrHTTPS()) {
405 return START_REQUEST;
406 }
407
408 if (using_spdy_proxy_ && url_request.url().SchemeIs("http")) {
409 return START_REQUEST;
410 }
411
412 net::HttpServerProperties& http_server_properties =
413 *url_request.context()->http_server_properties();
414
415 if (url_request.priority() >= net::LOW ||
416 !ResourceRequestInfo::ForRequest(&url_request)->IsAsync()) {
417 return START_REQUEST;
418 }
419
420 net::HostPortPair host_port_pair =
421 net::HostPortPair::FromURL(url_request.url());
422
423 // TODO(willchan): We should really improve this algorithm as described in
424 // crbug.com/164101. Also, theoretically we should not count a SPDY request
425 // against the delayable requests limit.
426 if (http_server_properties.SupportsSpdy(host_port_pair)) {
427 return START_REQUEST;
428 }
429
430 size_t num_delayable_requests_in_flight = total_delayable_count_;
431 if (num_delayable_requests_in_flight >= kMaxNumDelayableRequestsPerClient) {
432 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING;
433 }
434
435 if (ShouldKeepSearching(host_port_pair)) {
436 // There may be other requests for other hosts we'd allow,
437 // so keep checking.
438 return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING;
439 }
440
441 bool have_immediate_requests_in_flight =
442 in_flight_requests_.size() > num_delayable_requests_in_flight;
443 if (have_immediate_requests_in_flight && !has_body_ &&
444 num_delayable_requests_in_flight != 0) {
445 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING;
446 }
447
448 return START_REQUEST;
449 }
450
LoadAnyStartablePendingRequests()451 void LoadAnyStartablePendingRequests() {
452 // We iterate through all the pending requests, starting with the highest
453 // priority one. For each entry, one of three things can happen:
454 // 1) We start the request, remove it from the list, and keep checking.
455 // 2) We do NOT start the request, but ShouldStartRequest() signals us that
456 // there may be room for other requests, so we keep checking and leave
457 // the previous request still in the list.
458 // 3) We do not start the request, same as above, but StartRequest() tells
459 // us there's no point in checking any further requests.
460 RequestQueue::NetQueue::iterator request_iter =
461 pending_requests_.GetNextHighestIterator();
462
463 while (request_iter != pending_requests_.End()) {
464 ScheduledResourceRequest* request = *request_iter;
465 ShouldStartReqResult query_result = ShouldStartRequest(request);
466
467 if (query_result == START_REQUEST) {
468 pending_requests_.Erase(request);
469 StartRequest(request);
470
471 // StartRequest can modify the pending list, so we (re)start evaluation
472 // from the currently highest priority request. Avoid copying a singular
473 // iterator, which would trigger undefined behavior.
474 if (pending_requests_.GetNextHighestIterator() ==
475 pending_requests_.End())
476 break;
477 request_iter = pending_requests_.GetNextHighestIterator();
478 } else if (query_result == DO_NOT_START_REQUEST_AND_KEEP_SEARCHING) {
479 ++request_iter;
480 continue;
481 } else {
482 DCHECK(query_result == DO_NOT_START_REQUEST_AND_STOP_SEARCHING);
483 break;
484 }
485 }
486 }
487
488 bool has_body_;
489 bool using_spdy_proxy_;
490 RequestQueue pending_requests_;
491 RequestSet in_flight_requests_;
492 // The number of delayable in-flight requests.
493 size_t total_delayable_count_;
494 };
495
ResourceScheduler()496 ResourceScheduler::ResourceScheduler() {
497 }
498
~ResourceScheduler()499 ResourceScheduler::~ResourceScheduler() {
500 DCHECK(unowned_requests_.empty());
501 DCHECK(client_map_.empty());
502 }
503
ScheduleRequest(int child_id,int route_id,net::URLRequest * url_request)504 scoped_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest(
505 int child_id,
506 int route_id,
507 net::URLRequest* url_request) {
508 DCHECK(CalledOnValidThread());
509 ClientId client_id = MakeClientId(child_id, route_id);
510 scoped_ptr<ScheduledResourceRequest> request(
511 new ScheduledResourceRequest(client_id, url_request, this,
512 RequestPriorityParams(url_request->priority(), 0)));
513
514 ClientMap::iterator it = client_map_.find(client_id);
515 if (it == client_map_.end()) {
516 // There are several ways this could happen:
517 // 1. <a ping> requests don't have a route_id.
518 // 2. Most unittests don't send the IPCs needed to register Clients.
519 // 3. The tab is closed while a RequestResource IPC is in flight.
520 unowned_requests_.insert(request.get());
521 request->Start();
522 return request.PassAs<ResourceThrottle>();
523 }
524
525 Client* client = it->second;
526 client->ScheduleRequest(url_request, request.get());
527 return request.PassAs<ResourceThrottle>();
528 }
529
RemoveRequest(ScheduledResourceRequest * request)530 void ResourceScheduler::RemoveRequest(ScheduledResourceRequest* request) {
531 DCHECK(CalledOnValidThread());
532 if (ContainsKey(unowned_requests_, request)) {
533 unowned_requests_.erase(request);
534 return;
535 }
536
537 ClientMap::iterator client_it = client_map_.find(request->client_id());
538 if (client_it == client_map_.end()) {
539 return;
540 }
541
542 Client* client = client_it->second;
543 client->RemoveRequest(request);
544 }
545
OnClientCreated(int child_id,int route_id)546 void ResourceScheduler::OnClientCreated(int child_id, int route_id) {
547 DCHECK(CalledOnValidThread());
548 ClientId client_id = MakeClientId(child_id, route_id);
549 DCHECK(!ContainsKey(client_map_, client_id));
550
551 client_map_[client_id] = new Client;
552 }
553
OnClientDeleted(int child_id,int route_id)554 void ResourceScheduler::OnClientDeleted(int child_id, int route_id) {
555 DCHECK(CalledOnValidThread());
556 ClientId client_id = MakeClientId(child_id, route_id);
557 DCHECK(ContainsKey(client_map_, client_id));
558 ClientMap::iterator it = client_map_.find(client_id);
559 if (it == client_map_.end())
560 return;
561
562 Client* client = it->second;
563
564 // FYI, ResourceDispatcherHost cancels all of the requests after this function
565 // is called. It should end up canceling all of the requests except for a
566 // cross-renderer navigation.
567 RequestSet client_unowned_requests = client->RemoveAllRequests();
568 for (RequestSet::iterator it = client_unowned_requests.begin();
569 it != client_unowned_requests.end(); ++it) {
570 unowned_requests_.insert(*it);
571 }
572
573 delete client;
574 client_map_.erase(it);
575 }
576
OnNavigate(int child_id,int route_id)577 void ResourceScheduler::OnNavigate(int child_id, int route_id) {
578 DCHECK(CalledOnValidThread());
579 ClientId client_id = MakeClientId(child_id, route_id);
580
581 ClientMap::iterator it = client_map_.find(client_id);
582 if (it == client_map_.end()) {
583 // The client was likely deleted shortly before we received this IPC.
584 return;
585 }
586
587 Client* client = it->second;
588 client->OnNavigate();
589 }
590
OnWillInsertBody(int child_id,int route_id)591 void ResourceScheduler::OnWillInsertBody(int child_id, int route_id) {
592 DCHECK(CalledOnValidThread());
593 ClientId client_id = MakeClientId(child_id, route_id);
594
595 ClientMap::iterator it = client_map_.find(client_id);
596 if (it == client_map_.end()) {
597 // The client was likely deleted shortly before we received this IPC.
598 return;
599 }
600
601 Client* client = it->second;
602 client->OnWillInsertBody();
603 }
604
OnReceivedSpdyProxiedHttpResponse(int child_id,int route_id)605 void ResourceScheduler::OnReceivedSpdyProxiedHttpResponse(
606 int child_id,
607 int route_id) {
608 DCHECK(CalledOnValidThread());
609 ClientId client_id = MakeClientId(child_id, route_id);
610
611 ClientMap::iterator client_it = client_map_.find(client_id);
612 if (client_it == client_map_.end()) {
613 return;
614 }
615
616 Client* client = client_it->second;
617 client->OnReceivedSpdyProxiedHttpResponse();
618 }
619
ReprioritizeRequest(ScheduledResourceRequest * request,net::RequestPriority new_priority,int new_intra_priority_value)620 void ResourceScheduler::ReprioritizeRequest(ScheduledResourceRequest* request,
621 net::RequestPriority new_priority,
622 int new_intra_priority_value) {
623 if (request->url_request()->load_flags() & net::LOAD_IGNORE_LIMITS) {
624 // We should not be re-prioritizing requests with the
625 // IGNORE_LIMITS flag.
626 NOTREACHED();
627 return;
628 }
629 RequestPriorityParams new_priority_params(new_priority,
630 new_intra_priority_value);
631 RequestPriorityParams old_priority_params =
632 request->get_request_priority_params();
633
634 DCHECK(old_priority_params != new_priority_params);
635
636 ClientMap::iterator client_it = client_map_.find(request->client_id());
637 if (client_it == client_map_.end()) {
638 // The client was likely deleted shortly before we received this IPC.
639 request->url_request()->SetPriority(new_priority_params.priority);
640 request->set_request_priority_params(new_priority_params);
641 return;
642 }
643
644 if (old_priority_params == new_priority_params)
645 return;
646
647 Client *client = client_it->second;
648 client->ReprioritizeRequest(
649 request, old_priority_params, new_priority_params);
650 }
651
MakeClientId(int child_id,int route_id)652 ResourceScheduler::ClientId ResourceScheduler::MakeClientId(
653 int child_id, int route_id) {
654 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id;
655 }
656
657 } // namespace content
658