1 // Copyright 2014 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.
5 #include "base/memory/discardable_memory_manager.h"
8 #include "base/memory/discardable_memory.h"
9 #include "base/run_loop.h"
10 #include "base/synchronization/waitable_event.h"
11 #include "base/threading/thread.h"
12 #include "testing/gtest/include/gtest/gtest.h"
16 class DiscardableMemoryManagerTestBase {
18 class TestDiscardableMemory : public DiscardableMemory {
20 TestDiscardableMemory(
21 internal::DiscardableMemoryManager* manager, size_t size)
24 manager_->Register(this, size);
27 virtual ~TestDiscardableMemory() {
30 manager_->Unregister(this);
33 // Overridden from DiscardableMemory:
34 virtual DiscardableMemoryLockStatus Lock() OVERRIDE {
38 memory_ = manager_->Acquire(this, &purged);
40 return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED;
43 return purged ? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED
44 : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS;
46 virtual void Unlock() OVERRIDE {
48 manager_->Release(this, memory_.Pass());
51 virtual void* Memory() const OVERRIDE {
57 internal::DiscardableMemoryManager* manager_;
58 scoped_ptr<uint8, FreeDeleter> memory_;
61 DISALLOW_COPY_AND_ASSIGN(TestDiscardableMemory);
64 DiscardableMemoryManagerTestBase()
65 : manager_(new internal::DiscardableMemoryManager) {
66 manager_->RegisterMemoryPressureListener();
70 bool IsRegistered(const DiscardableMemory* discardable) {
71 return manager_->IsRegisteredForTest(discardable);
74 bool CanBePurged(const DiscardableMemory* discardable) {
75 return manager_->CanBePurgedForTest(discardable);
78 size_t BytesAllocated() const {
79 return manager_->GetBytesAllocatedForTest();
82 void* Memory(const DiscardableMemory* discardable) const {
83 return discardable->Memory();
86 void SetDiscardableMemoryLimit(size_t bytes) {
87 manager_->SetDiscardableMemoryLimit(bytes);
90 void SetBytesToKeepUnderModeratePressure(size_t bytes) {
91 manager_->SetBytesToKeepUnderModeratePressure(bytes);
94 scoped_ptr<DiscardableMemory> CreateLockedMemory(size_t size) {
95 scoped_ptr<TestDiscardableMemory> memory(
96 new TestDiscardableMemory(manager_.get(), size));
97 if (memory->Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_PURGED)
98 return scoped_ptr<DiscardableMemory>();
99 return memory.PassAs<DiscardableMemory>();
103 MessageLoopForIO message_loop_;
104 scoped_ptr<internal::DiscardableMemoryManager> manager_;
107 class DiscardableMemoryManagerTest
108 : public DiscardableMemoryManagerTestBase,
109 public testing::Test {
111 DiscardableMemoryManagerTest() {}
114 TEST_F(DiscardableMemoryManagerTest, CreateLockedMemory) {
116 const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
117 EXPECT_TRUE(IsRegistered(discardable.get()));
118 EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get()));
119 EXPECT_EQ(1024u, BytesAllocated());
120 EXPECT_FALSE(CanBePurged(discardable.get()));
123 TEST_F(DiscardableMemoryManagerTest, CreateLockedMemoryZeroSize) {
125 const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
126 EXPECT_FALSE(discardable);
127 EXPECT_FALSE(IsRegistered(discardable.get()));
128 EXPECT_EQ(0u, BytesAllocated());
131 TEST_F(DiscardableMemoryManagerTest, LockAfterUnlock) {
133 const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
134 EXPECT_TRUE(IsRegistered(discardable.get()));
135 EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get()));
136 EXPECT_EQ(1024u, BytesAllocated());
137 EXPECT_FALSE(CanBePurged(discardable.get()));
139 // Now unlock so we can lock later.
140 discardable->Unlock();
141 EXPECT_TRUE(CanBePurged(discardable.get()));
143 EXPECT_EQ(DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS, discardable->Lock());
144 EXPECT_FALSE(CanBePurged(discardable.get()));
147 TEST_F(DiscardableMemoryManagerTest, LockAfterPurge) {
149 const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
150 EXPECT_TRUE(IsRegistered(discardable.get()));
151 EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get()));
152 EXPECT_EQ(1024u, BytesAllocated());
153 EXPECT_FALSE(CanBePurged(discardable.get()));
155 // Now unlock so we can lock later.
156 discardable->Unlock();
157 EXPECT_TRUE(CanBePurged(discardable.get()));
159 // Force the system to purge.
160 MemoryPressureListener::NotifyMemoryPressure(
161 MemoryPressureListener::MEMORY_PRESSURE_CRITICAL);
163 // Required because ObserverListThreadSafe notifies via PostTask.
164 RunLoop().RunUntilIdle();
166 EXPECT_EQ(DISCARDABLE_MEMORY_LOCK_STATUS_PURGED, discardable->Lock());
167 EXPECT_FALSE(CanBePurged(discardable.get()));
170 TEST_F(DiscardableMemoryManagerTest, LockAfterPurgeAndCannotReallocate) {
172 const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
173 EXPECT_TRUE(IsRegistered(discardable.get()));
174 EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get()));
175 EXPECT_EQ(1024u, BytesAllocated());
176 EXPECT_FALSE(CanBePurged(discardable.get()));
178 // Now unlock so we can lock later.
179 discardable->Unlock();
180 EXPECT_TRUE(CanBePurged(discardable.get()));
182 // Set max allowed allocation to 1 byte. This will make cause the memory
184 SetDiscardableMemoryLimit(1);
186 EXPECT_EQ(DISCARDABLE_MEMORY_LOCK_STATUS_PURGED, discardable->Lock());
187 EXPECT_FALSE(CanBePurged(discardable.get()));
190 TEST_F(DiscardableMemoryManagerTest, Overflow) {
193 const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
194 EXPECT_TRUE(IsRegistered(discardable.get()));
195 EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get()));
196 EXPECT_EQ(1024u, BytesAllocated());
198 size_t massive_size = std::numeric_limits<size_t>::max();
199 const scoped_ptr<DiscardableMemory> massive_discardable(
200 CreateLockedMemory(massive_size));
201 EXPECT_FALSE(massive_discardable);
202 EXPECT_EQ(1024u, BytesAllocated());
204 EXPECT_EQ(0u, BytesAllocated());
207 class PermutationTestData {
209 PermutationTestData(unsigned d0, unsigned d1, unsigned d2) {
215 const unsigned* ordering() const { return ordering_; }
218 unsigned ordering_[3];
221 class DiscardableMemoryManagerPermutationTest
222 : public DiscardableMemoryManagerTestBase,
223 public testing::TestWithParam<PermutationTestData> {
225 DiscardableMemoryManagerPermutationTest() {}
228 // Use discardable memory in order specified by ordering parameter.
229 void CreateAndUseDiscardableMemory() {
230 for (int i = 0; i < 3; ++i) {
231 discardables_[i] = CreateLockedMemory(1024);
232 EXPECT_TRUE(discardables_[i]);
233 EXPECT_NE(static_cast<void*>(NULL), Memory(discardables_[i].get()));
234 discardables_[i]->Unlock();
236 for (int i = 0; i < 3; ++i) {
237 int index = GetParam().ordering()[i];
238 EXPECT_NE(DISCARDABLE_MEMORY_LOCK_STATUS_FAILED,
239 discardables_[index]->Lock());
240 // Leave i == 0 locked.
242 discardables_[index]->Unlock();
246 DiscardableMemory* discardable(unsigned position) {
247 return discardables_[GetParam().ordering()[position]].get();
251 scoped_ptr<DiscardableMemory> discardables_[3];
254 // Verify that memory was discarded in the correct order after applying
256 TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedModeratePressure) {
257 CreateAndUseDiscardableMemory();
259 SetBytesToKeepUnderModeratePressure(1024);
260 SetDiscardableMemoryLimit(2048);
262 MemoryPressureListener::NotifyMemoryPressure(
263 MemoryPressureListener::MEMORY_PRESSURE_MODERATE);
264 RunLoop().RunUntilIdle();
266 EXPECT_NE(DISCARDABLE_MEMORY_LOCK_STATUS_FAILED, discardable(2)->Lock());
267 EXPECT_NE(DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS, discardable(1)->Lock());
268 // 0 should still be locked.
269 EXPECT_NE(static_cast<void*>(NULL), Memory(discardable(0)));
272 // Verify that memory was discarded in the correct order after changing
274 TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedExceedLimit) {
275 CreateAndUseDiscardableMemory();
277 SetBytesToKeepUnderModeratePressure(1024);
278 SetDiscardableMemoryLimit(2048);
280 EXPECT_NE(DISCARDABLE_MEMORY_LOCK_STATUS_FAILED, discardable(2)->Lock());
281 EXPECT_NE(DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS, discardable(1)->Lock());
282 // 0 should still be locked.
283 EXPECT_NE(static_cast<void*>(NULL), Memory(discardable(0)));
286 // Verify that no more memory than necessary was discarded after changing
288 TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedAmount) {
289 SetBytesToKeepUnderModeratePressure(2048);
290 SetDiscardableMemoryLimit(4096);
292 CreateAndUseDiscardableMemory();
294 SetDiscardableMemoryLimit(2048);
296 EXPECT_EQ(DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS, discardable(2)->Lock());
297 EXPECT_EQ(DISCARDABLE_MEMORY_LOCK_STATUS_PURGED, discardable(1)->Lock());
298 // 0 should still be locked.
299 EXPECT_NE(static_cast<void*>(NULL), Memory(discardable(0)));
302 TEST_P(DiscardableMemoryManagerPermutationTest,
303 CriticalPressureFreesAllUnlocked) {
304 CreateAndUseDiscardableMemory();
306 MemoryPressureListener::NotifyMemoryPressure(
307 MemoryPressureListener::MEMORY_PRESSURE_CRITICAL);
308 RunLoop().RunUntilIdle();
310 for (int i = 0; i < 3; ++i) {
312 EXPECT_NE(static_cast<void*>(NULL), Memory(discardable(i)));
314 EXPECT_EQ(DISCARDABLE_MEMORY_LOCK_STATUS_PURGED, discardable(i)->Lock());
318 INSTANTIATE_TEST_CASE_P(DiscardableMemoryManagerPermutationTests,
319 DiscardableMemoryManagerPermutationTest,
320 ::testing::Values(PermutationTestData(0, 1, 2),
321 PermutationTestData(0, 2, 1),
322 PermutationTestData(1, 0, 2),
323 PermutationTestData(1, 2, 0),
324 PermutationTestData(2, 0, 1),
325 PermutationTestData(2, 1, 0)));
327 TEST_F(DiscardableMemoryManagerTest, NormalDestruction) {
330 const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
331 EXPECT_TRUE(IsRegistered(discardable.get()));
332 EXPECT_EQ(1024u, BytesAllocated());
334 EXPECT_EQ(0u, BytesAllocated());
337 TEST_F(DiscardableMemoryManagerTest, DestructionWhileLocked) {
340 const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
341 EXPECT_TRUE(IsRegistered(discardable.get()));
342 EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get()));
343 EXPECT_EQ(1024u, BytesAllocated());
344 EXPECT_FALSE(CanBePurged(discardable.get()));
346 // Should have ignored the "locked" status and freed the discardable memory.
347 EXPECT_EQ(0u, BytesAllocated());
350 #if !defined(NDEBUG) && !defined(OS_ANDROID) && !defined(OS_IOS)
351 // Death tests are not supported with Android APKs.
352 TEST_F(DiscardableMemoryManagerTest, UnlockedMemoryAccessCrashesInDebugMode) {
354 const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
355 EXPECT_TRUE(IsRegistered(discardable.get()));
356 EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get()));
357 EXPECT_EQ(1024u, BytesAllocated());
358 EXPECT_FALSE(CanBePurged(discardable.get()));
359 discardable->Unlock();
360 EXPECT_TRUE(CanBePurged(discardable.get()));
361 // We *must* die if we are asked to vend a pointer to unlocked memory.
362 EXPECT_DEATH(discardable->Memory(), ".*Check failed.*");
366 class ThreadedDiscardableMemoryManagerTest
367 : public DiscardableMemoryManagerTest {
369 ThreadedDiscardableMemoryManagerTest()
370 : memory_usage_thread_("memory_usage_thread"),
371 thread_sync_(true, false) {
374 virtual void SetUp() OVERRIDE {
375 memory_usage_thread_.Start();
378 virtual void TearDown() OVERRIDE {
379 memory_usage_thread_.Stop();
382 void UseMemoryHelper() {
384 const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
385 EXPECT_TRUE(IsRegistered(discardable.get()));
386 EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get()));
387 discardable->Unlock();
390 void SignalHelper() {
391 thread_sync_.Signal();
394 Thread memory_usage_thread_;
395 WaitableEvent thread_sync_;
398 TEST_F(ThreadedDiscardableMemoryManagerTest, UseMemoryOnThread) {
399 memory_usage_thread_.message_loop()->PostTask(
401 Bind(&ThreadedDiscardableMemoryManagerTest::UseMemoryHelper,
403 memory_usage_thread_.message_loop()->PostTask(
405 Bind(&ThreadedDiscardableMemoryManagerTest::SignalHelper,