OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/loader/resource_scheduler.h" | |
6 | |
5 #include <set> | 7 #include <set> |
6 | 8 |
7 #include "content/browser/loader/resource_scheduler.h" | |
8 | |
9 #include "base/metrics/field_trial.h" | 9 #include "base/metrics/field_trial.h" |
10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
11 #include "base/stl_util.h" | 11 #include "base/stl_util.h" |
12 #include "base/strings/string_number_conversions.h" | 12 #include "base/strings/string_number_conversions.h" |
13 #include "base/strings/string_piece.h" | 13 #include "base/strings/string_piece.h" |
14 #include "base/time/time.h" | 14 #include "base/time/time.h" |
15 #include "content/common/resource_messages.h" | 15 #include "content/common/resource_messages.h" |
16 #include "content/browser/loader/resource_message_delegate.h" | 16 #include "content/browser/loader/resource_message_delegate.h" |
17 #include "content/public/browser/resource_controller.h" | 17 #include "content/public/browser/resource_controller.h" |
18 #include "content/public/browser/resource_request_info.h" | 18 #include "content/public/browser/resource_request_info.h" |
(...skipping 11 matching lines...) Expand all Loading... | |
30 namespace { | 30 namespace { |
31 | 31 |
32 // Field trial constants | 32 // Field trial constants |
33 const char kThrottleCoalesceFieldTrial[] = "RequestThrottlingAndCoalescing"; | 33 const char kThrottleCoalesceFieldTrial[] = "RequestThrottlingAndCoalescing"; |
34 const char kThrottleCoalesceFieldTrialThrottle[] = "Throttle"; | 34 const char kThrottleCoalesceFieldTrialThrottle[] = "Throttle"; |
35 const char kThrottleCoalesceFieldTrialCoalesce[] = "Coalesce"; | 35 const char kThrottleCoalesceFieldTrialCoalesce[] = "Coalesce"; |
36 | 36 |
37 const char kRequestLimitFieldTrial[] = "OutstandingRequestLimiting"; | 37 const char kRequestLimitFieldTrial[] = "OutstandingRequestLimiting"; |
38 const char kRequestLimitFieldTrialGroupPrefix[] = "Limit"; | 38 const char kRequestLimitFieldTrialGroupPrefix[] = "Limit"; |
39 | 39 |
40 const char kResourcePrioritiesFieldTrial[] = "ResourcePriorities"; | |
41 | |
42 // Flags identifying various attributes of the request that are used | |
43 // when making scheduling decisions. | |
44 typedef uint8_t RequestAttributes; | |
mmenke
2015/08/13 21:27:44
nit: include <stdint.h>
mmenke
2015/08/13 21:27:45
I think "using RequestAttributes = uint8_t;" is no
Pat Meenan
2015/08/14 13:52:47
Done.
Pat Meenan
2015/08/14 13:52:47
Done.
| |
45 const RequestAttributes kAttributeNone = 0x00; | |
46 const RequestAttributes kAttributeInFlight = 0x01; | |
47 const RequestAttributes kAttributeDelayable = 0x02; | |
48 const RequestAttributes kAttributeLayoutBlocking = 0x04; | |
49 | |
40 // Post ResourceScheduler histograms of the following forms: | 50 // Post ResourceScheduler histograms of the following forms: |
41 // If |histogram_suffix| is NULL or the empty string: | 51 // If |histogram_suffix| is NULL or the empty string: |
42 // ResourceScheduler.base_name.histogram_name | 52 // ResourceScheduler.base_name.histogram_name |
43 // Else: | 53 // Else: |
44 // ResourceScheduler.base_name.histogram_name.histogram_suffix | 54 // ResourceScheduler.base_name.histogram_name.histogram_suffix |
45 void PostHistogram(const char* base_name, | 55 void PostHistogram(const char* base_name, |
46 const char* histogram_name, | 56 const char* histogram_name, |
47 const char* histogram_suffix, | 57 const char* histogram_suffix, |
48 base::TimeDelta time) { | 58 base::TimeDelta time) { |
49 std::string histogram = | 59 std::string histogram = |
(...skipping 17 matching lines...) Expand all Loading... | |
67 else if (num_clients <= 15) | 77 else if (num_clients <= 15) |
68 return "Max15Clients"; | 78 return "Max15Clients"; |
69 else if (num_clients <= 30) | 79 else if (num_clients <= 30) |
70 return "Max30Clients"; | 80 return "Max30Clients"; |
71 return "Over30Clients"; | 81 return "Over30Clients"; |
72 } | 82 } |
73 | 83 |
74 } // namespace | 84 } // namespace |
75 | 85 |
76 static const size_t kCoalescedTimerPeriod = 5000; | 86 static const size_t kCoalescedTimerPeriod = 5000; |
77 static const size_t kMaxNumDelayableRequestsPerClient = 10; | 87 static const size_t kDefaultMaxNumDelayableRequestsPerClient = 10; |
78 static const size_t kMaxNumDelayableRequestsPerHost = 6; | 88 static const size_t kMaxNumDelayableRequestsPerHost = 6; |
79 static const size_t kMaxNumThrottledRequestsPerClient = 1; | 89 static const size_t kMaxNumThrottledRequestsPerClient = 1; |
90 static const size_t kDefaultMaxNumDelayableWhileLayoutBlocking = 1; | |
91 static const net::RequestPriority | |
92 kDefaultLayoutBlockingPriorityThreshold = net::LOW; | |
80 | 93 |
81 struct ResourceScheduler::RequestPriorityParams { | 94 struct ResourceScheduler::RequestPriorityParams { |
82 RequestPriorityParams() | 95 RequestPriorityParams() |
83 : priority(net::DEFAULT_PRIORITY), | 96 : priority(net::DEFAULT_PRIORITY), |
84 intra_priority(0) { | 97 intra_priority(0) { |
85 } | 98 } |
86 | 99 |
87 RequestPriorityParams(net::RequestPriority priority, int intra_priority) | 100 RequestPriorityParams(net::RequestPriority priority, int intra_priority) |
88 : priority(priority), | 101 : priority(priority), |
89 intra_priority(intra_priority) { | 102 intra_priority(intra_priority) { |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
170 ScheduledResourceRequest(const ClientId& client_id, | 183 ScheduledResourceRequest(const ClientId& client_id, |
171 net::URLRequest* request, | 184 net::URLRequest* request, |
172 ResourceScheduler* scheduler, | 185 ResourceScheduler* scheduler, |
173 const RequestPriorityParams& priority) | 186 const RequestPriorityParams& priority) |
174 : ResourceMessageDelegate(request), | 187 : ResourceMessageDelegate(request), |
175 client_id_(client_id), | 188 client_id_(client_id), |
176 client_state_on_creation_(scheduler->GetClientState(client_id_)), | 189 client_state_on_creation_(scheduler->GetClientState(client_id_)), |
177 request_(request), | 190 request_(request), |
178 ready_(false), | 191 ready_(false), |
179 deferred_(false), | 192 deferred_(false), |
180 classification_(NORMAL_REQUEST), | 193 attributes_(kAttributeNone), |
181 scheduler_(scheduler), | 194 scheduler_(scheduler), |
182 priority_(priority), | 195 priority_(priority), |
183 fifo_ordering_(0) { | 196 fifo_ordering_(0) { |
184 } | 197 } |
185 | 198 |
186 ~ScheduledResourceRequest() override { scheduler_->RemoveRequest(this); } | 199 ~ScheduledResourceRequest() override { scheduler_->RemoveRequest(this); } |
187 | 200 |
188 void Start() { | 201 void Start() { |
189 ready_ = true; | 202 ready_ = true; |
190 if (!request_->status().is_success()) | 203 if (!request_->status().is_success()) |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
223 const RequestPriorityParams& get_request_priority_params() const { | 236 const RequestPriorityParams& get_request_priority_params() const { |
224 return priority_; | 237 return priority_; |
225 } | 238 } |
226 const ClientId& client_id() const { return client_id_; } | 239 const ClientId& client_id() const { return client_id_; } |
227 net::URLRequest* url_request() { return request_; } | 240 net::URLRequest* url_request() { return request_; } |
228 const net::URLRequest* url_request() const { return request_; } | 241 const net::URLRequest* url_request() const { return request_; } |
229 uint32 fifo_ordering() const { return fifo_ordering_; } | 242 uint32 fifo_ordering() const { return fifo_ordering_; } |
230 void set_fifo_ordering(uint32 fifo_ordering) { | 243 void set_fifo_ordering(uint32 fifo_ordering) { |
231 fifo_ordering_ = fifo_ordering; | 244 fifo_ordering_ = fifo_ordering; |
232 } | 245 } |
233 RequestClassification classification() const { | 246 RequestAttributes attributes() const { |
234 return classification_; | 247 return attributes_; |
235 } | 248 } |
236 void set_classification(RequestClassification classification) { | 249 void set_attributes(RequestAttributes attributes) { |
237 classification_ = classification; | 250 attributes_ = attributes; |
238 } | 251 } |
239 | 252 |
240 private: | 253 private: |
241 // ResourceMessageDelegate interface: | 254 // ResourceMessageDelegate interface: |
242 bool OnMessageReceived(const IPC::Message& message) override { | 255 bool OnMessageReceived(const IPC::Message& message) override { |
243 bool handled = true; | 256 bool handled = true; |
244 IPC_BEGIN_MESSAGE_MAP(ScheduledResourceRequest, message) | 257 IPC_BEGIN_MESSAGE_MAP(ScheduledResourceRequest, message) |
245 IPC_MESSAGE_HANDLER(ResourceHostMsg_DidChangePriority, DidChangePriority) | 258 IPC_MESSAGE_HANDLER(ResourceHostMsg_DidChangePriority, DidChangePriority) |
246 IPC_MESSAGE_UNHANDLED(handled = false) | 259 IPC_MESSAGE_UNHANDLED(handled = false) |
247 IPC_END_MESSAGE_MAP() | 260 IPC_END_MESSAGE_MAP() |
(...skipping 11 matching lines...) Expand all Loading... | |
259 void DidChangePriority(int request_id, net::RequestPriority new_priority, | 272 void DidChangePriority(int request_id, net::RequestPriority new_priority, |
260 int intra_priority_value) { | 273 int intra_priority_value) { |
261 scheduler_->ReprioritizeRequest(this, new_priority, intra_priority_value); | 274 scheduler_->ReprioritizeRequest(this, new_priority, intra_priority_value); |
262 } | 275 } |
263 | 276 |
264 const ClientId client_id_; | 277 const ClientId client_id_; |
265 const ResourceScheduler::ClientState client_state_on_creation_; | 278 const ResourceScheduler::ClientState client_state_on_creation_; |
266 net::URLRequest* request_; | 279 net::URLRequest* request_; |
267 bool ready_; | 280 bool ready_; |
268 bool deferred_; | 281 bool deferred_; |
269 RequestClassification classification_; | 282 RequestAttributes attributes_; |
270 ResourceScheduler* scheduler_; | 283 ResourceScheduler* scheduler_; |
271 RequestPriorityParams priority_; | 284 RequestPriorityParams priority_; |
272 uint32 fifo_ordering_; | 285 uint32 fifo_ordering_; |
273 base::TimeTicks time_deferred_; | 286 base::TimeTicks time_deferred_; |
274 | 287 |
275 DISALLOW_COPY_AND_ASSIGN(ScheduledResourceRequest); | 288 DISALLOW_COPY_AND_ASSIGN(ScheduledResourceRequest); |
276 }; | 289 }; |
277 | 290 |
278 bool ResourceScheduler::ScheduledResourceSorter::operator()( | 291 bool ResourceScheduler::ScheduledResourceSorter::operator()( |
279 const ScheduledResourceRequest* a, | 292 const ScheduledResourceRequest* a, |
(...skipping 21 matching lines...) Expand all Loading... | |
301 // Each client represents a tab. | 314 // Each client represents a tab. |
302 class ResourceScheduler::Client { | 315 class ResourceScheduler::Client { |
303 public: | 316 public: |
304 explicit Client(ResourceScheduler* scheduler, | 317 explicit Client(ResourceScheduler* scheduler, |
305 bool is_visible, | 318 bool is_visible, |
306 bool is_audible) | 319 bool is_audible) |
307 : is_audible_(is_audible), | 320 : is_audible_(is_audible), |
308 is_visible_(is_visible), | 321 is_visible_(is_visible), |
309 is_loaded_(false), | 322 is_loaded_(false), |
310 is_paused_(false), | 323 is_paused_(false), |
311 has_body_(false), | 324 has_html_body_(false), |
312 using_spdy_proxy_(false), | 325 using_spdy_proxy_(false), |
313 load_started_time_(base::TimeTicks::Now()), | 326 load_started_time_(base::TimeTicks::Now()), |
314 scheduler_(scheduler), | 327 scheduler_(scheduler), |
315 in_flight_delayable_count_(0), | 328 in_flight_delayable_count_(0), |
316 total_layout_blocking_count_(0), | 329 total_layout_blocking_count_(0), |
317 throttle_state_(ResourceScheduler::THROTTLED) {} | 330 throttle_state_(ResourceScheduler::THROTTLED) { |
331 } | |
318 | 332 |
319 ~Client() { | 333 ~Client() { |
320 // Update to default state and pause to ensure the scheduler has a | 334 // Update to default state and pause to ensure the scheduler has a |
321 // correct count of relevant types of clients. | 335 // correct count of relevant types of clients. |
322 is_visible_ = false; | 336 is_visible_ = false; |
323 is_audible_ = false; | 337 is_audible_ = false; |
324 is_paused_ = true; | 338 is_paused_ = true; |
325 UpdateThrottleState(); | 339 UpdateThrottleState(); |
326 } | 340 } |
327 | 341 |
328 void ScheduleRequest( | 342 void ScheduleRequest( |
329 net::URLRequest* url_request, | 343 net::URLRequest* url_request, |
330 ScheduledResourceRequest* request) { | 344 ScheduledResourceRequest* request) { |
345 SetRequestAttributes(request, DetermineRequestAttributes(request)); | |
331 if (ShouldStartRequest(request) == START_REQUEST) | 346 if (ShouldStartRequest(request) == START_REQUEST) |
332 StartRequest(request); | 347 StartRequest(request); |
333 else | 348 else |
334 pending_requests_.Insert(request); | 349 pending_requests_.Insert(request); |
335 SetRequestClassification(request, ClassifyRequest(request)); | |
336 } | 350 } |
337 | 351 |
338 void RemoveRequest(ScheduledResourceRequest* request) { | 352 void RemoveRequest(ScheduledResourceRequest* request) { |
339 if (pending_requests_.IsQueued(request)) { | 353 if (pending_requests_.IsQueued(request)) { |
340 pending_requests_.Erase(request); | 354 pending_requests_.Erase(request); |
341 DCHECK(!ContainsKey(in_flight_requests_, request)); | 355 DCHECK(!ContainsKey(in_flight_requests_, request)); |
342 } else { | 356 } else { |
343 EraseInFlightRequest(request); | 357 EraseInFlightRequest(request); |
344 | 358 |
345 // Removing this request may have freed up another to load. | 359 // Removing this request may have freed up another to load. |
346 LoadAnyStartablePendingRequests(); | 360 LoadAnyStartablePendingRequests(); |
347 } | 361 } |
348 } | 362 } |
349 | 363 |
350 RequestSet StartAndRemoveAllRequests() { | 364 RequestSet StartAndRemoveAllRequests() { |
351 // First start any pending requests so that they will be moved into | 365 // First start any pending requests so that they will be moved into |
352 // in_flight_requests_. This may exceed the limits | 366 // in_flight_requests_. This may exceed the limits |
353 // kMaxNumDelayableRequestsPerClient, kMaxNumDelayableRequestsPerHost and | 367 // kDefaultMaxNumDelayableRequestsPerClient, kMaxNumDelayableRequestsPerHost |
354 // kMaxNumThrottledRequestsPerClient, so this method must not do anything | 368 // and kMaxNumThrottledRequestsPerClient, so this method must not do |
355 // that depends on those limits before calling ClearInFlightRequests() | 369 // anything that depends on those limits before calling |
356 // below. | 370 // ClearInFlightRequests() below. |
357 while (!pending_requests_.IsEmpty()) { | 371 while (!pending_requests_.IsEmpty()) { |
358 ScheduledResourceRequest* request = | 372 ScheduledResourceRequest* request = |
359 *pending_requests_.GetNextHighestIterator(); | 373 *pending_requests_.GetNextHighestIterator(); |
360 pending_requests_.Erase(request); | 374 pending_requests_.Erase(request); |
361 // StartRequest() may modify pending_requests_. TODO(ricea): Does it? | 375 // StartRequest() may modify pending_requests_. TODO(ricea): Does it? |
362 StartRequest(request); | 376 StartRequest(request); |
363 } | 377 } |
364 RequestSet unowned_requests; | 378 RequestSet unowned_requests; |
365 for (RequestSet::iterator it = in_flight_requests_.begin(); | 379 for (RequestSet::iterator it = in_flight_requests_.begin(); |
366 it != in_flight_requests_.end(); ++it) { | 380 it != in_flight_requests_.end(); ++it) { |
367 unowned_requests.insert(*it); | 381 unowned_requests.insert(*it); |
368 (*it)->set_classification(NORMAL_REQUEST); | 382 (*it)->set_attributes(kAttributeNone); |
369 } | 383 } |
370 ClearInFlightRequests(); | 384 ClearInFlightRequests(); |
371 return unowned_requests; | 385 return unowned_requests; |
372 } | 386 } |
373 | 387 |
374 bool is_active() const { return is_visible_ || is_audible_; } | 388 bool is_active() const { return is_visible_ || is_audible_; } |
375 | 389 |
376 bool is_loaded() const { return is_loaded_; } | 390 bool is_loaded() const { return is_loaded_; } |
377 | 391 |
378 bool is_visible() const { return is_visible_; } | 392 bool is_visible() const { return is_visible_; } |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
463 scheduler_->DecrementActiveClientsLoading(); | 477 scheduler_->DecrementActiveClientsLoading(); |
464 } | 478 } |
465 if (throttle_state_ == COALESCED) { | 479 if (throttle_state_ == COALESCED) { |
466 scheduler_->IncrementCoalescedClients(); | 480 scheduler_->IncrementCoalescedClients(); |
467 } else if (old_throttle_state == COALESCED) { | 481 } else if (old_throttle_state == COALESCED) { |
468 scheduler_->DecrementCoalescedClients(); | 482 scheduler_->DecrementCoalescedClients(); |
469 } | 483 } |
470 } | 484 } |
471 | 485 |
472 void OnNavigate() { | 486 void OnNavigate() { |
473 has_body_ = false; | 487 has_html_body_ = false; |
474 is_loaded_ = false; | 488 is_loaded_ = false; |
475 } | 489 } |
476 | 490 |
477 void OnWillInsertBody() { | 491 void OnWillInsertBody() { |
478 has_body_ = true; | 492 has_html_body_ = true; |
479 LoadAnyStartablePendingRequests(); | 493 LoadAnyStartablePendingRequests(); |
480 } | 494 } |
481 | 495 |
482 void OnReceivedSpdyProxiedHttpResponse() { | 496 void OnReceivedSpdyProxiedHttpResponse() { |
483 if (!using_spdy_proxy_) { | 497 if (!using_spdy_proxy_) { |
484 using_spdy_proxy_ = true; | 498 using_spdy_proxy_ = true; |
485 LoadAnyStartablePendingRequests(); | 499 LoadAnyStartablePendingRequests(); |
486 } | 500 } |
487 } | 501 } |
488 | 502 |
489 void ReprioritizeRequest(ScheduledResourceRequest* request, | 503 void ReprioritizeRequest(ScheduledResourceRequest* request, |
490 RequestPriorityParams old_priority_params, | 504 RequestPriorityParams old_priority_params, |
491 RequestPriorityParams new_priority_params) { | 505 RequestPriorityParams new_priority_params) { |
492 request->url_request()->SetPriority(new_priority_params.priority); | 506 request->url_request()->SetPriority(new_priority_params.priority); |
493 request->set_request_priority_params(new_priority_params); | 507 request->set_request_priority_params(new_priority_params); |
508 SetRequestAttributes(request, DetermineRequestAttributes(request)); | |
494 if (!pending_requests_.IsQueued(request)) { | 509 if (!pending_requests_.IsQueued(request)) { |
495 DCHECK(ContainsKey(in_flight_requests_, request)); | 510 DCHECK(ContainsKey(in_flight_requests_, request)); |
496 // The priority of the request and priority support of the server may | |
497 // have changed, so update the delayable count. | |
498 SetRequestClassification(request, ClassifyRequest(request)); | |
499 // Request has already started. | 511 // Request has already started. |
500 return; | 512 return; |
501 } | 513 } |
502 | 514 |
503 pending_requests_.Erase(request); | 515 pending_requests_.Erase(request); |
504 pending_requests_.Insert(request); | 516 pending_requests_.Insert(request); |
505 | 517 |
506 if (new_priority_params.priority > old_priority_params.priority) { | 518 if (new_priority_params.priority > old_priority_params.priority) { |
507 // Check if this request is now able to load at its new priority. | 519 // Check if this request is now able to load at its new priority. |
508 LoadAnyStartablePendingRequests(); | 520 LoadAnyStartablePendingRequests(); |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
555 | 567 |
556 private: | 568 private: |
557 enum ShouldStartReqResult { | 569 enum ShouldStartReqResult { |
558 DO_NOT_START_REQUEST_AND_STOP_SEARCHING, | 570 DO_NOT_START_REQUEST_AND_STOP_SEARCHING, |
559 DO_NOT_START_REQUEST_AND_KEEP_SEARCHING, | 571 DO_NOT_START_REQUEST_AND_KEEP_SEARCHING, |
560 START_REQUEST, | 572 START_REQUEST, |
561 }; | 573 }; |
562 | 574 |
563 void InsertInFlightRequest(ScheduledResourceRequest* request) { | 575 void InsertInFlightRequest(ScheduledResourceRequest* request) { |
564 in_flight_requests_.insert(request); | 576 in_flight_requests_.insert(request); |
565 SetRequestClassification(request, ClassifyRequest(request)); | 577 SetRequestAttributes(request, DetermineRequestAttributes(request)); |
566 } | 578 } |
567 | 579 |
568 void EraseInFlightRequest(ScheduledResourceRequest* request) { | 580 void EraseInFlightRequest(ScheduledResourceRequest* request) { |
569 size_t erased = in_flight_requests_.erase(request); | 581 size_t erased = in_flight_requests_.erase(request); |
570 DCHECK_EQ(1u, erased); | 582 DCHECK_EQ(1u, erased); |
571 // Clear any special state that we were tracking for this request. | 583 // Clear any special state that we were tracking for this request. |
572 SetRequestClassification(request, NORMAL_REQUEST); | 584 SetRequestAttributes(request, kAttributeNone); |
573 } | 585 } |
574 | 586 |
575 void ClearInFlightRequests() { | 587 void ClearInFlightRequests() { |
576 in_flight_requests_.clear(); | 588 in_flight_requests_.clear(); |
577 in_flight_delayable_count_ = 0; | 589 in_flight_delayable_count_ = 0; |
578 total_layout_blocking_count_ = 0; | 590 total_layout_blocking_count_ = 0; |
579 } | 591 } |
580 | 592 |
581 size_t CountRequestsWithClassification( | 593 size_t CountRequestsWithAttributes( |
582 const RequestClassification classification, const bool include_pending) { | 594 const RequestAttributes attributes, |
583 size_t classification_request_count = 0; | 595 const bool include_pending) { |
596 size_t matching_request_count = 0; | |
584 for (RequestSet::const_iterator it = in_flight_requests_.begin(); | 597 for (RequestSet::const_iterator it = in_flight_requests_.begin(); |
585 it != in_flight_requests_.end(); ++it) { | 598 it != in_flight_requests_.end(); ++it) { |
586 if ((*it)->classification() == classification) | 599 if (RequestAttributesAreSet((*it)->attributes(), attributes)) |
587 classification_request_count++; | 600 matching_request_count++; |
588 } | 601 } |
589 if (include_pending) { | 602 if (include_pending) { |
590 for (RequestQueue::NetQueue::const_iterator | 603 for (RequestQueue::NetQueue::const_iterator |
591 it = pending_requests_.GetNextHighestIterator(); | 604 it = pending_requests_.GetNextHighestIterator(); |
592 it != pending_requests_.End(); ++it) { | 605 it != pending_requests_.End(); ++it) { |
593 if ((*it)->classification() == classification) | 606 if (RequestAttributesAreSet((*it)->attributes(), attributes)) |
594 classification_request_count++; | 607 matching_request_count++; |
595 } | 608 } |
596 } | 609 } |
597 return classification_request_count; | 610 return matching_request_count; |
598 } | 611 } |
599 | 612 |
600 void SetRequestClassification(ScheduledResourceRequest* request, | 613 bool RequestAttributesAreSet(RequestAttributes request_attributes, |
601 RequestClassification classification) { | 614 RequestAttributes matching_attributes) const { |
602 RequestClassification old_classification = request->classification(); | 615 return (request_attributes & matching_attributes) == matching_attributes; |
603 if (old_classification == classification) | 616 } |
617 | |
618 void SetRequestAttributes(ScheduledResourceRequest* request, | |
619 RequestAttributes attributes) { | |
620 RequestAttributes old_attributes = request->attributes(); | |
621 if (old_attributes == attributes) | |
604 return; | 622 return; |
605 | 623 |
606 if (old_classification == IN_FLIGHT_DELAYABLE_REQUEST) | 624 if (RequestAttributesAreSet(old_attributes, |
625 kAttributeInFlight | kAttributeDelayable)) { | |
mmenke
2015/08/13 21:27:45
nit: +1 indent
Pat Meenan
2015/08/14 13:52:47
Done.
| |
607 in_flight_delayable_count_--; | 626 in_flight_delayable_count_--; |
608 if (old_classification == LAYOUT_BLOCKING_REQUEST) | 627 } |
628 if (RequestAttributesAreSet(old_attributes, kAttributeLayoutBlocking)) | |
609 total_layout_blocking_count_--; | 629 total_layout_blocking_count_--; |
610 | 630 |
611 if (classification == IN_FLIGHT_DELAYABLE_REQUEST) | 631 if (RequestAttributesAreSet(attributes, |
632 kAttributeInFlight | kAttributeDelayable)) { | |
mmenke
2015/08/13 21:27:45
nit: +1 indent
Pat Meenan
2015/08/14 13:52:47
Done.
| |
612 in_flight_delayable_count_++; | 633 in_flight_delayable_count_++; |
613 if (classification == LAYOUT_BLOCKING_REQUEST) | 634 } |
635 if (RequestAttributesAreSet(attributes, kAttributeLayoutBlocking)) | |
614 total_layout_blocking_count_++; | 636 total_layout_blocking_count_++; |
615 | 637 |
616 request->set_classification(classification); | 638 request->set_attributes(attributes); |
617 DCHECK_EQ( | 639 DCHECK_EQ(CountRequestsWithAttributes( |
618 CountRequestsWithClassification(IN_FLIGHT_DELAYABLE_REQUEST, false), | 640 kAttributeInFlight | kAttributeDelayable, false), |
619 in_flight_delayable_count_); | 641 in_flight_delayable_count_); |
620 DCHECK_EQ(CountRequestsWithClassification(LAYOUT_BLOCKING_REQUEST, true), | 642 DCHECK_EQ(CountRequestsWithAttributes(kAttributeLayoutBlocking, true), |
621 total_layout_blocking_count_); | 643 total_layout_blocking_count_); |
622 } | 644 } |
623 | 645 |
624 RequestClassification ClassifyRequest(ScheduledResourceRequest* request) { | 646 RequestAttributes DetermineRequestAttributes( |
625 // If a request is already marked as layout-blocking make sure to keep the | 647 ScheduledResourceRequest* request) { |
626 // classification across redirects unless the priority was lowered. | 648 RequestAttributes attributes = kAttributeNone; |
627 if (request->classification() == LAYOUT_BLOCKING_REQUEST && | |
628 request->url_request()->priority() > net::LOW) { | |
629 return LAYOUT_BLOCKING_REQUEST; | |
630 } | |
631 | 649 |
632 if (!has_body_ && request->url_request()->priority() > net::LOW) | 650 if (ContainsKey(in_flight_requests_, request)) |
633 return LAYOUT_BLOCKING_REQUEST; | 651 attributes = attributes | kAttributeInFlight; |
mmenke
2015/08/13 21:27:45
Any reason not to use |= on all of these?
Pat Meenan
2015/08/14 13:52:47
I wasn't sure if it would look too much like != an
| |
634 | 652 |
635 if (request->url_request()->priority() < net::LOW) { | 653 if (RequestAttributesAreSet(request->attributes(), |
654 kAttributeLayoutBlocking)) { | |
655 // If a request is already marked as layout-blocking make sure to keep the | |
656 // attribute across redirects. | |
657 attributes = attributes | kAttributeLayoutBlocking; | |
658 } else if (!has_html_body_ && | |
659 request->url_request()->priority() > | |
660 scheduler_->non_delayable_threshold()) { | |
661 // Requests that are above the non_delayable threshold before the HTML | |
662 // body has been parsed are inferred to be layout-blocking. | |
663 attributes = attributes | kAttributeLayoutBlocking; | |
664 } else if (request->url_request()->priority() < | |
665 scheduler_->non_delayable_threshold()) { | |
666 // Resources below the non_delayable_threshold that are being requested | |
667 // from a server that does not support native prioritization are | |
668 // considered delayable. | |
636 net::HostPortPair host_port_pair = | 669 net::HostPortPair host_port_pair = |
637 net::HostPortPair::FromURL(request->url_request()->url()); | 670 net::HostPortPair::FromURL(request->url_request()->url()); |
638 net::HttpServerProperties& http_server_properties = | 671 net::HttpServerProperties& http_server_properties = |
639 *request->url_request()->context()->http_server_properties(); | 672 *request->url_request()->context()->http_server_properties(); |
640 if (!http_server_properties.SupportsRequestPriority(host_port_pair) && | 673 if (!http_server_properties.SupportsRequestPriority(host_port_pair)) |
641 ContainsKey(in_flight_requests_, request)) { | 674 attributes = attributes | kAttributeDelayable; |
642 return IN_FLIGHT_DELAYABLE_REQUEST; | |
643 } | |
644 } | 675 } |
645 return NORMAL_REQUEST; | 676 |
677 return attributes; | |
646 } | 678 } |
647 | 679 |
648 bool ShouldKeepSearching( | 680 bool ShouldKeepSearching( |
649 const net::HostPortPair& active_request_host) const { | 681 const net::HostPortPair& active_request_host) const { |
650 size_t same_host_count = 0; | 682 size_t same_host_count = 0; |
651 for (RequestSet::const_iterator it = in_flight_requests_.begin(); | 683 for (RequestSet::const_iterator it = in_flight_requests_.begin(); |
652 it != in_flight_requests_.end(); ++it) { | 684 it != in_flight_requests_.end(); ++it) { |
653 net::HostPortPair host_port_pair = | 685 net::HostPortPair host_port_pair = |
654 net::HostPortPair::FromURL((*it)->url_request()->url()); | 686 net::HostPortPair::FromURL((*it)->url_request()->url()); |
655 if (active_request_host.Equals(host_port_pair)) { | 687 if (active_request_host.Equals(host_port_pair)) { |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
761 return START_REQUEST; | 793 return START_REQUEST; |
762 } | 794 } |
763 | 795 |
764 if (throttle_state_ == THROTTLED && | 796 if (throttle_state_ == THROTTLED && |
765 in_flight_requests_.size() >= kMaxNumThrottledRequestsPerClient) { | 797 in_flight_requests_.size() >= kMaxNumThrottledRequestsPerClient) { |
766 // There may still be request-priority-capable requests that should be | 798 // There may still be request-priority-capable requests that should be |
767 // issued. | 799 // issued. |
768 return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING; | 800 return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING; |
769 } | 801 } |
770 | 802 |
771 // High-priority and layout-blocking requests. | 803 // non-delayable requests. |
mmenke
2015/08/13 21:27:45
Nit: "Non-delayable requests." (capitalize)
Pat Meenan
2015/08/14 13:52:47
Done.
| |
772 if (url_request.priority() >= net::LOW) { | 804 if (!RequestAttributesAreSet(request->attributes(), kAttributeDelayable)) { |
773 return START_REQUEST; | 805 return START_REQUEST; |
774 } | 806 } |
mmenke
2015/08/13 21:27:45
nit: Remove braces
Pat Meenan
2015/08/14 13:52:47
I had them to be consistent with the rest of the f
| |
775 | 807 |
776 if (in_flight_delayable_count_ >= kMaxNumDelayableRequestsPerClient) { | 808 if (in_flight_delayable_count_ >= |
809 scheduler_->max_num_delayable_requests()) { | |
777 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; | 810 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; |
778 } | 811 } |
779 | 812 |
780 if (ShouldKeepSearching(host_port_pair)) { | 813 if (ShouldKeepSearching(host_port_pair)) { |
781 // There may be other requests for other hosts we'd allow, | 814 // There may be other requests for other hosts we'd allow, |
782 // so keep checking. | 815 // so keep checking. |
783 return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING; | 816 return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING; |
784 } | 817 } |
785 | 818 |
786 bool have_immediate_requests_in_flight = | 819 // The in-flight requests consist of layout-blocking requests, |
787 in_flight_requests_.size() > in_flight_delayable_count_; | 820 // normal requests and delayable requests. Everything except for |
788 if (have_immediate_requests_in_flight && | 821 // delayable requests is handled above here so this is deciding what to |
789 (!has_body_ || total_layout_blocking_count_ != 0) && | 822 // do with a delayable request while we are in the layout-blocking phase |
790 // Do not allow a low priority request through in parallel if | 823 // of loading. |
791 // we are in a limit field trial. | 824 if (!has_html_body_ || total_layout_blocking_count_ != 0) { |
792 (scheduler_->limit_outstanding_requests() || | 825 size_t non_delayable_requests_in_flight_count = |
793 in_flight_delayable_count_ != 0)) { | 826 in_flight_requests_.size() - in_flight_delayable_count_; |
794 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; | 827 if (scheduler_->enable_in_flight_non_delayable_threshold()) { |
828 if (non_delayable_requests_in_flight_count > | |
829 scheduler_->in_flight_non_delayable_threshold()) { | |
830 // Too many higher priority in-flight requests to allow lower priority | |
831 // requests through. | |
832 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; | |
833 } | |
834 if (in_flight_requests_.size() > 0 && | |
835 (scheduler_->limit_outstanding_requests() || | |
836 in_flight_delayable_count_ >= | |
837 scheduler_->max_num_delayable_while_layout_blocking())) { | |
838 // Block the request if at least one request is in flight and the | |
839 // number of in-flight delayable requests has hit the configured | |
840 // limit. | |
841 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; | |
842 } | |
843 } else if (non_delayable_requests_in_flight_count > 0 && | |
844 (scheduler_->limit_outstanding_requests() || | |
845 in_flight_delayable_count_ >= | |
846 scheduler_->max_num_delayable_while_layout_blocking())) { | |
847 // If there are no high-priority requests in flight the floodgates open. | |
848 // If there are high-priority requests in-flight then limit the number | |
849 // of lower-priority requests (or zero if a limit field trial is | |
850 // active). | |
851 return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; | |
852 } | |
795 } | 853 } |
796 | 854 |
797 return START_REQUEST; | 855 return START_REQUEST; |
798 } | 856 } |
799 | 857 |
800 void LoadAnyStartablePendingRequests() { | 858 void LoadAnyStartablePendingRequests() { |
801 // We iterate through all the pending requests, starting with the highest | 859 // We iterate through all the pending requests, starting with the highest |
802 // priority one. For each entry, one of three things can happen: | 860 // priority one. For each entry, one of three things can happen: |
803 // 1) We start the request, remove it from the list, and keep checking. | 861 // 1) We start the request, remove it from the list, and keep checking. |
804 // 2) We do NOT start the request, but ShouldStartRequest() signals us that | 862 // 2) We do NOT start the request, but ShouldStartRequest() signals us that |
(...skipping 26 matching lines...) Expand all Loading... | |
831 DCHECK(query_result == DO_NOT_START_REQUEST_AND_STOP_SEARCHING); | 889 DCHECK(query_result == DO_NOT_START_REQUEST_AND_STOP_SEARCHING); |
832 break; | 890 break; |
833 } | 891 } |
834 } | 892 } |
835 } | 893 } |
836 | 894 |
837 bool is_audible_; | 895 bool is_audible_; |
838 bool is_visible_; | 896 bool is_visible_; |
839 bool is_loaded_; | 897 bool is_loaded_; |
840 bool is_paused_; | 898 bool is_paused_; |
841 bool has_body_; | 899 // Tracks if the main HTML parser has reached the body which marks the end of |
900 // layout-blocking resources. | |
901 bool has_html_body_; | |
842 bool using_spdy_proxy_; | 902 bool using_spdy_proxy_; |
843 RequestQueue pending_requests_; | 903 RequestQueue pending_requests_; |
844 RequestSet in_flight_requests_; | 904 RequestSet in_flight_requests_; |
845 base::TimeTicks load_started_time_; | 905 base::TimeTicks load_started_time_; |
846 // The last time the client switched state between active and background. | 906 // The last time the client switched state between active and background. |
847 base::TimeTicks last_active_switch_time_; | 907 base::TimeTicks last_active_switch_time_; |
848 ResourceScheduler* scheduler_; | 908 ResourceScheduler* scheduler_; |
849 // The number of delayable in-flight requests. | 909 // The number of delayable in-flight requests. |
850 size_t in_flight_delayable_count_; | 910 size_t in_flight_delayable_count_; |
851 // The number of layout-blocking in-flight requests. | 911 // The number of layout-blocking in-flight requests. |
852 size_t total_layout_blocking_count_; | 912 size_t total_layout_blocking_count_; |
853 ResourceScheduler::ClientThrottleState throttle_state_; | 913 ResourceScheduler::ClientThrottleState throttle_state_; |
854 }; | 914 }; |
855 | 915 |
856 ResourceScheduler::ResourceScheduler() | 916 ResourceScheduler::ResourceScheduler() |
857 : should_coalesce_(false), | 917 : should_coalesce_(false), |
858 should_throttle_(false), | 918 should_throttle_(false), |
859 active_clients_loading_(0), | 919 active_clients_loading_(0), |
860 coalesced_clients_(0), | 920 coalesced_clients_(0), |
861 limit_outstanding_requests_(false), | 921 limit_outstanding_requests_(false), |
862 outstanding_request_limit_(0), | 922 outstanding_request_limit_(0), |
923 non_delayable_threshold_( | |
924 kDefaultLayoutBlockingPriorityThreshold), | |
925 enable_in_flight_non_delayable_threshold_(false), | |
926 in_flight_non_delayable_threshold_(0), | |
927 max_num_delayable_while_layout_blocking_( | |
928 kDefaultMaxNumDelayableWhileLayoutBlocking), | |
929 max_num_delayable_requests_(kDefaultMaxNumDelayableRequestsPerClient), | |
863 coalescing_timer_(new base::Timer(true /* retain_user_task */, | 930 coalescing_timer_(new base::Timer(true /* retain_user_task */, |
864 true /* is_repeating */)) { | 931 true /* is_repeating */)) { |
865 std::string throttling_trial_group = | 932 std::string throttling_trial_group = |
866 base::FieldTrialList::FindFullName(kThrottleCoalesceFieldTrial); | 933 base::FieldTrialList::FindFullName(kThrottleCoalesceFieldTrial); |
867 if (throttling_trial_group == kThrottleCoalesceFieldTrialThrottle) { | 934 if (throttling_trial_group == kThrottleCoalesceFieldTrialThrottle) { |
868 should_throttle_ = true; | 935 should_throttle_ = true; |
869 } else if (throttling_trial_group == kThrottleCoalesceFieldTrialCoalesce) { | 936 } else if (throttling_trial_group == kThrottleCoalesceFieldTrialCoalesce) { |
870 should_coalesce_ = true; | 937 should_coalesce_ = true; |
871 should_throttle_ = true; | 938 should_throttle_ = true; |
872 } | 939 } |
873 | 940 |
874 std::string outstanding_limit_trial_group = | 941 std::string outstanding_limit_trial_group = |
875 base::FieldTrialList::FindFullName(kRequestLimitFieldTrial); | 942 base::FieldTrialList::FindFullName(kRequestLimitFieldTrial); |
876 std::vector<std::string> split_group( | 943 std::vector<std::string> split_group( |
877 base::SplitString(outstanding_limit_trial_group, "=", | 944 base::SplitString(outstanding_limit_trial_group, "=", |
878 base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL)); | 945 base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL)); |
879 int outstanding_limit = 0; | 946 int outstanding_limit = 0; |
880 if (split_group.size() == 2 && | 947 if (split_group.size() == 2 && |
881 split_group[0] == kRequestLimitFieldTrialGroupPrefix && | 948 split_group[0] == kRequestLimitFieldTrialGroupPrefix && |
882 base::StringToInt(split_group[1], &outstanding_limit) && | 949 base::StringToInt(split_group[1], &outstanding_limit) && |
883 outstanding_limit > 0) { | 950 outstanding_limit > 0) { |
884 limit_outstanding_requests_ = true; | 951 limit_outstanding_requests_ = true; |
885 outstanding_request_limit_ = outstanding_limit; | 952 outstanding_request_limit_ = outstanding_limit; |
886 } | 953 } |
954 | |
955 // Set up the ResourceScheduling field trial options. | |
956 // The field trial parameters are also encoded into the group name since | |
957 // the variations component is not available from here and plumbing the | |
958 // options through the code is overkill for a short experiment. | |
959 // | |
960 // The group name encoding looks like this: | |
961 // <descriptiveName>_ABCDE_E2_F_G | |
962 // A - fetchDeferLateScripts (1 for true, 0 for false) | |
963 // B - fetchIncreaseFontPriority (1 for true, 0 for false) | |
964 // C - fetchIncreaseAsyncScriptPriority (1 for true, 0 for false) | |
965 // D - fetchIncreasePriorities (1 for true, 0 for false) | |
966 // E - fetchEnableLayoutBlockingThreshold (1 for true, 0 for false) | |
967 // E2 - fetchLayoutBlockingThreshold (Numeric) | |
968 // F - fetchMaxNumDelayableWhileLayoutBlocking (Numeric) | |
969 // G - fetchMaxNumDelayableRequests (Numeric) | |
970 std::string resource_priorities_trial_group = | |
971 base::FieldTrialList::FindFullName(kResourcePrioritiesFieldTrial); | |
972 std::vector<std::string> resource_priorities_split_group( | |
973 base::SplitString(resource_priorities_trial_group, "_", | |
974 base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL)); | |
975 if (resource_priorities_split_group.size() == 5 && | |
976 resource_priorities_split_group[1].length() == 5) { | |
977 // fetchIncreasePriorities | |
978 if (resource_priorities_split_group[1].at(3) == '1') | |
979 non_delayable_threshold_ = net::MEDIUM; | |
980 enable_in_flight_non_delayable_threshold_ = | |
981 resource_priorities_split_group[1].at(4) == '1'; | |
982 size_t numeric_value = 0; | |
983 if (base::StringToSizeT(resource_priorities_split_group[2], &numeric_value)) | |
984 in_flight_non_delayable_threshold_ = numeric_value; | |
985 if (base::StringToSizeT(resource_priorities_split_group[3], &numeric_value)) | |
986 max_num_delayable_while_layout_blocking_ = numeric_value; | |
987 if (base::StringToSizeT(resource_priorities_split_group[4], &numeric_value)) | |
988 max_num_delayable_requests_ = numeric_value; | |
989 } | |
887 } | 990 } |
888 | 991 |
889 ResourceScheduler::~ResourceScheduler() { | 992 ResourceScheduler::~ResourceScheduler() { |
890 DCHECK(unowned_requests_.empty()); | 993 DCHECK(unowned_requests_.empty()); |
891 DCHECK(client_map_.empty()); | 994 DCHECK(client_map_.empty()); |
892 } | 995 } |
893 | 996 |
894 void ResourceScheduler::SetThrottleOptionsForTesting(bool should_throttle, | 997 void ResourceScheduler::SetThrottleOptionsForTesting(bool should_throttle, |
895 bool should_coalesce) { | 998 bool should_coalesce) { |
896 should_coalesce_ = should_coalesce; | 999 should_coalesce_ = should_coalesce; |
(...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1202 client->ReprioritizeRequest( | 1305 client->ReprioritizeRequest( |
1203 request, old_priority_params, new_priority_params); | 1306 request, old_priority_params, new_priority_params); |
1204 } | 1307 } |
1205 | 1308 |
1206 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( | 1309 ResourceScheduler::ClientId ResourceScheduler::MakeClientId( |
1207 int child_id, int route_id) { | 1310 int child_id, int route_id) { |
1208 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; | 1311 return (static_cast<ResourceScheduler::ClientId>(child_id) << 32) | route_id; |
1209 } | 1312 } |
1210 | 1313 |
1211 } // namespace content | 1314 } // namespace content |
OLD | NEW |