Add coalescing timer to ResourceScheduler. CL 2 from BUG=128035.
This is a continuation of https://codereview.chromium.org/357583003
BUG=128035
Review URL: https://codereview.chromium.org/393163003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@286372 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/content/browser/loader/resource_scheduler.cc b/content/browser/loader/resource_scheduler.cc
index fadbc29..85adbdb 100644
--- a/content/browser/loader/resource_scheduler.cc
+++ b/content/browser/loader/resource_scheduler.cc
@@ -22,6 +22,7 @@
namespace content {
+static const size_t kCoalescedTimerPeriod = 5000;
static const size_t kMaxNumDelayableRequestsPerClient = 10;
static const size_t kMaxNumDelayableRequestsPerHost = 6;
static const size_t kMaxNumThrottledRequestsPerClient = 1;
@@ -233,13 +234,22 @@
: is_audible_(false),
is_visible_(false),
is_loaded_(false),
+ is_paused_(false),
has_body_(false),
using_spdy_proxy_(false),
total_delayable_count_(0),
throttle_state_(ResourceScheduler::THROTTLED) {
scheduler_ = scheduler;
}
- ~Client() {}
+
+ ~Client() {
+ // Update to default state and pause to ensure the scheduler has a
+ // correct count of relevant types of clients.
+ is_visible_ = false;
+ is_audible_ = false;
+ is_paused_ = true;
+ UpdateThrottleState();
+ }
void ScheduleRequest(
net::URLRequest* url_request,
@@ -302,12 +312,19 @@
UpdateThrottleState();
}
+ void SetPaused() {
+ is_paused_ = true;
+ 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 (is_paused_) {
+ SetThrottleState(PAUSED);
} else if (!scheduler_->active_clients_loaded()) {
SetThrottleState(THROTTLED);
} else if (is_loaded_ && scheduler_->should_coalesce()) {
@@ -315,6 +332,7 @@
} else if (!is_active()) {
SetThrottleState(UNTHROTTLED);
}
+
if (throttle_state_ == old_throttle_state) {
return;
}
@@ -323,6 +341,11 @@
} else if (old_throttle_state == ACTIVE_AND_LOADING) {
scheduler_->DecrementActiveClientsLoading();
}
+ if (throttle_state_ == COALESCED) {
+ scheduler_->IncrementCoalescedClients();
+ } else if (old_throttle_state == COALESCED) {
+ scheduler_->DecrementCoalescedClients();
+ }
}
void OnNavigate() {
@@ -384,6 +407,9 @@
return;
}
throttle_state_ = throttle_state;
+ if (throttle_state_ != PAUSED) {
+ is_paused_ = false;
+ }
LoadAnyStartablePendingRequests();
// TODO(aiolos): Stop any started but not inflght requests when
// switching to stricter throttle state?
@@ -624,6 +650,7 @@
bool is_audible_;
bool is_visible_;
bool is_loaded_;
+ bool is_paused_;
bool has_body_;
bool using_spdy_proxy_;
RequestQueue pending_requests_;
@@ -634,9 +661,13 @@
ResourceScheduler::ClientThrottleState throttle_state_;
};
-ResourceScheduler::ResourceScheduler(): should_coalesce_(false),
- should_throttle_(false),
- active_clients_loading_(0) {
+ResourceScheduler::ResourceScheduler()
+ : should_coalesce_(false),
+ should_throttle_(false),
+ active_clients_loading_(0),
+ coalesced_clients_(0),
+ coalescing_timer_(new base::Timer(true /* retain_user_task */,
+ true /* is_repeating */)) {
}
ResourceScheduler::~ResourceScheduler() {
@@ -648,7 +679,7 @@
bool should_coalesce) {
should_coalesce_ = should_coalesce;
should_throttle_ = should_throttle;
- OnLoadingActiveClientsStateChanged();
+ OnLoadingActiveClientsStateChangedForAllClients();
}
ResourceScheduler::ClientThrottleState
@@ -722,7 +753,6 @@
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.
@@ -818,7 +848,7 @@
--active_clients_loading_;
DCHECK_EQ(active_clients_loading_, CountActiveClientsLoading());
if (active_clients_loading_ == 0) {
- OnLoadingActiveClientsStateChanged();
+ OnLoadingActiveClientsStateChangedForAllClients();
}
}
@@ -826,11 +856,11 @@
++active_clients_loading_;
DCHECK_EQ(active_clients_loading_, CountActiveClientsLoading());
if (active_clients_loading_ == 1) {
- OnLoadingActiveClientsStateChanged();
+ OnLoadingActiveClientsStateChangedForAllClients();
}
}
-void ResourceScheduler::OnLoadingActiveClientsStateChanged() {
+void ResourceScheduler::OnLoadingActiveClientsStateChangedForAllClients() {
ClientMap::iterator client_it = client_map_.begin();
while (client_it != client_map_.end()) {
Client* client = client_it->second;
@@ -839,9 +869,9 @@
}
}
-size_t ResourceScheduler::CountActiveClientsLoading() {
+size_t ResourceScheduler::CountActiveClientsLoading() const {
size_t active_and_loading = 0;
- ClientMap::iterator client_it = client_map_.begin();
+ ClientMap::const_iterator client_it = client_map_.begin();
while (client_it != client_map_.end()) {
Client* client = client_it->second;
if (client->throttle_state() == ACTIVE_AND_LOADING) {
@@ -852,6 +882,53 @@
return active_and_loading;
}
+void ResourceScheduler::IncrementCoalescedClients() {
+ ++coalesced_clients_;
+ DCHECK(should_coalesce_);
+ DCHECK_EQ(coalesced_clients_, CountCoalescedClients());
+ if (coalesced_clients_ == 1) {
+ coalescing_timer_->Start(
+ FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kCoalescedTimerPeriod),
+ base::Bind(&ResourceScheduler::LoadCoalescedRequests,
+ base::Unretained(this)));
+ }
+}
+
+void ResourceScheduler::DecrementCoalescedClients() {
+ DCHECK(should_coalesce_);
+ DCHECK_NE(0U, coalesced_clients_);
+ --coalesced_clients_;
+ DCHECK_EQ(coalesced_clients_, CountCoalescedClients());
+ if (coalesced_clients_ == 0) {
+ coalescing_timer_->Stop();
+ }
+}
+
+size_t ResourceScheduler::CountCoalescedClients() const {
+ DCHECK(should_coalesce_);
+ size_t coalesced_clients = 0;
+ ClientMap::const_iterator client_it = client_map_.begin();
+ while (client_it != client_map_.end()) {
+ Client* client = client_it->second;
+ if (client->throttle_state() == COALESCED) {
+ ++coalesced_clients;
+ }
+ ++client_it;
+ }
+ return coalesced_clients_;
+}
+
+void ResourceScheduler::LoadCoalescedRequests() {
+ DCHECK(should_coalesce_);
+ ClientMap::iterator client_it = client_map_.begin();
+ while (client_it != client_map_.end()) {
+ Client* client = client_it->second;
+ client->LoadCoalescedRequests();
+ ++client_it;
+ }
+}
+
void ResourceScheduler::ReprioritizeRequest(ScheduledResourceRequest* request,
net::RequestPriority new_priority,
int new_intra_priority_value) {
diff --git a/content/browser/loader/resource_scheduler.h b/content/browser/loader/resource_scheduler.h
index b445a68..74ac16d 100644
--- a/content/browser/loader/resource_scheduler.h
+++ b/content/browser/loader/resource_scheduler.h
@@ -12,6 +12,7 @@
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "base/threading/non_thread_safe.h"
+#include "base/timer/timer.h"
#include "content/common/content_export.h"
#include "net/base/priority_queue.h"
#include "net/base/request_priority.h"
@@ -53,6 +54,17 @@
class CONTENT_EXPORT ResourceScheduler : public base::NonThreadSafe {
public:
enum ClientThrottleState {
+ // TODO(aiolos): Add logic to ShouldStartRequest for PAUSED Clients to only
+ // issue synchronous requests.
+ // TODO(aiolos): Add max number of THROTTLED Clients, and logic to set
+ // subsquent Clients to PAUSED instead. Also add logic to unpause a Client
+ // when a background Client becomes COALESCED (ie, finishes loading.)
+ // TODO(aiolos): Add tests for the above mentioned logic.
+
+ // Currently being deleted client.
+ // This state currently follows the same logic for loading requests as
+ // UNTHROTTLED/ACTIVE_AND_LOADING Clients. See above TODO's.
+ PAUSED,
// Loaded background client, all observable clients loaded.
COALESCED,
// Background client, an observable client is loading.
@@ -69,6 +81,11 @@
ResourceScheduler();
~ResourceScheduler();
+ // Use a mock timer when testing.
+ void set_timer_for_testing(scoped_ptr<base::Timer> timer) {
+ coalescing_timer_.reset(timer.release());
+ }
+
// TODO(aiolos): Remove when throttling and coalescing have landed
void SetThrottleOptionsForTesting(bool should_throttle, bool should_coalesce);
@@ -136,16 +153,26 @@
// Called when a ScheduledResourceRequest is destroyed.
void RemoveRequest(ScheduledResourceRequest* request);
- // These Calls may update the ThrottleState of all clients, and have the
- // potential to be re-entarant.
+ // These calls may update the ThrottleState of all clients, and have the
+ // potential to be re-entrant.
// Called when a Client newly becomes active loading.
- void DecrementActiveClientsLoading();
- // Caled when a Client stops being active loading.
void IncrementActiveClientsLoading();
+ // Called when an active and loading Client either completes loading or
+ // becomes inactive.
+ void DecrementActiveClientsLoading();
- void OnLoadingActiveClientsStateChanged();
+ void OnLoadingActiveClientsStateChangedForAllClients();
- size_t CountActiveClientsLoading();
+ size_t CountActiveClientsLoading() const;
+
+ // Called when a Client becomes coalesced.
+ void IncrementCoalescedClients();
+ // Called when a client stops being coalesced.
+ void DecrementCoalescedClients();
+
+ void LoadCoalescedRequests();
+
+ size_t CountCoalescedClients() const;
// Update the queue position for |request|, possibly causing it to start
// loading.
@@ -167,6 +194,9 @@
bool should_throttle_;
ClientMap client_map_;
size_t active_clients_loading_;
+ size_t coalesced_clients_;
+ // This is a repeating timer to initiate requests on COALESCED Clients.
+ scoped_ptr<base::Timer> coalescing_timer_;
RequestSet unowned_requests_;
};
diff --git a/content/browser/loader/resource_scheduler_unittest.cc b/content/browser/loader/resource_scheduler_unittest.cc
index ea32a95d..e424c278 100644
--- a/content/browser/loader/resource_scheduler_unittest.cc
+++ b/content/browser/loader/resource_scheduler_unittest.cc
@@ -7,6 +7,8 @@
#include "base/memory/scoped_vector.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_number_conversions.h"
+#include "base/timer/mock_timer.h"
+#include "base/timer/timer.h"
#include "content/browser/browser_thread_impl.h"
#include "content/browser/loader/resource_dispatcher_host_impl.h"
#include "content/browser/loader/resource_message_filter.h"
@@ -136,7 +138,13 @@
ResourceSchedulerTest()
: next_request_id_(0),
ui_thread_(BrowserThread::UI, &message_loop_),
- io_thread_(BrowserThread::IO, &message_loop_) {
+ io_thread_(BrowserThread::IO, &message_loop_),
+ mock_timer_(new base::MockTimer(true, true)) {
+ scheduler_.set_timer_for_testing(scoped_ptr<base::Timer>(mock_timer_));
+
+ // TODO(aiolos): Remove when throttling and coalescing have both landed.
+ scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */,
+ false /* should_coalesce */);
scheduler_.OnClientCreated(kChildId, kRouteId);
scheduler_.OnVisibilityChanged(kChildId, kRouteId, true);
@@ -257,12 +265,18 @@
rdh_.OnMessageReceived(msg, filter.get());
}
+ void FireCoalescingTimer() {
+ EXPECT_TRUE(mock_timer_->IsRunning());
+ mock_timer_->Fire();
+ }
+
int next_request_id_;
base::MessageLoopForIO message_loop_;
BrowserThreadImpl ui_thread_;
BrowserThreadImpl io_thread_;
ResourceDispatcherHostImpl rdh_;
ResourceScheduler scheduler_;
+ base::MockTimer* mock_timer_;
net::HttpServerPropertiesImpl http_server_properties_;
net::TestURLRequestContext context_;
};
@@ -1751,6 +1765,338 @@
scheduler_.OnClientDeleted(kBackgroundChildId2, kBackgroundRouteId2);
}
+TEST_F(ResourceSchedulerTest, CoalescedClientCreationStartsTimer) {
+ scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */,
+ true /* should_coalesce */);
+ EXPECT_FALSE(mock_timer_->IsRunning());
+ scheduler_.OnLoadingStateChanged(kChildId, kRouteId, true);
+ EXPECT_FALSE(mock_timer_->IsRunning());
+ scheduler_.OnLoadingStateChanged(
+ kBackgroundChildId, kBackgroundRouteId, true);
+ EXPECT_EQ(ResourceScheduler::COALESCED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId,
+ kBackgroundRouteId));
+ EXPECT_TRUE(mock_timer_->IsRunning());
+}
+
+TEST_F(ResourceSchedulerTest, ActiveLoadingClientLoadedAndHiddenStartsTimer) {
+ scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */,
+ true /* should_coalesce */);
+ EXPECT_FALSE(mock_timer_->IsRunning());
+ EXPECT_EQ(ResourceScheduler::ACTIVE_AND_LOADING,
+ scheduler_.GetClientStateForTesting(kChildId, kRouteId));
+ EXPECT_EQ(ResourceScheduler::THROTTLED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId,
+ kBackgroundRouteId));
+ EXPECT_FALSE(mock_timer_->IsRunning());
+
+ scheduler_.OnLoadingStateChanged(kChildId, kRouteId, true);
+ EXPECT_EQ(ResourceScheduler::UNTHROTTLED,
+ scheduler_.GetClientStateForTesting(kChildId, kRouteId));
+ EXPECT_EQ(ResourceScheduler::UNTHROTTLED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId,
+ kBackgroundRouteId));
+ EXPECT_FALSE(mock_timer_->IsRunning());
+
+ scheduler_.OnVisibilityChanged(kChildId, kRouteId, false);
+ EXPECT_EQ(ResourceScheduler::COALESCED,
+ scheduler_.GetClientStateForTesting(kChildId, kRouteId));
+ EXPECT_EQ(ResourceScheduler::UNTHROTTLED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId,
+ kBackgroundRouteId));
+ EXPECT_TRUE(mock_timer_->IsRunning());
+}
+
+TEST_F(ResourceSchedulerTest, ActiveLoadingClientHiddenAndLoadedStartsTimer) {
+ scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */,
+ true /* should_coalesce */);
+ EXPECT_EQ(ResourceScheduler::ACTIVE_AND_LOADING,
+ scheduler_.GetClientStateForTesting(kChildId, kRouteId));
+ EXPECT_EQ(ResourceScheduler::THROTTLED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId,
+ kBackgroundRouteId));
+ EXPECT_FALSE(mock_timer_->IsRunning());
+
+ scheduler_.OnVisibilityChanged(kChildId, kRouteId, false);
+ EXPECT_EQ(ResourceScheduler::UNTHROTTLED,
+ scheduler_.GetClientStateForTesting(kChildId, kRouteId));
+ EXPECT_FALSE(mock_timer_->IsRunning());
+
+ scheduler_.OnLoadingStateChanged(kChildId, kRouteId, true);
+ EXPECT_EQ(ResourceScheduler::UNTHROTTLED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId,
+ kBackgroundRouteId));
+ EXPECT_EQ(ResourceScheduler::COALESCED,
+ scheduler_.GetClientStateForTesting(kChildId, kRouteId));
+ EXPECT_EQ(ResourceScheduler::UNTHROTTLED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId,
+ kBackgroundRouteId));
+ EXPECT_TRUE(mock_timer_->IsRunning());
+}
+
+TEST_F(ResourceSchedulerTest, CoalescedClientBecomesAudibleStopsTimer) {
+ scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */,
+ true /* should_coalesce */);
+ scheduler_.OnLoadingStateChanged(kChildId, kRouteId, true);
+ EXPECT_FALSE(mock_timer_->IsRunning());
+ scheduler_.OnLoadingStateChanged(
+ kBackgroundChildId, kBackgroundRouteId, true);
+ EXPECT_EQ(ResourceScheduler::COALESCED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId,
+ kBackgroundRouteId));
+ EXPECT_TRUE(mock_timer_->IsRunning());
+
+ scheduler_.OnAudibilityChanged(kBackgroundChildId, kBackgroundRouteId, true);
+ EXPECT_EQ(ResourceScheduler::UNTHROTTLED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId,
+ kBackgroundRouteId));
+ EXPECT_FALSE(mock_timer_->IsRunning());
+}
+
+TEST_F(ResourceSchedulerTest, LastCoalescedClientDeletionStopsTimer) {
+ scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */,
+ true /* should_coalesce */);
+ scheduler_.OnClientCreated(kBackgroundChildId2, kBackgroundRouteId2);
+ EXPECT_FALSE(mock_timer_->IsRunning());
+ scheduler_.OnLoadingStateChanged(kChildId, kRouteId, true);
+ EXPECT_FALSE(mock_timer_->IsRunning());
+ scheduler_.OnLoadingStateChanged(
+ kBackgroundChildId, kBackgroundRouteId, true);
+ EXPECT_EQ(ResourceScheduler::COALESCED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId,
+ kBackgroundRouteId));
+ scheduler_.OnLoadingStateChanged(
+ kBackgroundChildId2, kBackgroundRouteId2, true);
+ EXPECT_EQ(ResourceScheduler::COALESCED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId2,
+ kBackgroundRouteId2));
+ EXPECT_TRUE(mock_timer_->IsRunning());
+
+ scheduler_.OnClientDeleted(kBackgroundChildId, kBackgroundRouteId);
+ EXPECT_TRUE(mock_timer_->IsRunning());
+
+ scheduler_.OnClientDeleted(kBackgroundChildId2, kBackgroundRouteId2);
+ EXPECT_FALSE(mock_timer_->IsRunning());
+
+ // To avoid errors on test tear down.
+ scheduler_.OnClientCreated(kBackgroundChildId, kBackgroundRouteId);
+}
+
+TEST_F(ResourceSchedulerTest, LastCoalescedClientStartsLoadingStopsTimer) {
+ scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */,
+ true /* should_coalesce */);
+ scheduler_.OnClientCreated(kBackgroundChildId2, kBackgroundRouteId2);
+ EXPECT_FALSE(mock_timer_->IsRunning());
+ scheduler_.OnLoadingStateChanged(kChildId, kRouteId, true);
+ EXPECT_FALSE(mock_timer_->IsRunning());
+ scheduler_.OnLoadingStateChanged(
+ kBackgroundChildId, kBackgroundRouteId, true);
+ EXPECT_EQ(ResourceScheduler::COALESCED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId,
+ kBackgroundRouteId));
+ scheduler_.OnLoadingStateChanged(
+ kBackgroundChildId2, kBackgroundRouteId2, true);
+ EXPECT_EQ(ResourceScheduler::COALESCED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId2,
+ kBackgroundRouteId2));
+ EXPECT_TRUE(mock_timer_->IsRunning());
+
+ scheduler_.OnLoadingStateChanged(
+ kBackgroundChildId, kBackgroundRouteId, false);
+ EXPECT_TRUE(mock_timer_->IsRunning());
+
+ scheduler_.OnLoadingStateChanged(
+ kBackgroundChildId2, kBackgroundRouteId2, false);
+ EXPECT_FALSE(mock_timer_->IsRunning());
+
+ // This is needed to avoid errors on test tear down.
+ scheduler_.OnClientDeleted(kBackgroundChildId2, kBackgroundRouteId2);
+}
+
+TEST_F(ResourceSchedulerTest, LastCoalescedClientBecomesVisibleStopsTimer) {
+ scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */,
+ true /* should_coalesce */);
+ scheduler_.OnClientCreated(kBackgroundChildId2, kBackgroundRouteId2);
+ EXPECT_FALSE(mock_timer_->IsRunning());
+ scheduler_.OnLoadingStateChanged(kChildId, kRouteId, true);
+ EXPECT_FALSE(mock_timer_->IsRunning());
+ scheduler_.OnLoadingStateChanged(
+ kBackgroundChildId, kBackgroundRouteId, true);
+ EXPECT_EQ(ResourceScheduler::COALESCED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId,
+ kBackgroundRouteId));
+ scheduler_.OnLoadingStateChanged(
+ kBackgroundChildId2, kBackgroundRouteId2, true);
+ EXPECT_EQ(ResourceScheduler::COALESCED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId2,
+ kBackgroundRouteId2));
+ EXPECT_TRUE(mock_timer_->IsRunning());
+
+ scheduler_.OnVisibilityChanged(kBackgroundChildId, kBackgroundRouteId, true);
+ EXPECT_TRUE(mock_timer_->IsRunning());
+
+ scheduler_.OnVisibilityChanged(
+ kBackgroundChildId2, kBackgroundRouteId2, true);
+ EXPECT_FALSE(mock_timer_->IsRunning());
+
+ // To avoid errors on test tear down.
+ scheduler_.OnClientDeleted(kBackgroundChildId2, kBackgroundRouteId2);
+}
+
+TEST_F(ResourceSchedulerTest,
+ CoalescedClientBecomesLoadingAndVisibleStopsTimer) {
+ scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */,
+ true /* should_coalesce */);
+ scheduler_.OnLoadingStateChanged(kChildId, kRouteId, true);
+ EXPECT_FALSE(mock_timer_->IsRunning());
+ scheduler_.OnLoadingStateChanged(
+ kBackgroundChildId, kBackgroundRouteId, true);
+ EXPECT_EQ(ResourceScheduler::COALESCED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId,
+ kBackgroundRouteId));
+ EXPECT_TRUE(mock_timer_->IsRunning());
+
+ scheduler_.OnLoadingStateChanged(
+ kBackgroundChildId, kBackgroundRouteId, false);
+ EXPECT_EQ(ResourceScheduler::UNTHROTTLED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId,
+ kBackgroundRouteId));
+ EXPECT_FALSE(mock_timer_->IsRunning());
+
+ scheduler_.OnVisibilityChanged(kBackgroundChildId, kBackgroundRouteId, true);
+ EXPECT_EQ(ResourceScheduler::ACTIVE_AND_LOADING,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId,
+ kBackgroundRouteId));
+ EXPECT_FALSE(mock_timer_->IsRunning());
+}
+
+TEST_F(ResourceSchedulerTest, CoalescedRequestsIssueOnTimer) {
+ scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */,
+ true /* should_coalesce */);
+ scheduler_.OnLoadingStateChanged(kChildId, kRouteId, true);
+ scheduler_.OnLoadingStateChanged(
+ kBackgroundChildId, kBackgroundRouteId, true);
+ EXPECT_EQ(ResourceScheduler::COALESCED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId,
+ kBackgroundRouteId));
+ EXPECT_TRUE(scheduler_.active_clients_loaded());
+
+ scoped_ptr<TestRequest> high(
+ NewBackgroundRequest("http://host/high", net::HIGHEST));
+ scoped_ptr<TestRequest> low(
+ NewBackgroundRequest("http://host/low", net::LOWEST));
+ EXPECT_FALSE(high->started());
+ EXPECT_FALSE(low->started());
+
+ FireCoalescingTimer();
+
+ EXPECT_TRUE(high->started());
+ EXPECT_TRUE(low->started());
+}
+
+TEST_F(ResourceSchedulerTest, CoalescedRequestsUnthrottleCorrectlyOnTimer) {
+ scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */,
+ true /* should_coalesce */);
+ scheduler_.OnLoadingStateChanged(kChildId, kRouteId, true);
+ scheduler_.OnLoadingStateChanged(
+ kBackgroundChildId, kBackgroundRouteId, true);
+ EXPECT_EQ(ResourceScheduler::COALESCED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId,
+ kBackgroundRouteId));
+ EXPECT_TRUE(scheduler_.active_clients_loaded());
+
+ scoped_ptr<TestRequest> high(
+ NewBackgroundRequest("http://host/high", net::HIGHEST));
+ scoped_ptr<TestRequest> high2(
+ NewBackgroundRequest("http://host/high", net::HIGHEST));
+ scoped_ptr<TestRequest> high3(
+ NewBackgroundRequest("http://host/high", net::HIGHEST));
+ scoped_ptr<TestRequest> high4(
+ NewBackgroundRequest("http://host/high", net::HIGHEST));
+ scoped_ptr<TestRequest> low(
+ NewBackgroundRequest("http://host/low", net::LOWEST));
+ scoped_ptr<TestRequest> low2(
+ NewBackgroundRequest("http://host/low", net::LOWEST));
+ scoped_ptr<TestRequest> low3(
+ NewBackgroundRequest("http://host/low", net::LOWEST));
+ scoped_ptr<TestRequest> low4(
+ NewBackgroundRequest("http://host/low", net::LOWEST));
+
+ http_server_properties_.SetSupportsSpdy(net::HostPortPair("spdyhost", 443),
+ true);
+ scoped_ptr<TestRequest> low_spdy(
+ NewBackgroundRequest("https://spdyhost/low", net::LOW));
+ scoped_ptr<TestRequest> sync_request(
+ NewBackgroundSyncRequest("http://host/req", net::LOW));
+ scoped_ptr<TestRequest> non_http_request(
+ NewBackgroundRequest("chrome-extension://req", net::LOW));
+
+ // Sync requests should issue immediately.
+ EXPECT_TRUE(sync_request->started());
+ // Non-http(s) requests should issue immediately.
+ EXPECT_TRUE(non_http_request->started());
+ // Nothing else should issue without a timer fire.
+ EXPECT_FALSE(high->started());
+ EXPECT_FALSE(high2->started());
+ EXPECT_FALSE(high3->started());
+ EXPECT_FALSE(high4->started());
+ EXPECT_FALSE(low->started());
+ EXPECT_FALSE(low2->started());
+ EXPECT_FALSE(low3->started());
+ EXPECT_FALSE(low4->started());
+ EXPECT_FALSE(low_spdy->started());
+
+ FireCoalescingTimer();
+
+ // All high priority requests should issue.
+ EXPECT_TRUE(high->started());
+ EXPECT_TRUE(high2->started());
+ EXPECT_TRUE(high3->started());
+ EXPECT_TRUE(high4->started());
+ // There should only be one net::LOWEST priority request issued with
+ // non-delayable requests in flight.
+ EXPECT_TRUE(low->started());
+ EXPECT_FALSE(low2->started());
+ EXPECT_FALSE(low3->started());
+ EXPECT_FALSE(low4->started());
+ // Spdy-Enable requests should issue regardless of priority.
+ EXPECT_TRUE(low_spdy->started());
+}
+
+TEST_F(ResourceSchedulerTest, CoalescedRequestsWaitForNextTimer) {
+ scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */,
+ true /* should_coalesce */);
+ scheduler_.OnLoadingStateChanged(kChildId, kRouteId, true);
+ scheduler_.OnLoadingStateChanged(
+ kBackgroundChildId, kBackgroundRouteId, true);
+
+ EXPECT_EQ(ResourceScheduler::COALESCED,
+ scheduler_.GetClientStateForTesting(kBackgroundChildId,
+ kBackgroundRouteId));
+ EXPECT_TRUE(scheduler_.active_clients_loaded());
+
+ scoped_ptr<TestRequest> high(
+ NewBackgroundRequest("http://host/high", net::HIGHEST));
+ EXPECT_FALSE(high->started());
+
+ FireCoalescingTimer();
+
+ scoped_ptr<TestRequest> high2(
+ NewBackgroundRequest("http://host/high2", net::HIGHEST));
+ scoped_ptr<TestRequest> low(
+ NewBackgroundRequest("http://host/low", net::LOWEST));
+
+ EXPECT_TRUE(high->started());
+ EXPECT_FALSE(high2->started());
+ EXPECT_FALSE(low->started());
+
+ FireCoalescingTimer();
+
+ EXPECT_TRUE(high->started());
+ EXPECT_TRUE(high2->started());
+ EXPECT_TRUE(low->started());
+}
+
} // unnamed namespace
} // namespace content