OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/memory/memory_coordinator_impl.h" | 5 #include "content/browser/memory/memory_coordinator_impl.h" |
6 | 6 |
| 7 #include "base/memory/memory_coordinator_client_registry.h" |
7 #include "base/memory/memory_coordinator_proxy.h" | 8 #include "base/memory/memory_coordinator_proxy.h" |
| 9 #include "base/memory/memory_pressure_monitor.h" |
| 10 #include "base/message_loop/message_loop.h" |
8 #include "base/run_loop.h" | 11 #include "base/run_loop.h" |
9 #include "base/test/scoped_feature_list.h" | 12 #include "base/test/scoped_feature_list.h" |
10 #include "base/test/test_mock_time_task_runner.h" | 13 #include "base/test/test_mock_time_task_runner.h" |
11 #include "content/browser/memory/memory_monitor.h" | 14 #include "content/browser/memory/memory_monitor.h" |
12 #include "content/browser/memory/memory_state_updater.h" | 15 #include "content/browser/memory/memory_state_updater.h" |
13 #include "content/public/common/content_features.h" | 16 #include "content/public/common/content_features.h" |
| 17 #include "mojo/public/cpp/bindings/binding.h" |
14 #include "testing/gtest/include/gtest/gtest.h" | 18 #include "testing/gtest/include/gtest/gtest.h" |
15 | 19 |
16 namespace content { | 20 namespace content { |
17 | 21 |
18 namespace { | 22 namespace { |
19 | 23 |
| 24 void RunUntilIdle() { |
| 25 base::RunLoop loop; |
| 26 loop.RunUntilIdle(); |
| 27 } |
| 28 |
| 29 // A mock ChildMemoryCoordinator, for testing interaction between MC and CMC. |
| 30 class MockChildMemoryCoordinator : public mojom::ChildMemoryCoordinator { |
| 31 public: |
| 32 MockChildMemoryCoordinator() |
| 33 : state_(mojom::MemoryState::NORMAL), |
| 34 on_state_change_calls_(0) {} |
| 35 |
| 36 ~MockChildMemoryCoordinator() override {} |
| 37 |
| 38 void OnStateChange(mojom::MemoryState state) override { |
| 39 state_ = state; |
| 40 ++on_state_change_calls_; |
| 41 } |
| 42 |
| 43 mojom::MemoryState state() const { return state_; } |
| 44 int on_state_change_calls() const { return on_state_change_calls_; } |
| 45 |
| 46 private: |
| 47 mojom::MemoryState state_; |
| 48 int on_state_change_calls_; |
| 49 }; |
| 50 |
| 51 // A mock MemoryCoordinatorClient, for testing interaction between MC and |
| 52 // clients. |
20 class MockMemoryCoordinatorClient : public base::MemoryCoordinatorClient { | 53 class MockMemoryCoordinatorClient : public base::MemoryCoordinatorClient { |
21 public: | 54 public: |
22 void OnMemoryStateChange(base::MemoryState state) override { | 55 void OnMemoryStateChange(base::MemoryState state) override { |
23 is_called_ = true; | 56 is_called_ = true; |
24 state_ = state; | 57 state_ = state; |
25 } | 58 } |
26 | 59 |
27 bool is_called() { return is_called_; } | 60 bool is_called() { return is_called_; } |
28 base::MemoryState state() { return state_; } | 61 base::MemoryState state() { return state_; } |
29 | 62 |
30 private: | 63 private: |
31 bool is_called_ = false; | 64 bool is_called_ = false; |
32 base::MemoryState state_ = base::MemoryState::NORMAL; | 65 base::MemoryState state_ = base::MemoryState::NORMAL; |
33 }; | 66 }; |
34 | 67 |
35 } // namespace | |
36 | |
37 class MockMemoryMonitor : public MemoryMonitor { | 68 class MockMemoryMonitor : public MemoryMonitor { |
38 public: | 69 public: |
39 MockMemoryMonitor() {} | 70 MockMemoryMonitor() {} |
40 ~MockMemoryMonitor() override {} | 71 ~MockMemoryMonitor() override {} |
41 | 72 |
42 void SetFreeMemoryUntilCriticalMB(int free_memory) { | 73 void SetFreeMemoryUntilCriticalMB(int free_memory) { |
43 free_memory_ = free_memory; | 74 free_memory_ = free_memory; |
44 } | 75 } |
45 | 76 |
46 // MemoryMonitor implementation | 77 // MemoryMonitor implementation |
47 int GetFreeMemoryUntilCriticalMB() override { return free_memory_; } | 78 int GetFreeMemoryUntilCriticalMB() override { return free_memory_; } |
48 | 79 |
49 private: | 80 private: |
50 int free_memory_ = 0; | 81 int free_memory_ = 0; |
51 | 82 |
52 DISALLOW_COPY_AND_ASSIGN(MockMemoryMonitor); | 83 DISALLOW_COPY_AND_ASSIGN(MockMemoryMonitor); |
53 }; | 84 }; |
54 | 85 |
| 86 // A MemoryCoordinatorImpl that can be directly constructed. |
| 87 class TestMemoryCoordinatorImpl : public MemoryCoordinatorImpl { |
| 88 public: |
| 89 // Mojo machinery for wrapping a mock ChildMemoryCoordinator. |
| 90 struct Child { |
| 91 Child(mojom::ChildMemoryCoordinatorPtr* cmc_ptr) : cmc_binding(&cmc) { |
| 92 cmc_binding.Bind(mojo::GetProxy(cmc_ptr)); |
| 93 RunUntilIdle(); |
| 94 } |
| 95 |
| 96 MockChildMemoryCoordinator cmc; |
| 97 mojo::Binding<mojom::ChildMemoryCoordinator> cmc_binding; |
| 98 }; |
| 99 |
| 100 TestMemoryCoordinatorImpl( |
| 101 scoped_refptr<base::TestMockTimeTaskRunner> task_runner) |
| 102 : MemoryCoordinatorImpl(task_runner, |
| 103 base::MakeUnique<MockMemoryMonitor>()) {} |
| 104 ~TestMemoryCoordinatorImpl() override {} |
| 105 |
| 106 using MemoryCoordinatorImpl::OnConnectionError; |
| 107 using MemoryCoordinatorImpl::children; |
| 108 |
| 109 MockChildMemoryCoordinator* CreateChildMemoryCoordinator( |
| 110 int process_id) { |
| 111 mojom::ChildMemoryCoordinatorPtr cmc_ptr; |
| 112 children_.push_back(std::unique_ptr<Child>(new Child(&cmc_ptr))); |
| 113 AddChildForTesting(process_id, std::move(cmc_ptr)); |
| 114 return &children_.back()->cmc; |
| 115 } |
| 116 |
| 117 // Wrapper of MemoryCoordinator::SetMemoryState that also calls RunUntilIdle. |
| 118 bool SetChildMemoryState( |
| 119 int render_process_id, mojom::MemoryState memory_state) { |
| 120 bool result = MemoryCoordinatorImpl::SetChildMemoryState( |
| 121 render_process_id, memory_state); |
| 122 RunUntilIdle(); |
| 123 return result; |
| 124 } |
| 125 |
| 126 std::vector<std::unique_ptr<Child>> children_; |
| 127 }; |
| 128 |
| 129 } // namespace |
| 130 |
55 class MemoryCoordinatorImplTest : public testing::Test { | 131 class MemoryCoordinatorImplTest : public testing::Test { |
56 public: | 132 public: |
57 void SetUp() override { | 133 void SetUp() override { |
58 scoped_feature_list_.InitAndEnableFeature(features::kMemoryCoordinator); | 134 scoped_feature_list_.InitAndEnableFeature(features::kMemoryCoordinator); |
59 | 135 |
60 task_runner_ = new base::TestMockTimeTaskRunner(); | 136 task_runner_ = new base::TestMockTimeTaskRunner(); |
61 coordinator_.reset(new MemoryCoordinatorImpl( | 137 coordinator_.reset(new TestMemoryCoordinatorImpl(task_runner_)); |
62 task_runner_, base::WrapUnique(new MockMemoryMonitor))); | |
63 | 138 |
64 base::MemoryCoordinatorProxy::GetInstance()-> | 139 base::MemoryCoordinatorProxy::GetInstance()-> |
65 SetGetCurrentMemoryStateCallback(base::Bind( | 140 SetGetCurrentMemoryStateCallback(base::Bind( |
66 &MemoryCoordinator::GetCurrentMemoryState, | 141 &MemoryCoordinatorImpl::GetCurrentMemoryState, |
67 base::Unretained(coordinator_.get()))); | 142 base::Unretained(coordinator_.get()))); |
68 base::MemoryCoordinatorProxy::GetInstance()-> | 143 base::MemoryCoordinatorProxy::GetInstance()-> |
69 SetSetCurrentMemoryStateForTestingCallback(base::Bind( | 144 SetSetCurrentMemoryStateForTestingCallback(base::Bind( |
70 &MemoryCoordinator::SetCurrentMemoryStateForTesting, | 145 &MemoryCoordinatorImpl::SetCurrentMemoryStateForTesting, |
71 base::Unretained(coordinator_.get()))); | 146 base::Unretained(coordinator_.get()))); |
72 } | 147 } |
73 | 148 |
74 MockMemoryMonitor* GetMockMemoryMonitor() { | 149 MockMemoryMonitor* GetMockMemoryMonitor() { |
75 return static_cast<MockMemoryMonitor*>(coordinator_->memory_monitor()); | 150 return static_cast<MockMemoryMonitor*>(coordinator_->memory_monitor()); |
76 } | 151 } |
77 | 152 |
78 protected: | 153 protected: |
79 std::unique_ptr<MemoryCoordinatorImpl> coordinator_; | 154 std::unique_ptr<TestMemoryCoordinatorImpl> coordinator_; |
80 scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; | 155 scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; |
81 base::MessageLoop message_loop_; | 156 base::MessageLoop message_loop_; |
82 base::test::ScopedFeatureList scoped_feature_list_; | 157 base::test::ScopedFeatureList scoped_feature_list_; |
83 }; | 158 }; |
84 | 159 |
| 160 TEST_F(MemoryCoordinatorImplTest, ChildRemovedOnConnectionError) { |
| 161 coordinator_->CreateChildMemoryCoordinator(1); |
| 162 ASSERT_EQ(1u, coordinator_->children().size()); |
| 163 coordinator_->OnConnectionError(1); |
| 164 EXPECT_EQ(0u, coordinator_->children().size()); |
| 165 } |
| 166 |
| 167 TEST_F(MemoryCoordinatorImplTest, SetMemoryStateFailsInvalidState) { |
| 168 auto cmc1 = coordinator_->CreateChildMemoryCoordinator(1); |
| 169 |
| 170 EXPECT_FALSE( |
| 171 coordinator_->SetChildMemoryState(1, mojom::MemoryState::UNKNOWN)); |
| 172 EXPECT_EQ(0, cmc1->on_state_change_calls()); |
| 173 } |
| 174 |
| 175 TEST_F(MemoryCoordinatorImplTest, SetMemoryStateFailsInvalidRenderer) { |
| 176 auto cmc1 = coordinator_->CreateChildMemoryCoordinator(1); |
| 177 |
| 178 EXPECT_FALSE( |
| 179 coordinator_->SetChildMemoryState(2, mojom::MemoryState::THROTTLED)); |
| 180 EXPECT_EQ(0, cmc1->on_state_change_calls()); |
| 181 } |
| 182 |
| 183 TEST_F(MemoryCoordinatorImplTest, SetMemoryStateNotDeliveredNop) { |
| 184 auto cmc1 = coordinator_->CreateChildMemoryCoordinator(1); |
| 185 |
| 186 EXPECT_FALSE( |
| 187 coordinator_->SetChildMemoryState(2, mojom::MemoryState::NORMAL)); |
| 188 EXPECT_EQ(0, cmc1->on_state_change_calls()); |
| 189 } |
| 190 |
| 191 TEST_F(MemoryCoordinatorImplTest, SetMemoryStateDelivered) { |
| 192 auto cmc1 = coordinator_->CreateChildMemoryCoordinator(1); |
| 193 auto cmc2 = coordinator_->CreateChildMemoryCoordinator(2); |
| 194 |
| 195 EXPECT_TRUE( |
| 196 coordinator_->SetChildMemoryState(1, mojom::MemoryState::THROTTLED)); |
| 197 EXPECT_EQ(1, cmc1->on_state_change_calls()); |
| 198 EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc1->state()); |
| 199 |
| 200 EXPECT_TRUE( |
| 201 coordinator_->SetChildMemoryState(2, mojom::MemoryState::SUSPENDED)); |
| 202 EXPECT_EQ(1, cmc2->on_state_change_calls()); |
| 203 // Child processes are considered as visible (foreground) by default, |
| 204 // and visible ones won't be suspended but throttled. |
| 205 EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc2->state()); |
| 206 } |
| 207 |
| 208 TEST_F(MemoryCoordinatorImplTest, SetChildMemoryState) { |
| 209 auto cmc = coordinator_->CreateChildMemoryCoordinator(1); |
| 210 auto iter = coordinator_->children().find(1); |
| 211 ASSERT_TRUE(iter != coordinator_->children().end()); |
| 212 |
| 213 // Foreground |
| 214 iter->second.is_visible = true; |
| 215 EXPECT_TRUE(coordinator_->SetChildMemoryState(1, mojom::MemoryState::NORMAL)); |
| 216 EXPECT_EQ(mojom::MemoryState::NORMAL, cmc->state()); |
| 217 EXPECT_TRUE( |
| 218 coordinator_->SetChildMemoryState(1, mojom::MemoryState::THROTTLED)); |
| 219 EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc->state()); |
| 220 EXPECT_TRUE( |
| 221 coordinator_->SetChildMemoryState(1, mojom::MemoryState::SUSPENDED)); |
| 222 EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc->state()); |
| 223 |
| 224 // Background |
| 225 iter->second.is_visible = false; |
| 226 EXPECT_TRUE(coordinator_->SetChildMemoryState(1, mojom::MemoryState::NORMAL)); |
| 227 #if defined(OS_ANDROID) |
| 228 EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc->state()); |
| 229 #else |
| 230 EXPECT_EQ(mojom::MemoryState::NORMAL, cmc->state()); |
| 231 #endif |
| 232 EXPECT_TRUE( |
| 233 coordinator_->SetChildMemoryState(1, mojom::MemoryState::THROTTLED)); |
| 234 EXPECT_EQ(mojom::MemoryState::THROTTLED, cmc->state()); |
| 235 EXPECT_TRUE( |
| 236 coordinator_->SetChildMemoryState(1, mojom::MemoryState::SUSPENDED)); |
| 237 EXPECT_EQ(mojom::MemoryState::SUSPENDED, cmc->state()); |
| 238 } |
| 239 |
85 TEST_F(MemoryCoordinatorImplTest, CalculateNextState) { | 240 TEST_F(MemoryCoordinatorImplTest, CalculateNextState) { |
86 auto* state_updater = coordinator_->state_updater_.get(); | 241 auto* state_updater = coordinator_->state_updater_.get(); |
87 state_updater->expected_renderer_size_ = 10; | 242 state_updater->expected_renderer_size_ = 10; |
88 state_updater->new_renderers_until_throttled_ = 4; | 243 state_updater->new_renderers_until_throttled_ = 4; |
89 state_updater->new_renderers_until_suspended_ = 2; | 244 state_updater->new_renderers_until_suspended_ = 2; |
90 state_updater->new_renderers_back_to_normal_ = 5; | 245 state_updater->new_renderers_back_to_normal_ = 5; |
91 state_updater->new_renderers_back_to_throttled_ = 3; | 246 state_updater->new_renderers_back_to_throttled_ = 3; |
92 DCHECK(state_updater->ValidateParameters()); | 247 DCHECK(state_updater->ValidateParameters()); |
93 | 248 |
94 // The default state is NORMAL. | 249 // The default state is NORMAL. |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
153 state_updater->new_renderers_back_to_throttled_ = 3; | 308 state_updater->new_renderers_back_to_throttled_ = 3; |
154 DCHECK(state_updater->ValidateParameters()); | 309 DCHECK(state_updater->ValidateParameters()); |
155 | 310 |
156 { | 311 { |
157 // Transition happens (NORMAL -> THROTTLED). | 312 // Transition happens (NORMAL -> THROTTLED). |
158 MockMemoryCoordinatorClient client; | 313 MockMemoryCoordinatorClient client; |
159 base::MemoryCoordinatorClientRegistry::GetInstance()->Register(&client); | 314 base::MemoryCoordinatorClientRegistry::GetInstance()->Register(&client); |
160 coordinator_->current_state_ = base::MemoryState::NORMAL; | 315 coordinator_->current_state_ = base::MemoryState::NORMAL; |
161 GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(40); | 316 GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(40); |
162 state_updater->UpdateState(); | 317 state_updater->UpdateState(); |
163 base::RunLoop loop; | 318 RunUntilIdle(); |
164 loop.RunUntilIdle(); | |
165 EXPECT_TRUE(client.is_called()); | 319 EXPECT_TRUE(client.is_called()); |
166 EXPECT_EQ(base::MemoryState::THROTTLED, client.state()); | 320 EXPECT_EQ(base::MemoryState::THROTTLED, client.state()); |
167 base::MemoryCoordinatorClientRegistry::GetInstance()->Unregister(&client); | 321 base::MemoryCoordinatorClientRegistry::GetInstance()->Unregister(&client); |
168 } | 322 } |
169 | 323 |
170 { | 324 { |
171 // No transtion (NORMAL -> NORMAL). OnStateChange shouldn't be called. | 325 // No transtion (NORMAL -> NORMAL). OnStateChange shouldn't be called. |
172 MockMemoryCoordinatorClient client; | 326 MockMemoryCoordinatorClient client; |
173 base::MemoryCoordinatorClientRegistry::GetInstance()->Register(&client); | 327 base::MemoryCoordinatorClientRegistry::GetInstance()->Register(&client); |
174 coordinator_->current_state_ = base::MemoryState::NORMAL; | 328 coordinator_->current_state_ = base::MemoryState::NORMAL; |
175 GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(50); | 329 GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(50); |
176 state_updater->UpdateState(); | 330 state_updater->UpdateState(); |
177 base::RunLoop loop; | 331 RunUntilIdle(); |
178 loop.RunUntilIdle(); | |
179 EXPECT_FALSE(client.is_called()); | 332 EXPECT_FALSE(client.is_called()); |
180 EXPECT_EQ(base::MemoryState::NORMAL, client.state()); | 333 EXPECT_EQ(base::MemoryState::NORMAL, client.state()); |
181 base::MemoryCoordinatorClientRegistry::GetInstance()->Unregister(&client); | 334 base::MemoryCoordinatorClientRegistry::GetInstance()->Unregister(&client); |
182 } | 335 } |
183 } | 336 } |
184 | 337 |
185 TEST_F(MemoryCoordinatorImplTest, SetMemoryStateForTesting) { | 338 TEST_F(MemoryCoordinatorImplTest, SetMemoryStateForTesting) { |
186 auto* state_updater = coordinator_->state_updater_.get(); | 339 auto* state_updater = coordinator_->state_updater_.get(); |
187 state_updater->expected_renderer_size_ = 10; | 340 state_updater->expected_renderer_size_ = 10; |
188 state_updater->new_renderers_until_throttled_ = 4; | 341 state_updater->new_renderers_until_throttled_ = 4; |
(...skipping 19 matching lines...) Expand all Loading... |
208 EXPECT_EQ(base::MemoryState::THROTTLED, | 361 EXPECT_EQ(base::MemoryState::THROTTLED, |
209 base::MemoryCoordinatorProxy::GetInstance()-> | 362 base::MemoryCoordinatorProxy::GetInstance()-> |
210 GetCurrentMemoryState()); | 363 GetCurrentMemoryState()); |
211 | 364 |
212 base::MemoryCoordinatorProxy::GetInstance()->SetCurrentMemoryStateForTesting( | 365 base::MemoryCoordinatorProxy::GetInstance()->SetCurrentMemoryStateForTesting( |
213 base::MemoryState::THROTTLED); | 366 base::MemoryState::THROTTLED); |
214 EXPECT_EQ(base::MemoryState::THROTTLED, | 367 EXPECT_EQ(base::MemoryState::THROTTLED, |
215 coordinator_->GetCurrentMemoryState()); | 368 coordinator_->GetCurrentMemoryState()); |
216 EXPECT_EQ(base::MemoryState::THROTTLED, | 369 EXPECT_EQ(base::MemoryState::THROTTLED, |
217 base::MemoryCoordinatorProxy::GetInstance()-> | 370 base::MemoryCoordinatorProxy::GetInstance()-> |
218 GetCurrentMemoryState()); | 371 GetCurrentMemoryState()); |
219 base::RunLoop loop; | 372 RunUntilIdle(); |
220 loop.RunUntilIdle(); | |
221 EXPECT_TRUE(client.is_called()); | 373 EXPECT_TRUE(client.is_called()); |
222 EXPECT_EQ(base::MemoryState::THROTTLED, client.state()); | 374 EXPECT_EQ(base::MemoryState::THROTTLED, client.state()); |
223 base::MemoryCoordinatorClientRegistry::GetInstance()->Unregister(&client); | 375 base::MemoryCoordinatorClientRegistry::GetInstance()->Unregister(&client); |
224 } | 376 } |
225 | 377 |
226 TEST_F(MemoryCoordinatorImplTest, ForceSetGlobalState) { | 378 TEST_F(MemoryCoordinatorImplTest, ForceSetGlobalState) { |
227 auto* state_updater = coordinator_->state_updater_.get(); | 379 auto* state_updater = coordinator_->state_updater_.get(); |
228 state_updater->expected_renderer_size_ = 10; | 380 state_updater->expected_renderer_size_ = 10; |
229 state_updater->new_renderers_until_throttled_ = 4; | 381 state_updater->new_renderers_until_throttled_ = 4; |
230 state_updater->new_renderers_until_suspended_ = 2; | 382 state_updater->new_renderers_until_suspended_ = 2; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
263 // Also make sure that the state is updated based on free avaiable memory. | 415 // Also make sure that the state is updated based on free avaiable memory. |
264 // Since the global state has changed in the previous task, we have to wait | 416 // Since the global state has changed in the previous task, we have to wait |
265 // for |minimum_transition|. | 417 // for |minimum_transition|. |
266 GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(40); | 418 GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(40); |
267 task_runner_->FastForwardBy(minimum_transition); | 419 task_runner_->FastForwardBy(minimum_transition); |
268 task_runner_->RunUntilIdle(); | 420 task_runner_->RunUntilIdle(); |
269 EXPECT_EQ(base::MemoryState::THROTTLED, coordinator_->GetGlobalMemoryState()); | 421 EXPECT_EQ(base::MemoryState::THROTTLED, coordinator_->GetGlobalMemoryState()); |
270 } | 422 } |
271 | 423 |
272 } // namespace content | 424 } // namespace content |
OLD | NEW |