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/run_loop.h"
9 #include "base/synchronization/waitable_event.h"
10 #include "base/threading/thread.h"
11 #include "testing/gtest/include/gtest/gtest.h"
16 class TestAllocationImpl
: public internal::DiscardableMemoryManagerAllocation
{
18 TestAllocationImpl() : is_allocated_(false), is_locked_(false) {}
19 virtual ~TestAllocationImpl() { DCHECK(!is_locked_
); }
21 // Overridden from internal::DiscardableMemoryManagerAllocation:
22 virtual bool AllocateAndAcquireLock() OVERRIDE
{
23 bool was_allocated
= is_allocated_
;
29 virtual void ReleaseLock() OVERRIDE
{
33 virtual void Purge() OVERRIDE
{
34 DCHECK(is_allocated_
);
35 is_allocated_
= false;
38 bool is_locked() const { return is_locked_
; }
45 // Tests can assume that the default limit is at least 1024. Tests that rely on
46 // something else needs to explicit set the limit.
47 const size_t kDefaultMemoryLimit
= 1024;
48 const size_t kDefaultSoftMemoryLimit
= kDefaultMemoryLimit
;
49 const size_t kDefaultBytesToKeepUnderModeratePressure
= kDefaultMemoryLimit
;
51 class TestDiscardableMemoryManagerImpl
52 : public internal::DiscardableMemoryManager
{
54 TestDiscardableMemoryManagerImpl()
55 : DiscardableMemoryManager(kDefaultMemoryLimit
,
56 kDefaultSoftMemoryLimit
,
57 kDefaultBytesToKeepUnderModeratePressure
,
60 void SetNow(TimeTicks now
) { now_
= now
; }
63 // Overriden from internal::DiscardableMemoryManager:
64 virtual TimeTicks
Now() const OVERRIDE
{ return now_
; }
69 class DiscardableMemoryManagerTestBase
{
71 DiscardableMemoryManagerTestBase() {
72 manager_
.RegisterMemoryPressureListener();
82 size_t BytesAllocated() const { return manager_
.GetBytesAllocatedForTest(); }
84 void SetMemoryLimit(size_t bytes
) { manager_
.SetMemoryLimit(bytes
); }
86 void SetSoftMemoryLimit(size_t bytes
) { manager_
.SetSoftMemoryLimit(bytes
); }
88 void SetBytesToKeepUnderModeratePressure(size_t bytes
) {
89 manager_
.SetBytesToKeepUnderModeratePressure(bytes
);
92 void SetHardMemoryLimitExpirationTime(TimeDelta time
) {
93 manager_
.SetHardMemoryLimitExpirationTime(time
);
96 void Register(TestAllocationImpl
* allocation
, size_t bytes
) {
97 manager_
.Register(allocation
, bytes
);
100 void Unregister(TestAllocationImpl
* allocation
) {
101 manager_
.Unregister(allocation
);
104 bool IsRegistered(TestAllocationImpl
* allocation
) const {
105 return manager_
.IsRegisteredForTest(allocation
);
108 LockStatus
Lock(TestAllocationImpl
* allocation
) {
110 if (!manager_
.AcquireLock(allocation
, &purged
))
111 return LOCK_STATUS_FAILED
;
112 return purged
? LOCK_STATUS_PURGED
: LOCK_STATUS_SUCCESS
;
115 void Unlock(TestAllocationImpl
* allocation
) {
116 manager_
.ReleaseLock(allocation
);
119 LockStatus
RegisterAndLock(TestAllocationImpl
* allocation
, size_t bytes
) {
120 manager_
.Register(allocation
, bytes
);
121 return Lock(allocation
);
124 bool CanBePurged(TestAllocationImpl
* allocation
) const {
125 return manager_
.CanBePurgedForTest(allocation
);
128 void SetNow(TimeTicks now
) { manager_
.SetNow(now
); }
130 bool ReduceMemoryUsage() { return manager_
.ReduceMemoryUsage(); }
133 MessageLoopForIO message_loop_
;
134 TestDiscardableMemoryManagerImpl manager_
;
137 class DiscardableMemoryManagerTest
: public DiscardableMemoryManagerTestBase
,
138 public testing::Test
{
140 DiscardableMemoryManagerTest() {}
143 TEST_F(DiscardableMemoryManagerTest
, CreateAndLock
) {
145 TestAllocationImpl allocation
;
146 Register(&allocation
, size
);
147 EXPECT_TRUE(IsRegistered(&allocation
));
148 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(&allocation
));
149 EXPECT_TRUE(allocation
.is_locked());
150 EXPECT_EQ(1024u, BytesAllocated());
151 EXPECT_FALSE(CanBePurged(&allocation
));
153 Unregister(&allocation
);
156 TEST_F(DiscardableMemoryManagerTest
, CreateZeroSize
) {
158 TestAllocationImpl allocation
;
159 Register(&allocation
, size
);
160 EXPECT_TRUE(IsRegistered(&allocation
));
161 EXPECT_EQ(LOCK_STATUS_FAILED
, Lock(&allocation
));
162 EXPECT_EQ(0u, BytesAllocated());
163 Unregister(&allocation
);
166 TEST_F(DiscardableMemoryManagerTest
, LockAfterUnlock
) {
168 TestAllocationImpl allocation
;
169 RegisterAndLock(&allocation
, size
);
170 EXPECT_EQ(1024u, BytesAllocated());
171 EXPECT_FALSE(CanBePurged(&allocation
));
173 // Now unlock so we can lock later.
175 EXPECT_TRUE(CanBePurged(&allocation
));
177 EXPECT_EQ(LOCK_STATUS_SUCCESS
, Lock(&allocation
));
178 EXPECT_FALSE(CanBePurged(&allocation
));
180 Unregister(&allocation
);
183 TEST_F(DiscardableMemoryManagerTest
, LockAfterPurge
) {
185 TestAllocationImpl allocation
;
186 RegisterAndLock(&allocation
, size
);
187 EXPECT_EQ(1024u, BytesAllocated());
188 EXPECT_FALSE(CanBePurged(&allocation
));
190 // Now unlock so we can lock later.
192 EXPECT_TRUE(CanBePurged(&allocation
));
194 // Force the system to purge.
195 MemoryPressureListener::NotifyMemoryPressure(
196 MemoryPressureListener::MEMORY_PRESSURE_CRITICAL
);
198 // Required because ObserverListThreadSafe notifies via PostTask.
199 RunLoop().RunUntilIdle();
201 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(&allocation
));
202 EXPECT_FALSE(CanBePurged(&allocation
));
205 Unregister(&allocation
);
208 TEST_F(DiscardableMemoryManagerTest
, LockAfterPurgeAndCannotReallocate
) {
210 TestAllocationImpl allocation
;
211 RegisterAndLock(&allocation
, size
);
212 EXPECT_EQ(1024u, BytesAllocated());
213 EXPECT_FALSE(CanBePurged(&allocation
));
215 // Now unlock so we can lock later.
217 EXPECT_TRUE(CanBePurged(&allocation
));
219 // Set max allowed allocation to 1 byte. This will cause the memory to be
223 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(&allocation
));
224 EXPECT_FALSE(CanBePurged(&allocation
));
227 Unregister(&allocation
);
230 TEST_F(DiscardableMemoryManagerTest
, Overflow
) {
233 TestAllocationImpl allocation
;
234 RegisterAndLock(&allocation
, size
);
235 EXPECT_EQ(1024u, BytesAllocated());
237 size_t massive_size
= std::numeric_limits
<size_t>::max();
238 TestAllocationImpl massive_allocation
;
239 Register(&massive_allocation
, massive_size
);
240 EXPECT_EQ(LOCK_STATUS_FAILED
, Lock(&massive_allocation
));
241 EXPECT_EQ(1024u, BytesAllocated());
244 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(&massive_allocation
));
245 Unlock(&massive_allocation
);
246 Unregister(&massive_allocation
);
247 Unregister(&allocation
);
249 EXPECT_EQ(0u, BytesAllocated());
252 class PermutationTestData
{
254 PermutationTestData(unsigned d0
, unsigned d1
, unsigned d2
) {
260 const unsigned* ordering() const { return ordering_
; }
263 unsigned ordering_
[3];
266 class DiscardableMemoryManagerPermutationTest
267 : public DiscardableMemoryManagerTestBase
,
268 public testing::TestWithParam
<PermutationTestData
> {
270 DiscardableMemoryManagerPermutationTest() {}
273 // Use memory in order specified by ordering parameter.
274 void RegisterAndUseAllocations() {
275 for (int i
= 0; i
< 3; ++i
) {
276 RegisterAndLock(&allocation_
[i
], 1024);
277 Unlock(&allocation_
[i
]);
279 for (int i
= 0; i
< 3; ++i
) {
280 int index
= GetParam().ordering()[i
];
281 EXPECT_NE(LOCK_STATUS_FAILED
, Lock(&allocation_
[index
]));
282 // Leave i == 0 locked.
284 Unlock(&allocation_
[index
]);
288 TestAllocationImpl
* allocation(unsigned position
) {
289 return &allocation_
[GetParam().ordering()[position
]];
292 void UnlockAndUnregisterAllocations() {
293 for (int i
= 0; i
< 3; ++i
) {
294 if (allocation_
[i
].is_locked())
295 Unlock(&allocation_
[i
]);
296 Unregister(&allocation_
[i
]);
301 TestAllocationImpl allocation_
[3];
304 // Verify that memory was discarded in the correct order after applying
306 TEST_P(DiscardableMemoryManagerPermutationTest
, LRUDiscardedModeratePressure
) {
307 RegisterAndUseAllocations();
309 SetBytesToKeepUnderModeratePressure(1024);
310 SetMemoryLimit(2048);
312 MemoryPressureListener::NotifyMemoryPressure(
313 MemoryPressureListener::MEMORY_PRESSURE_MODERATE
);
314 RunLoop().RunUntilIdle();
316 EXPECT_NE(LOCK_STATUS_FAILED
, Lock(allocation(2)));
317 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(allocation(1)));
318 // 0 should still be locked.
319 EXPECT_TRUE(allocation(0)->is_locked());
321 UnlockAndUnregisterAllocations();
324 // Verify that memory was discarded in the correct order after changing
326 TEST_P(DiscardableMemoryManagerPermutationTest
, LRUDiscardedExceedLimit
) {
327 RegisterAndUseAllocations();
329 SetBytesToKeepUnderModeratePressure(1024);
330 SetMemoryLimit(2048);
332 EXPECT_NE(LOCK_STATUS_FAILED
, Lock(allocation(2)));
333 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(allocation(1)));
334 // 0 should still be locked.
335 EXPECT_TRUE(allocation(0)->is_locked());
337 UnlockAndUnregisterAllocations();
340 // Verify that no more memory than necessary was discarded after changing
342 TEST_P(DiscardableMemoryManagerPermutationTest
, LRUDiscardedAmount
) {
343 SetBytesToKeepUnderModeratePressure(2048);
344 SetMemoryLimit(4096);
346 RegisterAndUseAllocations();
348 SetMemoryLimit(2048);
350 EXPECT_EQ(LOCK_STATUS_SUCCESS
, Lock(allocation(2)));
351 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(allocation(1)));
352 // 0 should still be locked.
353 EXPECT_TRUE(allocation(0)->is_locked());
355 UnlockAndUnregisterAllocations();
358 TEST_P(DiscardableMemoryManagerPermutationTest
, PurgeFreesAllUnlocked
) {
359 RegisterAndUseAllocations();
361 MemoryPressureListener::NotifyMemoryPressure(
362 MemoryPressureListener::MEMORY_PRESSURE_CRITICAL
);
363 RunLoop().RunUntilIdle();
365 for (int i
= 0; i
< 3; ++i
) {
367 EXPECT_TRUE(allocation(i
)->is_locked());
369 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(allocation(i
)));
372 UnlockAndUnregisterAllocations();
375 INSTANTIATE_TEST_CASE_P(DiscardableMemoryManagerPermutationTests
,
376 DiscardableMemoryManagerPermutationTest
,
377 ::testing::Values(PermutationTestData(0, 1, 2),
378 PermutationTestData(0, 2, 1),
379 PermutationTestData(1, 0, 2),
380 PermutationTestData(1, 2, 0),
381 PermutationTestData(2, 0, 1),
382 PermutationTestData(2, 1, 0)));
384 TEST_F(DiscardableMemoryManagerTest
, NormalDestruction
) {
387 TestAllocationImpl allocation
;
388 Register(&allocation
, size
);
389 Unregister(&allocation
);
391 EXPECT_EQ(0u, BytesAllocated());
394 TEST_F(DiscardableMemoryManagerTest
, DestructionAfterLocked
) {
397 TestAllocationImpl allocation
;
398 RegisterAndLock(&allocation
, size
);
399 EXPECT_EQ(1024u, BytesAllocated());
400 EXPECT_FALSE(CanBePurged(&allocation
));
402 Unregister(&allocation
);
404 EXPECT_EQ(0u, BytesAllocated());
407 TEST_F(DiscardableMemoryManagerTest
, DestructionAfterPurged
) {
410 TestAllocationImpl allocation
;
411 RegisterAndLock(&allocation
, size
);
412 EXPECT_EQ(1024u, BytesAllocated());
414 EXPECT_TRUE(CanBePurged(&allocation
));
416 EXPECT_EQ(0u, BytesAllocated());
417 Unregister(&allocation
);
419 EXPECT_EQ(0u, BytesAllocated());
422 TEST_F(DiscardableMemoryManagerTest
, ReduceMemoryUsage
) {
423 SetMemoryLimit(3072);
424 SetSoftMemoryLimit(1024);
425 SetHardMemoryLimitExpirationTime(TimeDelta::FromInternalValue(1));
428 TestAllocationImpl allocation
[3];
429 RegisterAndLock(&allocation
[0], size
);
430 RegisterAndLock(&allocation
[1], size
);
431 RegisterAndLock(&allocation
[2], size
);
432 EXPECT_EQ(3072u, BytesAllocated());
434 // Above soft limit but nothing that can be purged.
435 EXPECT_FALSE(ReduceMemoryUsage());
437 SetNow(TimeTicks::FromInternalValue(0));
438 Unlock(&allocation
[0]);
440 // Above soft limit but still nothing that can be purged as all unlocked
441 // allocations are within the hard limit cutoff time.
442 EXPECT_FALSE(ReduceMemoryUsage());
444 SetNow(TimeTicks::FromInternalValue(1));
445 Unlock(&allocation
[1]);
447 // One unlocked allocation is no longer within the hard limit cutoff time. It
448 // should be purged and ReduceMemoryUsage() should return false as we're not
449 // yet within the soft memory limit.
450 EXPECT_FALSE(ReduceMemoryUsage());
451 EXPECT_EQ(2048u, BytesAllocated());
453 // One more unlocked allocation is no longer within the hard limit cutoff
454 // time. It should be purged and ReduceMemoryUsage() should return true as
455 // we're now within the soft memory limit.
456 SetNow(TimeTicks::FromInternalValue(2));
457 EXPECT_TRUE(ReduceMemoryUsage());
458 EXPECT_EQ(1024u, BytesAllocated());
460 Unlock(&allocation
[2]);
462 Unregister(&allocation
[0]);
463 Unregister(&allocation
[1]);
464 Unregister(&allocation
[2]);
467 class ThreadedDiscardableMemoryManagerTest
468 : public DiscardableMemoryManagerTest
{
470 ThreadedDiscardableMemoryManagerTest()
471 : memory_usage_thread_("memory_usage_thread"),
472 thread_sync_(true, false) {}
474 virtual void SetUp() OVERRIDE
{ memory_usage_thread_
.Start(); }
476 virtual void TearDown() OVERRIDE
{ memory_usage_thread_
.Stop(); }
478 void UseMemoryHelper() {
480 TestAllocationImpl allocation
;
481 RegisterAndLock(&allocation
, size
);
483 Unregister(&allocation
);
486 void SignalHelper() { thread_sync_
.Signal(); }
488 Thread memory_usage_thread_
;
489 WaitableEvent thread_sync_
;
492 TEST_F(ThreadedDiscardableMemoryManagerTest
, UseMemoryOnThread
) {
493 memory_usage_thread_
.message_loop()->PostTask(
495 Bind(&ThreadedDiscardableMemoryManagerTest::UseMemoryHelper
,
497 memory_usage_thread_
.message_loop()->PostTask(
499 Bind(&ThreadedDiscardableMemoryManagerTest::SignalHelper
,