Skip to content

Commit

Permalink
TabManager: Kill apps on ARC enabled devices.
Browse files Browse the repository at this point in the history
Prioritize ARC apps and Chrome tabs as a whole and choose the least important process to kill on memory pressure.

BUG=none

Review URL: https://codereview.chromium.org/1813633003

Cr-Commit-Position: refs/heads/master@{#384604}
  • Loading branch information
cylee authored and Commit bot committed Apr 1, 2016
1 parent 6c6e2a5 commit 88ca974
Show file tree
Hide file tree
Showing 8 changed files with 352 additions and 65 deletions.
103 changes: 63 additions & 40 deletions chrome/browser/memory/tab_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@
#include <set>
#include <vector>

#include "ash/multi_profile_uma.h"
#include "ash/session/session_state_delegate.h"
#include "ash/shell.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
Expand Down Expand Up @@ -54,7 +51,11 @@
#include "content/public/common/page_importance_signals.h"

#if defined(OS_CHROMEOS)
#include "ash/multi_profile_uma.h"
#include "ash/session/session_state_delegate.h"
#include "ash/shell.h"
#include "chrome/browser/memory/tab_manager_delegate_chromeos.h"
#include "chromeos/chromeos_switches.h"
#endif

using base::TimeDelta;
Expand Down Expand Up @@ -221,32 +222,13 @@ void TabManager::Stop() {
memory_pressure_listener_.reset();
}

// Things to collect on the browser thread (because TabStripModel isn't thread
// safe):
// 1) whether or not a tab is pinned
// 2) last time a tab was selected
// 3) is the tab currently selected
TabStatsList TabManager::GetTabStats() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TabStatsList stats_list;
stats_list.reserve(32); // 99% of users have < 30 tabs open.

// TODO(chrisha): Move this code to a TabStripModel enumeration delegate!
if (!test_tab_strip_models_.empty()) {
for (size_t i = 0; i < test_tab_strip_models_.size(); ++i) {
AddTabStats(test_tab_strip_models_[i].first, // tab_strip_model
test_tab_strip_models_[i].second, // is_app
i == 0, // is_active
&stats_list);
}
} else {
// The code here can only be tested under a full browser test.
AddTabStats(&stats_list);
}
TabStatsList stats_list(GetUnsortedTabStats());

// Sort the collected data so that least desirable to be killed is first, most
// desirable is last.
std::sort(stats_list.begin(), stats_list.end(), CompareTabStats);

return stats_list;
}

Expand Down Expand Up @@ -290,23 +272,18 @@ bool TabManager::IsTabDiscarded(content::WebContents* contents) const {
return GetWebContentsData(contents)->IsDiscarded();
}

// TODO(jamescook): This should consider tabs with references to other tabs,
// such as tabs created with JavaScript window.open(). Potentially consider
// discarding the entire set together, or use that in the priority computation.
bool TabManager::DiscardTab() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TabStatsList stats = GetTabStats();
if (stats.empty())
return false;
// Loop until a non-discarded tab to kill is found.
for (TabStatsList::const_reverse_iterator stats_rit = stats.rbegin();
stats_rit != stats.rend(); ++stats_rit) {
int64_t least_important_tab_id = stats_rit->tab_contents_id;
if (CanDiscardTab(least_important_tab_id) &&
DiscardTabById(least_important_tab_id))
return true;
void TabManager::DiscardTab() {
#if defined(OS_CHROMEOS)
// If --enable-arc-memory-management is on, call Chrome OS specific low memory
// handling process.
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
chromeos::switches::kEnableArcMemoryManagement)) {
delegate_->LowMemoryKill(weak_ptr_factory_.GetWeakPtr(),
GetUnsortedTabStats());
return;
}
return false;
#endif
DiscardTabImpl();
}

WebContents* TabManager::DiscardTabById(int64_t target_web_contents_id) {
Expand Down Expand Up @@ -819,4 +796,50 @@ void TabManager::DoChildProcessDispatch() {
base::TimeDelta::FromSeconds(kRendererNotificationDelayInSeconds));
}

// TODO(jamescook): This should consider tabs with references to other tabs,
// such as tabs created with JavaScript window.open(). Potentially consider
// discarding the entire set together, or use that in the priority computation.
bool TabManager::DiscardTabImpl() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TabStatsList stats = GetTabStats();

if (stats.empty())
return false;
// Loop until a non-discarded tab to kill is found.
for (TabStatsList::const_reverse_iterator stats_rit = stats.rbegin();
stats_rit != stats.rend(); ++stats_rit) {
int64_t least_important_tab_id = stats_rit->tab_contents_id;
if (CanDiscardTab(least_important_tab_id) &&
DiscardTabById(least_important_tab_id))
return true;
}
return false;
}

// Things to collect on the browser thread (because TabStripModel isn't thread
// safe):
// 1) whether or not a tab is pinned
// 2) last time a tab was selected
// 3) is the tab currently selected
TabStatsList TabManager::GetUnsortedTabStats() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TabStatsList stats_list;
stats_list.reserve(32); // 99% of users have < 30 tabs open.

// TODO(chrisha): Move this code to a TabStripModel enumeration delegate!
if (!test_tab_strip_models_.empty()) {
for (size_t i = 0; i < test_tab_strip_models_.size(); ++i) {
AddTabStats(test_tab_strip_models_[i].first, // tab_strip_model
test_tab_strip_models_[i].second, // is_app
i == 0, // is_active
&stats_list);
}
} else {
// The code here can only be tested under a full browser test.
AddTabStats(&stats_list);
}

return stats_list;
}

} // namespace memory
26 changes: 17 additions & 9 deletions chrome/browser/memory/tab_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <stdint.h>

#include <set>
#include <string>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -81,7 +82,7 @@ class TabManager : public TabStripModelObserver {
void Stop();

// Returns the list of the stats for all renderers. Must be called on the UI
// thread.
// thread. The returned list is sorted by reversed importance.
TabStatsList GetTabStats();

// Returns a sorted list of renderers, from most important to least important.
Expand All @@ -90,10 +91,14 @@ class TabManager : public TabStripModelObserver {
// Returns true if |contents| is currently discarded.
bool IsTabDiscarded(content::WebContents* contents) const;

// Goes through a list of checks to see if a tab is allowed to be discarded by
// the automatic tab discarding mechanism. Note that this is not used when
// discarding a particular tab from about:discards.
bool CanDiscardTab(int64_t target_web_contents_id) const;

// Discards a tab to free the memory occupied by its renderer. The tab still
// exists in the tab-strip; clicking on it will reload it. Returns true if it
// successfully found a tab and discarded it.
bool DiscardTab();
// exists in the tab-strip; clicking on it will reload it.
void DiscardTab();

// Discards a tab with the given unique ID. The tab still exists in the
// tab-strip; clicking on it will reload it. Returns true if it successfully
Expand All @@ -120,6 +125,7 @@ class TabManager : public TabStripModelObserver {
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, InvalidOrEmptyURL);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, IsInternalPage);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, OomPressureListener);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ProtectPDFPages);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ProtectRecentlyUsedTabs);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ProtectVideoTabs);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ReloadDiscardedTabContextMenu);
Expand Down Expand Up @@ -185,11 +191,6 @@ class TabManager : public TabStripModelObserver {
// that need to be run periodically (see comment in implementation).
void UpdateTimerCallback();

// Goes through a list of checks to see if a tab is allowed to be discarded by
// the automatic tab discarding mechanism. Note that this is not used when
// discarding a particular tab from about:discards.
bool CanDiscardTab(int64_t target_web_contents_id) const;

// Does the actual discard by destroying the WebContents in |model| at |index|
// and replacing it by an empty one. Returns the new WebContents or NULL if
// the operation fails (return value used only in testing).
Expand Down Expand Up @@ -232,6 +233,13 @@ class TabManager : public TabStripModelObserver {
// schedules another call to itself as long as memory pressure continues.
void DoChildProcessDispatch();

// Implementation of DiscardTab.
bool DiscardTabImpl();

// Returns the list of the stats for all renderers. Must be called on the UI
// thread.
TabStatsList GetUnsortedTabStats();

// Timer to periodically update the stats of the renderers.
base::RepeatingTimer update_timer_;

Expand Down
24 changes: 12 additions & 12 deletions chrome/browser/memory/tab_manager_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -105,23 +105,23 @@ IN_PROC_BROWSER_TEST_F(TabManagerTest, TabManagerBasics) {

// Discard a tab. It should kill the first tab, since it was the oldest
// and was not selected.
EXPECT_TRUE(tab_manager->DiscardTab());
EXPECT_TRUE(tab_manager->DiscardTabImpl());
EXPECT_EQ(3, tsm->count());
EXPECT_TRUE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(0)));
EXPECT_FALSE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(1)));
EXPECT_FALSE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(2)));
EXPECT_TRUE(tab_manager->recent_tab_discard());

// Run discard again, make sure it kills the second tab.
EXPECT_TRUE(tab_manager->DiscardTab());
EXPECT_TRUE(tab_manager->DiscardTabImpl());
EXPECT_EQ(3, tsm->count());
EXPECT_TRUE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(0)));
EXPECT_TRUE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(1)));
EXPECT_FALSE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(2)));

// Kill the third tab. It should not kill the last tab, since it is active
// tab.
EXPECT_FALSE(tab_manager->DiscardTab());
EXPECT_FALSE(tab_manager->DiscardTabImpl());
EXPECT_TRUE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(0)));
EXPECT_TRUE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(1)));
EXPECT_FALSE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(2)));
Expand Down Expand Up @@ -259,11 +259,11 @@ IN_PROC_BROWSER_TEST_F(TabManagerTest, InvalidOrEmptyURL) {

// This shouldn't be able to discard a tab as the background tab has not yet
// started loading (its URL is not committed).
EXPECT_FALSE(tab_manager->DiscardTab());
EXPECT_FALSE(tab_manager->DiscardTabImpl());

// Wait for the background tab to load which then allows it to be discarded.
load2.Wait();
EXPECT_TRUE(tab_manager->DiscardTab());
EXPECT_TRUE(tab_manager->DiscardTabImpl());
}

// Makes sure that PDF pages are protected.
Expand All @@ -287,7 +287,7 @@ IN_PROC_BROWSER_TEST_F(TabManagerTest, ProtectPDFPages) {

// No discarding should be possible as the only background tab is displaying a
// PDF page, hence protected.
EXPECT_FALSE(tab_manager->DiscardTab());
EXPECT_FALSE(tab_manager->DiscardTabImpl());
}

// Makes sure that recently opened or used tabs are protected, depending on the
Expand Down Expand Up @@ -320,13 +320,13 @@ IN_PROC_BROWSER_TEST_F(TabManagerTest, ProtectRecentlyUsedTabs) {
test_clock_.Advance(base::TimeDelta::FromMinutes(kProtectionTime / 2));

// Should not be able to discard a tab.
ASSERT_FALSE(tab_manager->DiscardTab());
ASSERT_FALSE(tab_manager->DiscardTabImpl());

// Advance the clock for more than the protection time.
test_clock_.Advance(base::TimeDelta::FromMinutes(kProtectionTime / 2 + 2));

// Should be able to discard the background tab now.
EXPECT_TRUE(tab_manager->DiscardTab());
EXPECT_TRUE(tab_manager->DiscardTabImpl());

// Activate the 2nd tab.
tsm->ActivateTabAt(1, true);
Expand All @@ -336,13 +336,13 @@ IN_PROC_BROWSER_TEST_F(TabManagerTest, ProtectRecentlyUsedTabs) {
test_clock_.Advance(base::TimeDelta::FromMinutes(kProtectionTime / 2));

// Should not be able to discard a tab.
ASSERT_FALSE(tab_manager->DiscardTab());
ASSERT_FALSE(tab_manager->DiscardTabImpl());

// Advance the clock for more than the protection time.
test_clock_.Advance(base::TimeDelta::FromMinutes(kProtectionTime / 2 + 2));

// Should be able to discard the background tab now.
EXPECT_TRUE(tab_manager->DiscardTab());
EXPECT_TRUE(tab_manager->DiscardTabImpl());

// This is necessary otherwise the test crashes in
// WebContentsData::WebContentsDestroyed.
Expand Down Expand Up @@ -379,13 +379,13 @@ IN_PROC_BROWSER_TEST_F(TabManagerTest, ProtectVideoTabs) {
video_stream_ui->OnStarted(base::Closure());

// Should not be able to discard a tab.
ASSERT_FALSE(tab_manager->DiscardTab());
ASSERT_FALSE(tab_manager->DiscardTabImpl());

// Remove the video stream.
video_stream_ui.reset();

// Should be able to discard the background tab now.
EXPECT_TRUE(tab_manager->DiscardTab());
EXPECT_TRUE(tab_manager->DiscardTabImpl());
}

} // namespace memory
Expand Down
Loading

0 comments on commit 88ca974

Please sign in to comment.