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

Unified Diff: content/browser/memory/memory_coordinator_impl.cc

Issue 2867353002: memory coordinator: Introduce MemoryCoordinatorImpl::Policy (Closed)
Patch Set: Tweak Created 3 years, 7 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
Index: content/browser/memory/memory_coordinator_impl.cc
diff --git a/content/browser/memory/memory_coordinator_impl.cc b/content/browser/memory/memory_coordinator_impl.cc
index 8dbfa01f4082f02aa5b511444b47def7f818335f..c6847ad6c53050e3af1b6cc8a27a82b4f036ccf4 100644
--- a/content/browser/memory/memory_coordinator_impl.cc
+++ b/content/browser/memory/memory_coordinator_impl.cc
@@ -12,6 +12,7 @@
#include "base/time/default_tick_clock.h"
#include "base/trace_event/trace_event.h"
#include "content/browser/memory/memory_condition_observer.h"
+#include "content/browser/memory/memory_coordinator_default_policy.h"
#include "content/browser/memory/memory_monitor.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/notification_service.h"
@@ -23,12 +24,9 @@
namespace content {
-using MemoryState = base::MemoryState;
-
namespace {
const int kDefaultMinimumTransitionPeriodSeconds = 30;
-const int kDefaultBackgroundChildPurgeCandidatePeriodSeconds = 30;
mojom::MemoryState ToMojomMemoryState(MemoryState state) {
switch (state) {
@@ -59,25 +57,6 @@ const char* MemoryConditionToString(MemoryCondition condition) {
return "N/A";
}
-MemoryState CalculateMemoryStateForProcess(MemoryCondition condition,
- bool is_visible) {
- // The current heuristics for state calculation:
- // - Foregrounded(visible) processes: THROTTLED when condition is CRITICAL,
- // otherwise NORMAL.
- // - Backgrounded(invisible) processes: THROTTLED when condition is
- // WARNING/CRITICAL, otherwise NORMAL.
- switch (condition) {
- case MemoryCondition::NORMAL:
- return MemoryState::NORMAL;
- case MemoryCondition::WARNING:
- return is_visible ? MemoryState::NORMAL : MemoryState::THROTTLED;
- case MemoryCondition::CRITICAL:
- return MemoryState::THROTTLED;
- }
- NOTREACHED();
- return MemoryState::NORMAL;
-}
-
void RecordBrowserPurge(size_t before) {
auto metrics = base::ProcessMetrics::CreateCurrentProcessMetrics();
size_t after = metrics->GetWorkingSetSize();
@@ -160,6 +139,7 @@ MemoryCoordinatorImpl::MemoryCoordinatorImpl(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
std::unique_ptr<MemoryMonitor> memory_monitor)
: task_runner_(task_runner),
+ policy_(base::MakeUnique<MemoryCoordinatorDefaultPolicy>(this)),
delegate_(GetContentClient()->browser()->GetMemoryCoordinatorDelegate()),
memory_monitor_(std::move(memory_monitor)),
condition_observer_(
@@ -209,6 +189,32 @@ void MemoryCoordinatorImpl::CreateHandle(
CreateChildInfoMapEntry(render_process_id, std::move(handle));
}
+void MemoryCoordinatorImpl::SetBrowserMemoryState(MemoryState memory_state) {
+ if (memory_state == browser_memory_state_)
+ return;
+
+ base::TimeTicks now = tick_clock_->NowTicks();
+ base::TimeDelta elapsed = now - last_state_change_;
+ if (!last_state_change_.is_null() &&
+ (elapsed < minimum_state_transition_period_)) {
+ base::TimeDelta delay = minimum_state_transition_period_ - elapsed +
+ base::TimeDelta::FromSeconds(1);
+ delayed_browser_memory_state_setter_.Reset(
+ base::Bind(&MemoryCoordinatorImpl::SetBrowserMemoryState,
+ base::Unretained(this), memory_state));
+ task_runner_->PostDelayedTask(
+ FROM_HERE, delayed_browser_memory_state_setter_.callback(), delay);
+ return;
+ }
+
+ if (!delayed_browser_memory_state_setter_.IsCancelled())
+ delayed_browser_memory_state_setter_.Cancel();
+
+ last_state_change_ = now;
+ browser_memory_state_ = memory_state;
+ NotifyStateToClients(memory_state);
+}
+
bool MemoryCoordinatorImpl::SetChildMemoryState(int render_process_id,
MemoryState memory_state) {
// Can't set an invalid memory state.
@@ -241,12 +247,41 @@ bool MemoryCoordinatorImpl::SetChildMemoryState(int render_process_id,
return true;
}
-MemoryState MemoryCoordinatorImpl::GetChildMemoryState(
- int render_process_id) const {
- auto iter = children_.find(render_process_id);
- if (iter == children_.end())
- return MemoryState::UNKNOWN;
- return iter->second.memory_state;
+bool MemoryCoordinatorImpl::TryToPurgeMemoryFromBrowser() {
+ base::TimeTicks now = tick_clock_->NowTicks();
+ if (can_purge_after_ > now)
+ return false;
+
+ auto metrics = base::ProcessMetrics::CreateCurrentProcessMetrics();
+ size_t before = metrics->GetWorkingSetSize();
+ task_runner_->PostDelayedTask(FROM_HERE,
+ base::Bind(&RecordBrowserPurge, before),
+ base::TimeDelta::FromSeconds(2));
+
+ // Suppress purging in the browser process until a certain period of time is
+ // passed.
+ can_purge_after_ = now + base::TimeDelta::FromMinutes(2);
+ base::MemoryCoordinatorClientRegistry::GetInstance()->PurgeMemory();
+ return true;
+}
+
+bool MemoryCoordinatorImpl::TryToPurgeMemoryFromChild(int render_process_id) {
+ auto iter = children().find(render_process_id);
+ if (iter == children().end())
+ return false;
+ MemoryCoordinatorHandleImpl* handle = iter->second.handle.get();
+ if (!handle || !handle->child() || !handle->child().is_bound())
+ return false;
+ if (!iter->second.can_purge_after.is_null() &&
+ iter->second.can_purge_after > tick_clock_->NowTicks())
+ return false;
+
+ // Set |can_purge_after| to the maximum value to suppress another purge
+ // request until the child process goes foreground and then goes background
+ // again.
+ iter->second.can_purge_after = base::TimeTicks::Max();
+ handle->child()->PurgeMemory();
+ return true;
}
void MemoryCoordinatorImpl::RecordMemoryPressure(
@@ -273,7 +308,7 @@ void MemoryCoordinatorImpl::Observe(int type,
if (!process)
return;
bool is_visible = *Details<bool>(details).ptr();
- OnChildVisibilityChanged(process->GetID(), is_visible);
+ policy_->OnChildVisibilityChanged(process->GetID(), is_visible);
}
MemoryState MemoryCoordinatorImpl::GetStateForProcess(
@@ -297,45 +332,20 @@ void MemoryCoordinatorImpl::UpdateConditionIfNeeded(
DCHECK(CalledOnValidThread());
if (next_condition == MemoryCondition::WARNING)
- OnWarningCondition();
+ policy_->OnWarningCondition();
else if (next_condition == MemoryCondition::CRITICAL)
- OnCriticalCondition();
-
- if (memory_condition_ == next_condition)
- return;
+ policy_->OnCriticalCondition();
- if (suppress_condition_change_until_ > tick_clock_->NowTicks())
+ if (suppress_condition_change_until_ > tick_clock_->NowTicks() ||
+ memory_condition_ == next_condition)
return;
- MemoryCondition prev_condition = memory_condition_;
- memory_condition_ = next_condition;
-
TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("memory_coordinator"),
"MemoryCoordinatorImpl::UpdateConditionIfNeeded", "prev",
- MemoryConditionToString(prev_condition), "next",
+ MemoryConditionToString(memory_condition_), "next",
MemoryConditionToString(next_condition));
-
- // TODO(bashi): Following actions are tentative. We might want to prioritize
- // processes and handle them one-by-one.
-
- if (next_condition == MemoryCondition::NORMAL) {
- // Set NORMAL state to all clients/processes.
- UpdateBrowserStateAndNotifyStateToClients(MemoryState::NORMAL);
- NotifyStateToChildren(MemoryState::NORMAL);
- } else if (next_condition == MemoryCondition::WARNING) {
- // Set NORMAL state to foreground proceses and clients in the browser
- // process. Set THROTTLED state to background processes.
- UpdateBrowserStateAndNotifyStateToClients(MemoryState::NORMAL);
- for (auto& iter : children()) {
- auto state =
- iter.second.is_visible ? MemoryState::NORMAL : MemoryState::THROTTLED;
- SetChildMemoryState(iter.first, state);
- }
- } else if (next_condition == MemoryCondition::CRITICAL) {
- // Set THROTTLED state to all clients/processes.
- UpdateBrowserStateAndNotifyStateToClients(MemoryState::THROTTLED);
- NotifyStateToChildren(MemoryState::THROTTLED);
- }
+ policy_->OnConditionChanged(memory_condition_, next_condition);
+ memory_condition_ = next_condition;
}
void MemoryCoordinatorImpl::DiscardTab() {
@@ -396,28 +406,8 @@ void MemoryCoordinatorImpl::OnChildAdded(int render_process_id) {
// TODO(bashi): IsProcessBackgrounded() may return true even when tabs in the
// renderer process are invisible (e.g. restoring tabs all at once).
// Figure out a better way to set visibility.
- OnChildVisibilityChanged(render_process_id,
- !render_process_host->IsProcessBackgrounded());
-}
-
-void MemoryCoordinatorImpl::OnChildVisibilityChanged(int render_process_id,
- bool is_visible) {
- auto iter = children().find(render_process_id);
- if (iter == children().end())
- return;
-
- iter->second.is_visible = is_visible;
- if (!is_visible) {
- // A backgrounded process becomes a candidate for purging memory when
- // the process remains backgrounded for a certian period of time.
- iter->second.can_purge_after =
- tick_clock_->NowTicks() +
- base::TimeDelta::FromSeconds(
- kDefaultBackgroundChildPurgeCandidatePeriodSeconds);
- }
- MemoryState new_state =
- CalculateMemoryStateForProcess(GetMemoryCondition(), is_visible);
- SetChildMemoryState(render_process_id, new_state);
+ policy_->OnChildVisibilityChanged(
+ render_process_id, !render_process_host->IsProcessBackgrounded());
}
MemoryState MemoryCoordinatorImpl::OverrideState(MemoryState memory_state,
@@ -449,100 +439,10 @@ void MemoryCoordinatorImpl::CreateChildInfoMapEntry(
child_info.handle = std::move(handle);
}
-void MemoryCoordinatorImpl::UpdateBrowserStateAndNotifyStateToClients(
- MemoryState memory_state) {
- if (memory_state == browser_memory_state_)
- return;
-
- base::TimeTicks now = tick_clock_->NowTicks();
- base::TimeDelta elapsed = now - last_state_change_;
- if (!last_state_change_.is_null() &&
- (elapsed < minimum_state_transition_period_)) {
- base::TimeDelta delay = minimum_state_transition_period_ - elapsed +
- base::TimeDelta::FromSeconds(1);
- delayed_browser_memory_state_setter_.Reset(base::Bind(
- &MemoryCoordinatorImpl::UpdateBrowserStateAndNotifyStateToClients,
- base::Unretained(this), memory_state));
- task_runner_->PostDelayedTask(
- FROM_HERE, delayed_browser_memory_state_setter_.callback(), delay);
- return;
- }
-
- if (!delayed_browser_memory_state_setter_.IsCancelled())
- delayed_browser_memory_state_setter_.Cancel();
-
- last_state_change_ = now;
- browser_memory_state_ = memory_state;
- NotifyStateToClients(memory_state);
-}
-
void MemoryCoordinatorImpl::NotifyStateToClients(MemoryState state) {
base::MemoryCoordinatorClientRegistry::GetInstance()->Notify(state);
}
-void MemoryCoordinatorImpl::NotifyStateToChildren(MemoryState state) {
- // It's OK to call SetChildMemoryState() unconditionally because it checks
- // whether this state transition is valid.
- for (auto& iter : children())
- SetChildMemoryState(iter.first, state);
-}
-
-void MemoryCoordinatorImpl::OnWarningCondition() {
- TryToPurgeMemoryFromChildren(PurgeTarget::BACKGROUNDED);
-}
-
-void MemoryCoordinatorImpl::OnCriticalCondition() {
- DiscardTab();
-
- // Prefer to purge memory from child processes than browser process because
- // we prioritize the brower process.
- if (TryToPurgeMemoryFromChildren(PurgeTarget::ALL))
- return;
-
- TryToPurgeMemoryFromBrowser();
-}
-
-bool MemoryCoordinatorImpl::TryToPurgeMemoryFromChildren(PurgeTarget target) {
- base::TimeTicks now = tick_clock_->NowTicks();
- // TODO(bashi): Better to sort child processes based on their priorities.
- for (auto& iter : children()) {
- MemoryCoordinatorHandleImpl* handle = iter.second.handle.get();
- if (!handle || !handle->child() || !handle->child().is_bound())
- continue;
- if (iter.second.is_visible && target == PurgeTarget::BACKGROUNDED)
- continue;
- if (!iter.second.can_purge_after.is_null() &&
- iter.second.can_purge_after > now)
- continue;
-
- // Set |can_purge_after| to the maximum value to suppress another purge
- // request until the child process goes foreground and then goes background
- // again.
- iter.second.can_purge_after = base::TimeTicks::Max();
- handle->child()->PurgeMemory();
- return true;
- }
- return false;
-}
-
-bool MemoryCoordinatorImpl::TryToPurgeMemoryFromBrowser() {
- base::TimeTicks now = tick_clock_->NowTicks();
- if (can_purge_after_ > now)
- return false;
-
- auto metrics = base::ProcessMetrics::CreateCurrentProcessMetrics();
- size_t before = metrics->GetWorkingSetSize();
- task_runner_->PostDelayedTask(FROM_HERE,
- base::Bind(&RecordBrowserPurge, before),
- base::TimeDelta::FromSeconds(2));
-
- // Suppress purging in the browser process until a certain period of time is
- // passed.
- can_purge_after_ = now + base::TimeDelta::FromMinutes(2);
- base::MemoryCoordinatorClientRegistry::GetInstance()->PurgeMemory();
- return true;
-}
-
MemoryCoordinatorImpl::ChildInfo::ChildInfo() {}
MemoryCoordinatorImpl::ChildInfo::ChildInfo(const ChildInfo& rhs) {

Powered by Google App Engine
This is Rietveld 408576698