Commit 1188b631 authored by Zhen Wang's avatar Zhen Wang Committed by Commit Bot

Add BackgroundTabLoadingMode to TabManager

This CL adds BackgroundTabLoadingMode to TabManager. So TabManager can pause
loading background tabs when memory pressure gets critical.

Bug: 730098
Change-Id: I1add97d91b9bb9abeeb3b483046da310ba9abff9
Reviewed-on: https://chromium-review.googlesource.com/585212
Commit-Queue: Zhen Wang <zhenw@chromium.org>
Reviewed-by: default avatarChris Hamilton <chrisha@chromium.org>
Cr-Commit-Position: refs/heads/master@{#491049}
parent d09da8ef
......@@ -76,9 +76,8 @@ namespace resource_coordinator {
namespace {
// The timeout time after which the next background tab gets loaded if the
// previous tab has not finished loading yet.
// TODO(zhenw): Ignore this when under memory pressure. Or possibly make this
// a dynamic threshold under different scenarios.
// previous tab has not finished loading yet. This is ignored in kPaused loading
// mode.
const TimeDelta kBackgroundTabLoadTimeout = TimeDelta::FromSeconds(10);
// The number of loading slots for background tabs. TabManager will start to
......@@ -217,6 +216,7 @@ TabManager::TabManager()
browser_tab_strip_tracker_(this, nullptr, this),
test_tick_clock_(nullptr),
is_session_restore_loading_tabs_(false),
background_tab_loading_mode_(BackgroundTabLoadingMode::kStaggered),
force_load_timer_(base::MakeUnique<base::OneShotTimer>()),
loading_slots_(kNumOfLoadingSlots),
weak_ptr_factory_(this) {
......@@ -236,6 +236,8 @@ TabManager::~TabManager() {
}
void TabManager::Start() {
background_tab_loading_mode_ = BackgroundTabLoadingMode::kStaggered;
#if defined(OS_WIN) || defined(OS_MACOSX)
// Note that discarding is now enabled by default. This check is kept as a
// kill switch.
......@@ -899,10 +901,20 @@ void TabManager::OnMemoryPressure(
if (g_browser_process->IsShuttingDown())
return;
// Under critical pressure try to discard a tab.
if (memory_pressure_level ==
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) {
LogMemoryAndDiscardTab(kUrgentShutdown);
switch (memory_pressure_level) {
case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
background_tab_loading_mode_ = BackgroundTabLoadingMode::kStaggered;
LoadNextBackgroundTabIfNeeded();
break;
case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
background_tab_loading_mode_ = BackgroundTabLoadingMode::kPaused;
break;
case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
background_tab_loading_mode_ = BackgroundTabLoadingMode::kPaused;
LogMemoryAndDiscardTab(kUrgentShutdown);
break;
default:
NOTREACHED();
}
// TODO(skuhne): If more memory pressure levels are introduced, consider
// calling PurgeBrowserMemory() before CRITICAL is reached.
......@@ -1167,6 +1179,9 @@ void TabManager::StartForceLoadTimer() {
}
void TabManager::LoadNextBackgroundTabIfNeeded() {
if (background_tab_loading_mode_ != BackgroundTabLoadingMode::kStaggered)
return;
// Do not load more background tabs until TabManager can load the next tab.
// Ignore this constraint if the timer fires to force loading the next
// background tab.
......
......@@ -182,6 +182,12 @@ class TabManager : public TabStripModelObserver,
// TODO(tasak): rename this to CanPurgeBackgroundedRenderer.
bool CanSuspendBackgroundedRenderer(int render_process_id) const;
// Indicates how TabManager should load pending background tabs.
enum BackgroundTabLoadingMode {
kStaggered, // Load a background tab after another tab has done loading.
kPaused // Pause loading background tabs unless the user selects it.
};
// Maybe throttle a tab's navigation based on current system status.
content::NavigationThrottle::ThrottleCheckResult MaybeThrottleNavigation(
BackgroundTabNavigationThrottle* throttle);
......@@ -240,6 +246,7 @@ class TabManager : public TabStripModelObserver,
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, OnWebContentsDestroyed);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, OnDelayedTabSelected);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, TimeoutWhenLoadingBackgroundTabs);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, BackgroundTabLoadingMode);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, BackgroundTabLoadingSlots);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, BackgroundTabsLoadingOrdering);
FRIEND_TEST_ALL_PREFIXES(TabManagerStatsCollectorTest,
......@@ -413,10 +420,15 @@ class TabManager : public TabStripModelObserver,
// expires before the current tab loading is finished.
void StartForceLoadTimer();
// Start loading the next background tab if needed.
// Start loading the next background tab if needed. This is called when:
// 1. a tab has finished loading;
// 2. or a tab has been destroyed;
// 3. or memory pressure is relieved;
// 4. or |force_load_timer_| fires.
void LoadNextBackgroundTabIfNeeded();
// Resume the tab's navigation if it is pending right now.
// Resume the tab's navigation if it is pending right now. This is called when
// a tab is selected.
void ResumeTabNavigationIfNeeded(content::WebContents* contents);
// Resume navigation.
......@@ -447,6 +459,12 @@ class TabManager : public TabStripModelObserver,
loading_slots_ = loading_slots;
}
// Reset |memory_pressure_listener_| in test so that the test is not affected
// by memory pressure.
void ResetMemoryPressureListenerForTest() {
memory_pressure_listener_.reset();
}
// Timer to periodically update the stats of the renderers.
base::RepeatingTimer update_timer_;
......@@ -516,6 +534,9 @@ class TabManager : public TabStripModelObserver,
class TabManagerSessionRestoreObserver;
std::unique_ptr<TabManagerSessionRestoreObserver> session_restore_observer_;
// The mode that TabManager is using to load pending background tabs.
BackgroundTabLoadingMode background_tab_loading_mode_;
// When the timer fires, it forces loading the next background tab if needed.
std::unique_ptr<base::OneShotTimer> force_load_timer_;
......
......@@ -707,6 +707,7 @@ TEST_F(TabManagerTest, DiscardTabWithNonVisibleTabs) {
TEST_F(TabManagerTest, MaybeThrottleNavigation) {
TabManager* tab_manager = g_browser_process->GetTabManager();
tab_manager->ResetMemoryPressureListenerForTest();
MaybeThrottleNavigations(tab_manager);
tab_manager->GetWebContentsData(contents1_)
->DidStartNavigation(nav_handle1_.get());
......@@ -723,6 +724,7 @@ TEST_F(TabManagerTest, MaybeThrottleNavigation) {
TEST_F(TabManagerTest, OnDidFinishNavigation) {
TabManager* tab_manager = g_browser_process->GetTabManager();
tab_manager->ResetMemoryPressureListenerForTest();
MaybeThrottleNavigations(tab_manager);
tab_manager->GetWebContentsData(contents1_)
->DidStartNavigation(nav_handle1_.get());
......@@ -735,6 +737,7 @@ TEST_F(TabManagerTest, OnDidFinishNavigation) {
TEST_F(TabManagerTest, OnDidStopLoading) {
TabManager* tab_manager = g_browser_process->GetTabManager();
tab_manager->ResetMemoryPressureListenerForTest();
MaybeThrottleNavigations(tab_manager);
tab_manager->GetWebContentsData(contents1_)
->DidStartNavigation(nav_handle1_.get());
......@@ -754,6 +757,7 @@ TEST_F(TabManagerTest, OnDidStopLoading) {
TEST_F(TabManagerTest, OnWebContentsDestroyed) {
TabManager* tab_manager = g_browser_process->GetTabManager();
tab_manager->ResetMemoryPressureListenerForTest();
MaybeThrottleNavigations(tab_manager);
tab_manager->GetWebContentsData(contents1_)
->DidStartNavigation(nav_handle1_.get());
......@@ -779,6 +783,7 @@ TEST_F(TabManagerTest, OnWebContentsDestroyed) {
TEST_F(TabManagerTest, OnDelayedTabSelected) {
TabManager* tab_manager = g_browser_process->GetTabManager();
tab_manager->ResetMemoryPressureListenerForTest();
MaybeThrottleNavigations(tab_manager);
tab_manager->GetWebContentsData(contents1_)
->DidStartNavigation(nav_handle1_.get());
......@@ -815,6 +820,7 @@ TEST_F(TabManagerTest, OnDelayedTabSelected) {
TEST_F(TabManagerTest, TimeoutWhenLoadingBackgroundTabs) {
TabManager* tab_manager = g_browser_process->GetTabManager();
tab_manager->ResetMemoryPressureListenerForTest();
base::SimpleTestTickClock test_clock;
tab_manager->set_test_tick_clock(&test_clock);
......@@ -853,6 +859,71 @@ TEST_F(TabManagerTest, TimeoutWhenLoadingBackgroundTabs) {
EXPECT_FALSE(tab_manager->IsNavigationDelayedForTest(nav_handle3_.get()));
}
TEST_F(TabManagerTest, BackgroundTabLoadingMode) {
TabManager* tab_manager = g_browser_process->GetTabManager();
tab_manager->ResetMemoryPressureListenerForTest();
base::SimpleTestTickClock test_clock;
tab_manager->set_test_tick_clock(&test_clock);
EXPECT_EQ(TabManager::BackgroundTabLoadingMode::kStaggered,
tab_manager->background_tab_loading_mode_);
MaybeThrottleNavigations(tab_manager);
tab_manager->GetWebContentsData(contents1_)
->DidStartNavigation(nav_handle1_.get());
EXPECT_TRUE(tab_manager->IsTabLoadingForTest(contents1_));
EXPECT_FALSE(tab_manager->IsTabLoadingForTest(contents2_));
EXPECT_FALSE(tab_manager->IsTabLoadingForTest(contents3_));
EXPECT_FALSE(tab_manager->IsNavigationDelayedForTest(nav_handle1_.get()));
EXPECT_TRUE(tab_manager->IsNavigationDelayedForTest(nav_handle2_.get()));
EXPECT_TRUE(tab_manager->IsNavigationDelayedForTest(nav_handle3_.get()));
// Simulate memory pressure getting high. TabManager pause loading pending
// background tabs.
tab_manager->OnMemoryPressure(
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
EXPECT_EQ(TabManager::BackgroundTabLoadingMode::kPaused,
tab_manager->background_tab_loading_mode_);
// Simulate timout when loading the 1st tab.
test_clock.Advance(base::TimeDelta::FromMinutes(1));
EXPECT_TRUE(tab_manager->TriggerForceLoadTimerForTest());
// Tab 2 and 3 are still pending because of the paused loading mode.
EXPECT_FALSE(tab_manager->IsTabLoadingForTest(contents2_));
EXPECT_FALSE(tab_manager->IsTabLoadingForTest(contents3_));
EXPECT_TRUE(tab_manager->IsNavigationDelayedForTest(nav_handle2_.get()));
EXPECT_TRUE(tab_manager->IsNavigationDelayedForTest(nav_handle3_.get()));
// Simulate tab 1 has finished loading.
tab_manager->GetWebContentsData(contents1_)->DidStopLoading();
// Tab 2 and 3 are still pending because of the paused loading mode.
EXPECT_FALSE(tab_manager->IsTabLoadingForTest(contents2_));
EXPECT_FALSE(tab_manager->IsTabLoadingForTest(contents3_));
EXPECT_TRUE(tab_manager->IsNavigationDelayedForTest(nav_handle2_.get()));
EXPECT_TRUE(tab_manager->IsNavigationDelayedForTest(nav_handle3_.get()));
// Simulate memory pressure is relieved. TabManager will reset the loading
// mode and try to load the next tab.
tab_manager->OnMemoryPressure(
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE);
EXPECT_EQ(TabManager::BackgroundTabLoadingMode::kStaggered,
tab_manager->background_tab_loading_mode_);
// Tab 2 should start loading right away.
EXPECT_TRUE(tab_manager->IsTabLoadingForTest(contents2_));
EXPECT_FALSE(tab_manager->IsNavigationDelayedForTest(nav_handle2_.get()));
// Simulate tab 2 has finished loading.
tab_manager->GetWebContentsData(contents2_)->DidStopLoading();
// Tab 3 should start loading now in staggered loading mode.
EXPECT_TRUE(tab_manager->IsTabLoadingForTest(contents3_));
EXPECT_FALSE(tab_manager->IsNavigationDelayedForTest(nav_handle3_.get()));
}
TEST_F(TabManagerTest, BackgroundTabLoadingSlots) {
TabManager tab_manager1;
MaybeThrottleNavigations(&tab_manager1, 1);
......@@ -877,6 +948,7 @@ TEST_F(TabManagerTest, BackgroundTabLoadingSlots) {
TEST_F(TabManagerTest, BackgroundTabsLoadingOrdering) {
TabManager* tab_manager = g_browser_process->GetTabManager();
tab_manager->ResetMemoryPressureListenerForTest();
base::SimpleTestTickClock test_clock;
tab_manager->set_test_tick_clock(&test_clock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment