Chromium Code Reviews

Side by Side Diff: content/browser/memory/memory_coordinator_impl.cc

Issue 2374343002: Add MemoryCoordinatorImpl (Closed)
Patch Set: Addressed comments (still lacks tests & comments) Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff |
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/memory/memory_coordinator_impl.h"
6
7 #include "base/threading/thread_task_runner_handle.h"
8 #include "content/browser/memory/memory_monitor.h"
9 #include "content/public/common/content_features.h"
10
11 namespace content {
12
13 namespace {
14
15 #if defined(OS_ANDROID)
16 static const int kDefaultPredictedRendererSizeMB = 40;
17 #else
18 static const int kDefaultPredictedRendererSizeMB = 80;
19 #endif
20
21 mojom::MemoryState ToMojomMemoryState(base::MemoryState state) {
22 switch (state) {
23 case base::MemoryState::UNKNOWN:
24 return mojom::MemoryState::UNKNOWN;
25 case base::MemoryState::NORMAL:
26 return mojom::MemoryState::NORMAL;
27 case base::MemoryState::THROTTLED:
28 return mojom::MemoryState::THROTTLED;
29 case base::MemoryState::SUSPENDED:
30 return mojom::MemoryState::SUSPENDED;
31 default:
32 NOTREACHED();
33 return mojom::MemoryState::UNKNOWN;
34 }
35 }
36
37 } // namespace
38
39 // SingletonTraits for MemoryCoordinator. Returns MemoryCoordinatorImpl
40 // as an actual instance.
41 struct MemoryCoordinatorSingletonTraits
42 : public base::LeakySingletonTraits<MemoryCoordinator> {
43 static MemoryCoordinator* New() {
44 return new MemoryCoordinatorImpl(base::ThreadTaskRunnerHandle::Get(),
45 CreateMemoryMonitor());
46 }
47 };
48
49 // static
50 MemoryCoordinator* MemoryCoordinator::GetInstance() {
51 if (!base::FeatureList::IsEnabled(features::kMemoryCoordinator))
52 return nullptr;
53 return base::Singleton<MemoryCoordinator,
54 MemoryCoordinatorSingletonTraits>::get();
55 }
56
57 MemoryCoordinatorImpl::MemoryCoordinatorImpl(
58 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
59 std::unique_ptr<MemoryMonitor> memory_monitor)
60 : task_runner_(task_runner),
61 memory_monitor_(std::move(memory_monitor)),
62 weak_ptr_factory_(this) {
63 DCHECK(memory_monitor_.get());
64 update_state_callback_ = base::Bind(&MemoryCoordinatorImpl::UpdateState,
65 weak_ptr_factory_.GetWeakPtr());
66
67 // Set initial parameters for calculating the global state.
68 predicted_renderer_size_ = kDefaultPredictedRendererSizeMB;
69 num_children_until_throttled_ = 4;
70 num_children_until_suspended_ = 2;
71 num_children_back_to_normal_ = 5;
72 num_children_back_to_throttled_ = 3;
73 minimum_transition_period_ = base::TimeDelta::FromSeconds(30);
74 monitoring_iterval_ = base::TimeDelta::FromSeconds(5);
75 }
76
77 MemoryCoordinatorImpl::~MemoryCoordinatorImpl() {}
78
79 void MemoryCoordinatorImpl::Start() {
80 DCHECK(CalledOnValidThread());
81 DCHECK(last_state_change_.is_null());
82 ScheduleUpdateState(base::TimeDelta());
83 }
84
85 void MemoryCoordinatorImpl::OnChildAdded(int render_process_id) {
86 // Populate the global state as an initial state of a newly created process.
87 SetMemoryState(render_process_id, ToMojomMemoryState(current_state_));
88 }
89
90 base::MemoryState MemoryCoordinatorImpl::CalculateNextState() {
91 using MemoryState = base::MemoryState;
92
93 int available = memory_monitor_->GetFreeMemoryUntilCriticalMB();
94 if (available <= 0)
95 return MemoryState::SUSPENDED;
96
97 int num_children_until_critical = available / predicted_renderer_size_;
98
99 switch (current_state_) {
100 case MemoryState::NORMAL:
101 if (num_children_until_critical <= num_children_until_suspended_)
102 return MemoryState::SUSPENDED;
103 if (num_children_until_critical <= num_children_until_throttled_)
104 return MemoryState::THROTTLED;
105 return MemoryState::NORMAL;
106 case MemoryState::THROTTLED:
107 if (num_children_until_critical <= num_children_until_suspended_)
108 return MemoryState::SUSPENDED;
109 if (num_children_until_critical >= num_children_back_to_normal_)
110 return MemoryState::NORMAL;
111 return MemoryState::THROTTLED;
112 case MemoryState::SUSPENDED:
113 if (num_children_until_critical >= num_children_back_to_normal_)
114 return MemoryState::NORMAL;
115 if (num_children_until_critical >= num_children_back_to_throttled_)
116 return MemoryState::THROTTLED;
117 return MemoryState::SUSPENDED;
118 case MemoryState::UNKNOWN:
119 // Fall through
120 default:
121 NOTREACHED();
122 return MemoryState::UNKNOWN;
123 }
124 }
125
126 void MemoryCoordinatorImpl::UpdateState() {
127 base::TimeTicks now = base::TimeTicks::Now();
128 MemoryState prev_state = current_state_;
129 MemoryState next_state = CalculateNextState();
130
131 if (last_state_change_.is_null() || current_state_ != next_state) {
132 current_state_ = next_state;
133 last_state_change_ = now;
134 }
135
136 if (next_state != prev_state) {
137 NotifyStateToClients();
138 NotifyStateToChildren();
139 ScheduleUpdateState(minimum_transition_period_);
140 } else {
141 ScheduleUpdateState(monitoring_iterval_);
142 }
143 }
144
145 void MemoryCoordinatorImpl::NotifyStateToClients() {
146 // SUSPENDED state may not make sense to the browser process. Use THROTTLED
147 // instead when the global state is SUSPENDED.
148 // TODO(bashi): Maybe worth considering another state for the browser.
149 auto state = current_state_ == MemoryState::SUSPENDED ? MemoryState::THROTTLED
150 : current_state_;
151 base::MemoryCoordinatorClientRegistry::GetInstance()->Notify(state);
152 }
153
154 void MemoryCoordinatorImpl::NotifyStateToChildren() {
155 // TODO(bashi): Don't send SUSPENDED state to the foreground renderer.
156 // TODO(bashi): Consider how to deal with background renderers which can't
157 // be suspended.
158 auto mojo_state = ToMojomMemoryState(current_state_);
159 for (auto& iter : children())
160 SetMemoryState(iter.first, mojo_state);
161 }
162
163 void MemoryCoordinatorImpl::ScheduleUpdateState(base::TimeDelta delta) {
164 task_runner_->PostDelayedTask(FROM_HERE, update_state_callback_, delta);
165 }
166
167 } // namespace content
OLDNEW

Powered by Google App Engine