Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(337)

Unified Diff: content/browser/loader/resource_scheduler.cc

Issue 357583003: Adding observable throttle logic without the signals. Defaults to current behavior. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Underflow check added. Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/browser/loader/resource_scheduler.h ('k') | content/browser/loader/resource_scheduler_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/browser/loader/resource_scheduler.cc
diff --git a/content/browser/loader/resource_scheduler.cc b/content/browser/loader/resource_scheduler.cc
index 680ddfee3c5944aa913af4162c872a47736a095c..f21dd4badfadafc10f5b15e2dce73e9e003200d3 100644
--- a/content/browser/loader/resource_scheduler.cc
+++ b/content/browser/loader/resource_scheduler.cc
@@ -24,7 +24,7 @@ namespace content {
static const size_t kMaxNumDelayableRequestsPerClient = 10;
static const size_t kMaxNumDelayableRequestsPerHost = 6;
-
+static const size_t kMaxNumThrottledRequestsPerClient = 1;
struct ResourceScheduler::RequestPriorityParams {
RequestPriorityParams()
@@ -229,10 +229,16 @@ void ResourceScheduler::RequestQueue::Insert(
// Each client represents a tab.
class ResourceScheduler::Client {
public:
- Client()
- : has_body_(false),
+ explicit Client(ResourceScheduler* scheduler)
+ : is_audible_(false),
+ is_visible_(false),
+ is_loaded_(false),
+ has_body_(false),
using_spdy_proxy_(false),
- total_delayable_count_(0) {}
+ total_delayable_count_(0),
+ throttle_state_(ResourceScheduler::THROTTLED) {
+ scheduler_ = scheduler;
+ }
~Client() {}
void ScheduleRequest(
@@ -268,8 +274,60 @@ class ResourceScheduler::Client {
return unowned_requests;
}
+ bool is_active() const { return is_visible_ || is_audible_; }
+
+ bool is_loaded() const { return is_loaded_; }
+
+ void OnAudibilityChanged(bool is_audible) {
+ if (is_audible == is_audible_) {
+ return;
+ }
+ is_audible_ = is_audible;
+ UpdateThrottleState();
+ }
+
+ void OnVisibilityChanged(bool is_visible) {
+ if (is_visible == is_visible_) {
+ return;
+ }
+ is_visible_ = is_visible;
+ UpdateThrottleState();
+ }
+
+ void OnLoadingStateChanged(bool is_loaded) {
+ if (is_loaded == is_loaded_) {
+ return;
+ }
+ is_loaded_ = is_loaded;
+ UpdateThrottleState();
+ }
+
+ void UpdateThrottleState() {
+ ClientThrottleState old_throttle_state = throttle_state_;
+ if (is_active() && !is_loaded_) {
+ SetThrottleState(ACTIVE_AND_LOADING);
+ } else if (is_active()) {
+ SetThrottleState(UNTHROTTLED);
+ } else if (!scheduler_->active_clients_loaded()) {
+ SetThrottleState(THROTTLED);
+ } else if (is_loaded_ && scheduler_->should_coalesce()) {
+ SetThrottleState(COALESCED);
+ } else if (!is_active()) {
+ SetThrottleState(UNTHROTTLED);
+ }
+ if (throttle_state_ == old_throttle_state) {
+ return;
+ }
+ if (throttle_state_ == ACTIVE_AND_LOADING) {
+ scheduler_->IncrementActiveClientsLoading();
+ } else if (old_throttle_state == ACTIVE_AND_LOADING) {
+ scheduler_->DecrementActiveClientsLoading();
+ }
+ }
+
void OnNavigate() {
has_body_ = false;
+ is_loaded_ = false;
}
void OnWillInsertBody() {
@@ -307,6 +365,47 @@ class ResourceScheduler::Client {
}
}
+ // Called on Client creation, when a Client changes user observability,
+ // possibly when all observable Clients have finished loading, and
+ // possibly when this Client has finished loading.
+ // State changes:
+ // Client became observable.
+ // any state -> UNTHROTTLED
+ // Client is unobservable, but all observable clients finished loading.
+ // THROTTLED -> UNTHROTTLED
+ // Non-observable client finished loading.
+ // THROTTLED || UNTHROTTLED -> COALESCED
+ // Non-observable client, an observable client starts loading.
+ // COALESCED -> THROTTLED
+ // A COALESCED client will transition into UNTHROTTLED when the network is
+ // woken up by a heartbeat and then transition back into COALESCED.
+ void SetThrottleState(ResourceScheduler::ClientThrottleState throttle_state) {
+ if (throttle_state == throttle_state_) {
+ return;
+ }
+ throttle_state_ = throttle_state;
+ LoadAnyStartablePendingRequests();
+ // TODO(aiolos): Stop any started but not inflght requests when
+ // switching to stricter throttle state?
+ }
+
+ ResourceScheduler::ClientThrottleState throttle_state() const {
+ return throttle_state_;
+ }
+
+ void LoadCoalescedRequests() {
+ if (throttle_state_ != COALESCED) {
+ return;
+ }
+ if (scheduler_->active_clients_loaded()) {
+ SetThrottleState(UNTHROTTLED);
+ } else {
+ SetThrottleState(THROTTLED);
+ }
+ LoadAnyStartablePendingRequests();
+ SetThrottleState(COALESCED);
+ }
+
private:
enum ShouldStartReqResult {
DO_NOT_START_REQUEST_AND_STOP_SEARCHING,
@@ -379,46 +478,73 @@ class ResourceScheduler::Client {
// ShouldStartRequest is the main scheduling algorithm.
//
- // Requests are categorized into two categories:
- //
- // 1. Immediately issued requests, which are:
+ // Requests are categorized into three categories:
//
- // * Higher priority requests (>= net::LOW).
+ // 1. Non-delayable requests:
// * Synchronous requests.
- // * Requests to SPDY-capable origin servers.
// * Non-HTTP[S] requests.
//
- // 2. The remainder are delayable requests, which follow these rules:
+ // 2. Requests to SPDY-capable origin servers.
//
+ // 3. High-priority requests:
+ // * Higher priority requests (>= net::LOW).
+ //
+ // 4. Low priority requests
+ //
+ // The following rules are followed:
+ //
+ // ACTIVE_AND_LOADING and UNTHROTTLED Clients follow these rules:
+ // * Non-delayable, High-priority and SDPY capable requests are issued
+ // immediately
// * If no high priority requests are in flight, start loading low priority
- // requests.
+ // requests.
+ // * Low priority requests are delayable.
// * Once the renderer has a <body>, start loading delayable requests.
// * Never exceed 10 delayable requests in flight per client.
// * Never exceed 6 delayable requests for a given host.
// * Prior to <body>, allow one delayable request to load at a time.
+ //
+ // THROTTLED Clients follow these rules:
+ // * Non-delayable and SPDY-capable requests are issued immediately.
+ // * At most one non-SPDY request will be issued per THROTTLED Client
+ // * If no high priority requests are in flight, start loading low priority
+ // requests.
+ //
+ // COALESCED Clients never load requests, with the following exceptions:
+ // * Non-delayable requests are issued imediately.
+ // * On a (currently 5 second) heart beat, they load all requests as an
+ // UNTHROTTLED Client, and then return to the COALESCED state.
+ // * When an active Client makes a request, they are THROTTLED until the
+ // active Client finishes loading.
ShouldStartReqResult ShouldStartRequest(
ScheduledResourceRequest* request) const {
const net::URLRequest& url_request = *request->url_request();
+ // Syncronous requests could block the entire render, which could impact
+ // user-observable Clients.
+ if (!ResourceRequestInfo::ForRequest(&url_request)->IsAsync()) {
+ return START_REQUEST;
+ }
+
// TODO(simonjam): This may end up causing disk contention. We should
// experiment with throttling if that happens.
+ // TODO(aiolos): We probably want to Coalesce these as well to avoid
+ // waking the disk.
if (!url_request.url().SchemeIsHTTPOrHTTPS()) {
return START_REQUEST;
}
- if (using_spdy_proxy_ && url_request.url().SchemeIs("http")) {
- return START_REQUEST;
+ if (throttle_state_ == COALESCED) {
+ return DO_NOT_START_REQUEST_AND_STOP_SEARCHING;
}
- net::HttpServerProperties& http_server_properties =
- *url_request.context()->http_server_properties();
-
- if (url_request.priority() >= net::LOW ||
- !ResourceRequestInfo::ForRequest(&url_request)->IsAsync()) {
+ if (using_spdy_proxy_ && url_request.url().SchemeIs("http")) {
return START_REQUEST;
}
net::HostPortPair host_port_pair =
net::HostPortPair::FromURL(url_request.url());
+ net::HttpServerProperties& http_server_properties =
+ *url_request.context()->http_server_properties();
// TODO(willchan): We should really improve this algorithm as described in
// crbug.com/164101. Also, theoretically we should not count a SPDY request
@@ -427,6 +553,16 @@ class ResourceScheduler::Client {
return START_REQUEST;
}
+ if (throttle_state_ == THROTTLED &&
+ in_flight_requests_.size() >= kMaxNumThrottledRequestsPerClient) {
+ // There may still be SPDY-capable requests that should be issued.
+ return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING;
+ }
+
+ if (url_request.priority() >= net::LOW) {
+ return START_REQUEST;
+ }
+
size_t num_delayable_requests_in_flight = total_delayable_count_;
if (num_delayable_requests_in_flight >= kMaxNumDelayableRequestsPerClient) {
return DO_NOT_START_REQUEST_AND_STOP_SEARCHING;
@@ -485,15 +621,20 @@ class ResourceScheduler::Client {
}
}
+ bool is_audible_;
+ bool is_visible_;
+ bool is_loaded_;
bool has_body_;
bool using_spdy_proxy_;
RequestQueue pending_requests_;
RequestSet in_flight_requests_;
+ ResourceScheduler* scheduler_;
// The number of delayable in-flight requests.
size_t total_delayable_count_;
+ ResourceScheduler::ClientThrottleState throttle_state_;
};
-ResourceScheduler::ResourceScheduler() {
+ResourceScheduler::ResourceScheduler() : active_clients_loading_(0) {
}
ResourceScheduler::~ResourceScheduler() {
@@ -501,6 +642,20 @@ ResourceScheduler::~ResourceScheduler() {
DCHECK(client_map_.empty());
}
+void ResourceScheduler::SetThrottleOptionsForTesting(bool should_throttle,
+ bool should_coalesce) {
+ should_coalesce_ = should_coalesce;
+ should_throttle_ = should_throttle;
+ OnLoadingActiveClientsStateChanged();
+}
+
+ResourceScheduler::ClientThrottleState
+ResourceScheduler::GetClientStateForTesting(int child_id, int route_id) {
+ Client* client = GetClient(child_id, route_id);
+ DCHECK(client);
+ return client->throttle_state();
+}
+
scoped_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest(
int child_id,
int route_id,
@@ -548,7 +703,12 @@ void ResourceScheduler::OnClientCreated(int child_id, int route_id) {
ClientId client_id = MakeClientId(child_id, route_id);
DCHECK(!ContainsKey(client_map_, client_id));
- client_map_[client_id] = new Client;
+ Client* client = new Client(this);
+ client_map_[client_id] = client;
+
+ // TODO(aiolos): set Client visibility/audibility when signals are added
+ // this will UNTHROTTLE Clients as needed
+ client->UpdateThrottleState();
}
void ResourceScheduler::OnClientDeleted(int child_id, int route_id) {
@@ -560,7 +720,7 @@ void ResourceScheduler::OnClientDeleted(int child_id, int route_id) {
return;
Client* client = it->second;
-
+ client->OnLoadingStateChanged(true);
// FYI, ResourceDispatcherHost cancels all of the requests after this function
// is called. It should end up canceling all of the requests except for a
// cross-renderer navigation.
@@ -617,6 +777,79 @@ void ResourceScheduler::OnReceivedSpdyProxiedHttpResponse(
client->OnReceivedSpdyProxiedHttpResponse();
}
+void ResourceScheduler::OnAudibilityChanged(int child_id,
+ int route_id,
+ bool is_audible) {
+ Client* client = GetClient(child_id, route_id);
+ DCHECK(client);
+ client->OnAudibilityChanged(is_audible);
+}
+
+void ResourceScheduler::OnVisibilityChanged(int child_id,
+ int route_id,
+ bool is_visible) {
+ Client* client = GetClient(child_id, route_id);
+ DCHECK(client);
+ client->OnVisibilityChanged(is_visible);
+}
+
+void ResourceScheduler::OnLoadingStateChanged(int child_id,
+ int route_id,
+ bool is_loaded) {
+ Client* client = GetClient(child_id, route_id);
+ DCHECK(client);
+ client->OnLoadingStateChanged(is_loaded);
+}
+
+ResourceScheduler::Client* ResourceScheduler::GetClient(int child_id,
+ int route_id) {
+ ClientId client_id = MakeClientId(child_id, route_id);
+ ClientMap::iterator client_it = client_map_.find(client_id);
+ if (client_it == client_map_.end()) {
+ return NULL;
+ }
+ return client_it->second;
+}
+
+void ResourceScheduler::DecrementActiveClientsLoading() {
+ DCHECK_NE(0u, active_clients_loading_);
+ --active_clients_loading_;
+ DCHECK_EQ(active_clients_loading_, CountActiveClientsLoading());
+ if (active_clients_loading_ == 0) {
+ OnLoadingActiveClientsStateChanged();
+ }
+}
+
+void ResourceScheduler::IncrementActiveClientsLoading() {
+ ++active_clients_loading_;
+ DCHECK_EQ(active_clients_loading_, CountActiveClientsLoading());
+ if (active_clients_loading_ == 1) {
+ OnLoadingActiveClientsStateChanged();
+ }
+}
+
+void ResourceScheduler::OnLoadingActiveClientsStateChanged() {
+ ClientMap::iterator client_it = client_map_.begin();
+ while (client_it != client_map_.end()) {
+ Client* client = client_it->second;
+ client->UpdateThrottleState();
+ ++client_it;
+ }
+}
+
+size_t ResourceScheduler::CountActiveClientsLoading() {
+ size_t active_and_loading = 0;
+ ClientMap::iterator client_it = client_map_.begin();
+ while (client_it != client_map_.end()) {
+ Client* client = client_it->second;
+ if (client->throttle_state() == ACTIVE_AND_LOADING) {
+ ++active_and_loading;
+ }
+ ++client_it;
+ }
+ return active_and_loading;
+}
+
void ResourceScheduler::ReprioritizeRequest(ScheduledResourceRequest* request,
net::RequestPriority new_priority,
int new_intra_priority_value) {
« no previous file with comments | « content/browser/loader/resource_scheduler.h ('k') | content/browser/loader/resource_scheduler_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698