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 virtual ~TestAllocationImpl() { DCHECK(!is_locked_
); }
20 // Overridden from internal::DiscardableMemoryManagerAllocation:
21 virtual bool AllocateAndAcquireLock() OVERRIDE
{
22 bool was_allocated
= is_allocated_
;
28 virtual void ReleaseLock() OVERRIDE
{
32 virtual void Purge() OVERRIDE
{
33 DCHECK(is_allocated_
);
34 is_allocated_
= false;
37 bool is_locked() const { return is_locked_
; }
44 // Tests can assume that the default limit is at least 1024. Tests that rely on
45 // something else needs to explicit set the limit.
46 const size_t kDefaultMemoryLimit
= 1024;
47 const size_t kDefaultSoftMemoryLimit
= kDefaultMemoryLimit
;
49 class TestDiscardableMemoryManagerImpl
50 : public internal::DiscardableMemoryManager
{
52 TestDiscardableMemoryManagerImpl()
53 : DiscardableMemoryManager(kDefaultMemoryLimit
,
54 kDefaultSoftMemoryLimit
,
57 void SetNow(TimeTicks now
) { now_
= now
; }
60 // Overriden from internal::DiscardableMemoryManager:
61 virtual TimeTicks
Now() const OVERRIDE
{ return now_
; }
66 class DiscardableMemoryManagerTestBase
{
68 DiscardableMemoryManagerTestBase() {}
77 size_t BytesAllocated() const { return manager_
.GetBytesAllocatedForTest(); }
79 void SetMemoryLimit(size_t bytes
) { manager_
.SetMemoryLimit(bytes
); }
81 void SetSoftMemoryLimit(size_t bytes
) { manager_
.SetSoftMemoryLimit(bytes
); }
83 void SetHardMemoryLimitExpirationTime(TimeDelta time
) {
84 manager_
.SetHardMemoryLimitExpirationTime(time
);
87 void Register(TestAllocationImpl
* allocation
, size_t bytes
) {
88 manager_
.Register(allocation
, bytes
);
91 void Unregister(TestAllocationImpl
* allocation
) {
92 manager_
.Unregister(allocation
);
95 bool IsRegistered(TestAllocationImpl
* allocation
) const {
96 return manager_
.IsRegisteredForTest(allocation
);
99 LockStatus
Lock(TestAllocationImpl
* allocation
) {
101 if (!manager_
.AcquireLock(allocation
, &purged
))
102 return LOCK_STATUS_FAILED
;
103 return purged
? LOCK_STATUS_PURGED
: LOCK_STATUS_SUCCESS
;
106 void Unlock(TestAllocationImpl
* allocation
) {
107 manager_
.ReleaseLock(allocation
);
110 LockStatus
RegisterAndLock(TestAllocationImpl
* allocation
, size_t bytes
) {
111 manager_
.Register(allocation
, bytes
);
112 return Lock(allocation
);
115 bool CanBePurged(TestAllocationImpl
* allocation
) const {
116 return manager_
.CanBePurgedForTest(allocation
);
119 void SetNow(TimeTicks now
) { manager_
.SetNow(now
); }
121 void PurgeAll() { return manager_
.PurgeAll(); }
123 bool ReduceMemoryUsage() { return manager_
.ReduceMemoryUsage(); }
125 void ReduceMemoryUsageUntilWithinLimit(size_t bytes
) {
126 manager_
.ReduceMemoryUsageUntilWithinLimit(bytes
);
130 TestDiscardableMemoryManagerImpl manager_
;
133 class DiscardableMemoryManagerTest
: public DiscardableMemoryManagerTestBase
,
134 public testing::Test
{
136 DiscardableMemoryManagerTest() {}
139 TEST_F(DiscardableMemoryManagerTest
, CreateAndLock
) {
141 TestAllocationImpl allocation
;
142 Register(&allocation
, size
);
143 EXPECT_TRUE(IsRegistered(&allocation
));
144 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(&allocation
));
145 EXPECT_TRUE(allocation
.is_locked());
146 EXPECT_EQ(1024u, BytesAllocated());
147 EXPECT_FALSE(CanBePurged(&allocation
));
149 Unregister(&allocation
);
152 TEST_F(DiscardableMemoryManagerTest
, CreateZeroSize
) {
154 TestAllocationImpl allocation
;
155 Register(&allocation
, size
);
156 EXPECT_TRUE(IsRegistered(&allocation
));
157 EXPECT_EQ(LOCK_STATUS_FAILED
, Lock(&allocation
));
158 EXPECT_EQ(0u, BytesAllocated());
159 Unregister(&allocation
);
162 TEST_F(DiscardableMemoryManagerTest
, LockAfterUnlock
) {
164 TestAllocationImpl allocation
;
165 RegisterAndLock(&allocation
, size
);
166 EXPECT_EQ(1024u, BytesAllocated());
167 EXPECT_FALSE(CanBePurged(&allocation
));
169 // Now unlock so we can lock later.
171 EXPECT_TRUE(CanBePurged(&allocation
));
173 EXPECT_EQ(LOCK_STATUS_SUCCESS
, Lock(&allocation
));
174 EXPECT_FALSE(CanBePurged(&allocation
));
176 Unregister(&allocation
);
179 TEST_F(DiscardableMemoryManagerTest
, LockAfterPurge
) {
181 TestAllocationImpl allocation
;
182 RegisterAndLock(&allocation
, size
);
183 EXPECT_EQ(1024u, BytesAllocated());
184 EXPECT_FALSE(CanBePurged(&allocation
));
186 // Now unlock so we can lock later.
188 EXPECT_TRUE(CanBePurged(&allocation
));
190 // Force the system to purge.
193 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(&allocation
));
194 EXPECT_FALSE(CanBePurged(&allocation
));
197 Unregister(&allocation
);
200 TEST_F(DiscardableMemoryManagerTest
, LockAfterPurgeAndCannotReallocate
) {
202 TestAllocationImpl allocation
;
203 RegisterAndLock(&allocation
, size
);
204 EXPECT_EQ(1024u, BytesAllocated());
205 EXPECT_FALSE(CanBePurged(&allocation
));
207 // Now unlock so we can lock later.
209 EXPECT_TRUE(CanBePurged(&allocation
));
211 // Set max allowed allocation to 1 byte. This will cause the memory to be
215 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(&allocation
));
216 EXPECT_FALSE(CanBePurged(&allocation
));
219 Unregister(&allocation
);
222 TEST_F(DiscardableMemoryManagerTest
, Overflow
) {
225 TestAllocationImpl allocation
;
226 RegisterAndLock(&allocation
, size
);
227 EXPECT_EQ(1024u, BytesAllocated());
229 size_t massive_size
= std::numeric_limits
<size_t>::max();
230 TestAllocationImpl massive_allocation
;
231 Register(&massive_allocation
, massive_size
);
232 EXPECT_EQ(LOCK_STATUS_FAILED
, Lock(&massive_allocation
));
233 EXPECT_EQ(1024u, BytesAllocated());
236 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(&massive_allocation
));
237 Unlock(&massive_allocation
);
238 Unregister(&massive_allocation
);
239 Unregister(&allocation
);
241 EXPECT_EQ(0u, BytesAllocated());
244 class PermutationTestData
{
246 PermutationTestData(unsigned d0
, unsigned d1
, unsigned d2
) {
252 const unsigned* ordering() const { return ordering_
; }
255 unsigned ordering_
[3];
258 class DiscardableMemoryManagerPermutationTest
259 : public DiscardableMemoryManagerTestBase
,
260 public testing::TestWithParam
<PermutationTestData
> {
262 DiscardableMemoryManagerPermutationTest() {}
265 // Use memory in order specified by ordering parameter.
266 void RegisterAndUseAllocations() {
267 for (int i
= 0; i
< 3; ++i
) {
268 RegisterAndLock(&allocation_
[i
], 1024);
269 Unlock(&allocation_
[i
]);
271 for (int i
= 0; i
< 3; ++i
) {
272 int index
= GetParam().ordering()[i
];
273 EXPECT_NE(LOCK_STATUS_FAILED
, Lock(&allocation_
[index
]));
274 // Leave i == 0 locked.
276 Unlock(&allocation_
[index
]);
280 TestAllocationImpl
* allocation(unsigned position
) {
281 return &allocation_
[GetParam().ordering()[position
]];
284 void UnlockAndUnregisterAllocations() {
285 for (int i
= 0; i
< 3; ++i
) {
286 if (allocation_
[i
].is_locked())
287 Unlock(&allocation_
[i
]);
288 Unregister(&allocation_
[i
]);
293 TestAllocationImpl allocation_
[3];
296 // Verify that memory was discarded in the correct order after reducing usage to
298 TEST_P(DiscardableMemoryManagerPermutationTest
, LRUDiscarded
) {
299 RegisterAndUseAllocations();
301 SetMemoryLimit(2048);
303 ReduceMemoryUsageUntilWithinLimit(1024);
305 EXPECT_NE(LOCK_STATUS_FAILED
, Lock(allocation(2)));
306 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(allocation(1)));
307 // 0 should still be locked.
308 EXPECT_TRUE(allocation(0)->is_locked());
310 UnlockAndUnregisterAllocations();
313 // Verify that memory was discarded in the correct order after changing
315 TEST_P(DiscardableMemoryManagerPermutationTest
, LRUDiscardedExceedLimit
) {
316 RegisterAndUseAllocations();
318 SetMemoryLimit(2048);
320 EXPECT_NE(LOCK_STATUS_FAILED
, Lock(allocation(2)));
321 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(allocation(1)));
322 // 0 should still be locked.
323 EXPECT_TRUE(allocation(0)->is_locked());
325 UnlockAndUnregisterAllocations();
328 // Verify that no more memory than necessary was discarded after changing
330 TEST_P(DiscardableMemoryManagerPermutationTest
, LRUDiscardedAmount
) {
331 SetMemoryLimit(4096);
333 RegisterAndUseAllocations();
335 SetMemoryLimit(2048);
337 EXPECT_EQ(LOCK_STATUS_SUCCESS
, Lock(allocation(2)));
338 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(allocation(1)));
339 // 0 should still be locked.
340 EXPECT_TRUE(allocation(0)->is_locked());
342 UnlockAndUnregisterAllocations();
345 TEST_P(DiscardableMemoryManagerPermutationTest
, PurgeFreesAllUnlocked
) {
346 RegisterAndUseAllocations();
350 for (int i
= 0; i
< 3; ++i
) {
352 EXPECT_TRUE(allocation(i
)->is_locked());
354 EXPECT_EQ(LOCK_STATUS_PURGED
, Lock(allocation(i
)));
357 UnlockAndUnregisterAllocations();
360 INSTANTIATE_TEST_CASE_P(DiscardableMemoryManagerPermutationTests
,
361 DiscardableMemoryManagerPermutationTest
,
362 ::testing::Values(PermutationTestData(0, 1, 2),
363 PermutationTestData(0, 2, 1),
364 PermutationTestData(1, 0, 2),
365 PermutationTestData(1, 2, 0),
366 PermutationTestData(2, 0, 1),
367 PermutationTestData(2, 1, 0)));
369 TEST_F(DiscardableMemoryManagerTest
, NormalDestruction
) {
372 TestAllocationImpl allocation
;
373 Register(&allocation
, size
);
374 Unregister(&allocation
);
376 EXPECT_EQ(0u, BytesAllocated());
379 TEST_F(DiscardableMemoryManagerTest
, DestructionAfterLocked
) {
382 TestAllocationImpl allocation
;
383 RegisterAndLock(&allocation
, size
);
384 EXPECT_EQ(1024u, BytesAllocated());
385 EXPECT_FALSE(CanBePurged(&allocation
));
387 Unregister(&allocation
);
389 EXPECT_EQ(0u, BytesAllocated());
392 TEST_F(DiscardableMemoryManagerTest
, DestructionAfterPurged
) {
395 TestAllocationImpl allocation
;
396 RegisterAndLock(&allocation
, size
);
397 EXPECT_EQ(1024u, BytesAllocated());
399 EXPECT_TRUE(CanBePurged(&allocation
));
401 EXPECT_EQ(0u, BytesAllocated());
402 Unregister(&allocation
);
404 EXPECT_EQ(0u, BytesAllocated());
407 TEST_F(DiscardableMemoryManagerTest
, ReduceMemoryUsage
) {
408 SetMemoryLimit(3072);
409 SetSoftMemoryLimit(1024);
410 SetHardMemoryLimitExpirationTime(TimeDelta::FromInternalValue(1));
413 TestAllocationImpl allocation
[3];
414 RegisterAndLock(&allocation
[0], size
);
415 RegisterAndLock(&allocation
[1], size
);
416 RegisterAndLock(&allocation
[2], size
);
417 EXPECT_EQ(3072u, BytesAllocated());
419 // Above soft limit but nothing that can be purged.
420 EXPECT_FALSE(ReduceMemoryUsage());
422 SetNow(TimeTicks::FromInternalValue(0));
423 Unlock(&allocation
[0]);
425 // Above soft limit but still nothing that can be purged as all unlocked
426 // allocations are within the hard limit cutoff time.
427 EXPECT_FALSE(ReduceMemoryUsage());
429 SetNow(TimeTicks::FromInternalValue(1));
430 Unlock(&allocation
[1]);
432 // One unlocked allocation is no longer within the hard limit cutoff time. It
433 // should be purged and ReduceMemoryUsage() should return false as we're not
434 // yet within the soft memory limit.
435 EXPECT_FALSE(ReduceMemoryUsage());
436 EXPECT_EQ(2048u, BytesAllocated());
438 // One more unlocked allocation is no longer within the hard limit cutoff
439 // time. It should be purged and ReduceMemoryUsage() should return true as
440 // we're now within the soft memory limit.
441 SetNow(TimeTicks::FromInternalValue(2));
442 EXPECT_TRUE(ReduceMemoryUsage());
443 EXPECT_EQ(1024u, BytesAllocated());
445 Unlock(&allocation
[2]);
447 Unregister(&allocation
[0]);
448 Unregister(&allocation
[1]);
449 Unregister(&allocation
[2]);
452 class ThreadedDiscardableMemoryManagerTest
453 : public DiscardableMemoryManagerTest
{
455 ThreadedDiscardableMemoryManagerTest()
456 : memory_usage_thread_("memory_usage_thread"),
457 thread_sync_(true, false) {}
459 virtual void SetUp() OVERRIDE
{ memory_usage_thread_
.Start(); }
461 virtual void TearDown() OVERRIDE
{ memory_usage_thread_
.Stop(); }
463 void UseMemoryHelper() {
465 TestAllocationImpl allocation
;
466 RegisterAndLock(&allocation
, size
);
468 Unregister(&allocation
);
471 void SignalHelper() { thread_sync_
.Signal(); }
473 Thread memory_usage_thread_
;
474 WaitableEvent thread_sync_
;
477 TEST_F(ThreadedDiscardableMemoryManagerTest
, UseMemoryOnThread
) {
478 memory_usage_thread_
.message_loop()->PostTask(
480 Bind(&ThreadedDiscardableMemoryManagerTest::UseMemoryHelper
,
482 memory_usage_thread_
.message_loop()->PostTask(
484 Bind(&ThreadedDiscardableMemoryManagerTest::SignalHelper
,