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/synchronization/waitable_event.h"
9 #include "base/threading/thread.h"
10 #include "testing/gtest/include/gtest/gtest.h"
15 class TestAllocationImpl
: public internal::DiscardableMemoryManagerAllocation
{
17 TestAllocationImpl() : is_allocated_(false), is_locked_(false) {}
18 ~TestAllocationImpl() override
{ DCHECK(!is_locked_
); }
20 // Overridden from internal::DiscardableMemoryManagerAllocation:
21 bool AllocateAndAcquireLock() override
{
22 bool was_allocated
= is_allocated_
;
28 void ReleaseLock() override
{
32 void Purge() override
{
33 DCHECK(is_allocated_
);
34 is_allocated_
= false;
36 bool IsMemoryResident() const override
{
37 DCHECK(is_allocated_
);
41 bool is_locked() const { return is_locked_
; }
48 // Tests can assume that the default limit is at least 1024. Tests that rely on
49 // something else needs to explicit set the limit.
50 const size_t kDefaultMemoryLimit
= 1024;
51 const size_t kDefaultSoftMemoryLimit
= kDefaultMemoryLimit
;
53 class TestDiscardableMemoryManagerImpl
54 : public internal::DiscardableMemoryManager
{
56 TestDiscardableMemoryManagerImpl()
57 : DiscardableMemoryManager(kDefaultMemoryLimit
,
58 kDefaultSoftMemoryLimit
,
61 void SetNow(TimeTicks now
) { now_
= now
; }
64 // Overriden from internal::DiscardableMemoryManager:
65 TimeTicks
Now() const override
{ return now_
; }
70 class DiscardableMemoryManagerTestBase
{
72 DiscardableMemoryManagerTestBase() {}
81 size_t BytesAllocated() const { return manager_
.GetBytesAllocatedForTest(); }
83 void SetMemoryLimit(size_t bytes
) { manager_
.SetMemoryLimit(bytes
); }
85 void SetSoftMemoryLimit(size_t bytes
) { manager_
.SetSoftMemoryLimit(bytes
); }
87 void SetHardMemoryLimitExpirationTime(TimeDelta time
) {
88 manager_
.SetHardMemoryLimitExpirationTime(time
);
91 void Register(TestAllocationImpl
* allocation
, size_t bytes
) {
92 manager_
.Register(allocation
, bytes
);
95 void Unregister(TestAllocationImpl
* allocation
) {
96 manager_
.Unregister(allocation
);
99 bool IsRegistered(TestAllocationImpl
* allocation
) const {
100 return manager_
.IsRegisteredForTest(allocation
);
103 LockStatus
Lock(TestAllocationImpl
* allocation
) {
105 if (!manager_
.AcquireLock(allocation
, &purged
))
106 return LOCK_STATUS_FAILED
;
107 return purged
? LOCK_STATUS_PURGED
: LOCK_STATUS_SUCCESS
;
110 void Unlock(TestAllocationImpl
* allocation
) {
111 manager_
.ReleaseLock(allocation
);
114 LockStatus
RegisterAndLock(TestAllocationImpl
* allocation
, size_t bytes
) {
115 manager_
.Register(allocation
, bytes
);
116 return Lock(allocation
);
119 bool CanBePurged(TestAllocationImpl
* allocation
) const {
120 return manager_
.CanBePurgedForTest(allocation
);
123 void SetNow(TimeTicks now
) { manager_
.SetNow(now
); }
125 void PurgeAll() { return manager_
.PurgeAll(); }
127 bool ReduceMemoryUsage() { return manager_
.ReduceMemoryUsage(); }
129 void ReduceMemoryUsageUntilWithinLimit(size_t bytes
) {
130 manager_
.ReduceMemoryUsageUntilWithinLimit(bytes
);
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.
197 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(&allocation
));
198 EXPECT_FALSE(CanBePurged(&allocation
));
201 Unregister(&allocation
);
204 TEST_F(DiscardableMemoryManagerTest
, LockAfterPurgeAndCannotReallocate
) {
206 TestAllocationImpl allocation
;
207 RegisterAndLock(&allocation
, size
);
208 EXPECT_EQ(1024u, BytesAllocated());
209 EXPECT_FALSE(CanBePurged(&allocation
));
211 // Now unlock so we can lock later.
213 EXPECT_TRUE(CanBePurged(&allocation
));
215 // Set max allowed allocation to 1 byte. This will cause the memory to be
219 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(&allocation
));
220 EXPECT_FALSE(CanBePurged(&allocation
));
223 Unregister(&allocation
);
226 TEST_F(DiscardableMemoryManagerTest
, Overflow
) {
229 TestAllocationImpl allocation
;
230 RegisterAndLock(&allocation
, size
);
231 EXPECT_EQ(1024u, BytesAllocated());
233 size_t massive_size
= std::numeric_limits
<size_t>::max();
234 TestAllocationImpl massive_allocation
;
235 Register(&massive_allocation
, massive_size
);
236 EXPECT_EQ(LOCK_STATUS_FAILED
, Lock(&massive_allocation
));
237 EXPECT_EQ(1024u, BytesAllocated());
240 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(&massive_allocation
));
241 Unlock(&massive_allocation
);
242 Unregister(&massive_allocation
);
243 Unregister(&allocation
);
245 EXPECT_EQ(0u, BytesAllocated());
248 class PermutationTestData
{
250 PermutationTestData(unsigned d0
, unsigned d1
, unsigned d2
) {
256 const unsigned* ordering() const { return ordering_
; }
259 unsigned ordering_
[3];
262 class DiscardableMemoryManagerPermutationTest
263 : public DiscardableMemoryManagerTestBase
,
264 public testing::TestWithParam
<PermutationTestData
> {
266 DiscardableMemoryManagerPermutationTest() {}
269 // Use memory in order specified by ordering parameter.
270 void RegisterAndUseAllocations() {
271 for (int i
= 0; i
< 3; ++i
) {
272 RegisterAndLock(&allocation_
[i
], 1024);
273 Unlock(&allocation_
[i
]);
275 for (int i
= 0; i
< 3; ++i
) {
276 int index
= GetParam().ordering()[i
];
277 EXPECT_NE(LOCK_STATUS_FAILED
, Lock(&allocation_
[index
]));
278 // Leave i == 0 locked.
280 Unlock(&allocation_
[index
]);
284 TestAllocationImpl
* allocation(unsigned position
) {
285 return &allocation_
[GetParam().ordering()[position
]];
288 void UnlockAndUnregisterAllocations() {
289 for (int i
= 0; i
< 3; ++i
) {
290 if (allocation_
[i
].is_locked())
291 Unlock(&allocation_
[i
]);
292 Unregister(&allocation_
[i
]);
297 TestAllocationImpl allocation_
[3];
300 // Verify that memory was discarded in the correct order after reducing usage to
302 TEST_P(DiscardableMemoryManagerPermutationTest
, LRUDiscarded
) {
303 RegisterAndUseAllocations();
305 SetMemoryLimit(2048);
307 ReduceMemoryUsageUntilWithinLimit(1024);
309 EXPECT_NE(LOCK_STATUS_FAILED
, Lock(allocation(2)));
310 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(allocation(1)));
311 // 0 should still be locked.
312 EXPECT_TRUE(allocation(0)->is_locked());
314 UnlockAndUnregisterAllocations();
317 // Verify that memory was discarded in the correct order after changing
319 TEST_P(DiscardableMemoryManagerPermutationTest
, LRUDiscardedExceedLimit
) {
320 RegisterAndUseAllocations();
322 SetMemoryLimit(2048);
324 EXPECT_NE(LOCK_STATUS_FAILED
, Lock(allocation(2)));
325 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(allocation(1)));
326 // 0 should still be locked.
327 EXPECT_TRUE(allocation(0)->is_locked());
329 UnlockAndUnregisterAllocations();
332 // Verify that no more memory than necessary was discarded after changing
334 TEST_P(DiscardableMemoryManagerPermutationTest
, LRUDiscardedAmount
) {
335 SetMemoryLimit(4096);
337 RegisterAndUseAllocations();
339 SetMemoryLimit(2048);
341 EXPECT_EQ(LOCK_STATUS_SUCCESS
, Lock(allocation(2)));
342 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(allocation(1)));
343 // 0 should still be locked.
344 EXPECT_TRUE(allocation(0)->is_locked());
346 UnlockAndUnregisterAllocations();
349 TEST_P(DiscardableMemoryManagerPermutationTest
, PurgeFreesAllUnlocked
) {
350 RegisterAndUseAllocations();
354 for (int i
= 0; i
< 3; ++i
) {
356 EXPECT_TRUE(allocation(i
)->is_locked());
358 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(allocation(i
)));
361 UnlockAndUnregisterAllocations();
364 INSTANTIATE_TEST_CASE_P(DiscardableMemoryManagerPermutationTests
,
365 DiscardableMemoryManagerPermutationTest
,
366 ::testing::Values(PermutationTestData(0, 1, 2),
367 PermutationTestData(0, 2, 1),
368 PermutationTestData(1, 0, 2),
369 PermutationTestData(1, 2, 0),
370 PermutationTestData(2, 0, 1),
371 PermutationTestData(2, 1, 0)));
373 TEST_F(DiscardableMemoryManagerTest
, NormalDestruction
) {
376 TestAllocationImpl allocation
;
377 Register(&allocation
, size
);
378 Unregister(&allocation
);
380 EXPECT_EQ(0u, BytesAllocated());
383 TEST_F(DiscardableMemoryManagerTest
, DestructionAfterLocked
) {
386 TestAllocationImpl allocation
;
387 RegisterAndLock(&allocation
, size
);
388 EXPECT_EQ(1024u, BytesAllocated());
389 EXPECT_FALSE(CanBePurged(&allocation
));
391 Unregister(&allocation
);
393 EXPECT_EQ(0u, BytesAllocated());
396 TEST_F(DiscardableMemoryManagerTest
, DestructionAfterPurged
) {
399 TestAllocationImpl allocation
;
400 RegisterAndLock(&allocation
, size
);
401 EXPECT_EQ(1024u, BytesAllocated());
403 EXPECT_TRUE(CanBePurged(&allocation
));
405 EXPECT_EQ(0u, BytesAllocated());
406 Unregister(&allocation
);
408 EXPECT_EQ(0u, BytesAllocated());
411 TEST_F(DiscardableMemoryManagerTest
, ReduceMemoryUsage
) {
412 SetMemoryLimit(3072);
413 SetSoftMemoryLimit(1024);
414 SetHardMemoryLimitExpirationTime(TimeDelta::FromInternalValue(1));
417 TestAllocationImpl allocation
[3];
418 RegisterAndLock(&allocation
[0], size
);
419 RegisterAndLock(&allocation
[1], size
);
420 RegisterAndLock(&allocation
[2], size
);
421 EXPECT_EQ(3072u, BytesAllocated());
423 // Above soft limit but nothing that can be purged.
424 EXPECT_FALSE(ReduceMemoryUsage());
426 SetNow(TimeTicks::FromInternalValue(0));
427 Unlock(&allocation
[0]);
429 // Above soft limit but still nothing that can be purged as all unlocked
430 // allocations are within the hard limit cutoff time.
431 EXPECT_FALSE(ReduceMemoryUsage());
433 SetNow(TimeTicks::FromInternalValue(1));
434 Unlock(&allocation
[1]);
436 // One unlocked allocation is no longer within the hard limit cutoff time. It
437 // should be purged and ReduceMemoryUsage() should return false as we're not
438 // yet within the soft memory limit.
439 EXPECT_FALSE(ReduceMemoryUsage());
440 EXPECT_EQ(2048u, BytesAllocated());
442 // One more unlocked allocation is no longer within the hard limit cutoff
443 // time. It should be purged and ReduceMemoryUsage() should return true as
444 // we're now within the soft memory limit.
445 SetNow(TimeTicks::FromInternalValue(2));
446 EXPECT_TRUE(ReduceMemoryUsage());
447 EXPECT_EQ(1024u, BytesAllocated());
449 Unlock(&allocation
[2]);
451 Unregister(&allocation
[0]);
452 Unregister(&allocation
[1]);
453 Unregister(&allocation
[2]);
456 class ThreadedDiscardableMemoryManagerTest
457 : public DiscardableMemoryManagerTest
{
459 ThreadedDiscardableMemoryManagerTest()
460 : memory_usage_thread_("memory_usage_thread"),
461 thread_sync_(true, false) {}
463 void SetUp() override
{ memory_usage_thread_
.Start(); }
465 void TearDown() override
{ memory_usage_thread_
.Stop(); }
467 void UseMemoryHelper() {
469 TestAllocationImpl allocation
;
470 RegisterAndLock(&allocation
, size
);
472 Unregister(&allocation
);
475 void SignalHelper() { thread_sync_
.Signal(); }
477 Thread memory_usage_thread_
;
478 WaitableEvent thread_sync_
;
481 TEST_F(ThreadedDiscardableMemoryManagerTest
, UseMemoryOnThread
) {
482 memory_usage_thread_
.message_loop()->PostTask(
484 Bind(&ThreadedDiscardableMemoryManagerTest::UseMemoryHelper
,
486 memory_usage_thread_
.message_loop()->PostTask(
488 Bind(&ThreadedDiscardableMemoryManagerTest::SignalHelper
,