1 // Copyright 2013 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_provider.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 DiscardableMemoryProviderTestBase
{
18 class TestDiscardableMemory
: public DiscardableMemory
{
20 TestDiscardableMemory(
21 internal::DiscardableMemoryProvider
* provider
, size_t size
)
22 : provider_(provider
),
24 provider_
->Register(this, size
);
27 virtual ~TestDiscardableMemory() {
30 provider_
->Unregister(this);
33 // Overridden from DiscardableMemory:
34 virtual DiscardableMemoryLockStatus
Lock() OVERRIDE
{
38 memory_
= provider_
->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 provider_
->Release(this, memory_
.Pass());
51 virtual void* Memory() const OVERRIDE
{
57 internal::DiscardableMemoryProvider
* provider_
;
58 scoped_ptr
<uint8
, FreeDeleter
> memory_
;
61 DISALLOW_COPY_AND_ASSIGN(TestDiscardableMemory
);
64 DiscardableMemoryProviderTestBase()
65 : provider_(new internal::DiscardableMemoryProvider
) {
66 provider_
->RegisterMemoryPressureListener();
70 bool IsRegistered(const DiscardableMemory
* discardable
) {
71 return provider_
->IsRegisteredForTest(discardable
);
74 bool CanBePurged(const DiscardableMemory
* discardable
) {
75 return provider_
->CanBePurgedForTest(discardable
);
78 size_t BytesAllocated() const {
79 return provider_
->GetBytesAllocatedForTest();
82 void* Memory(const DiscardableMemory
* discardable
) const {
83 return discardable
->Memory();
86 void SetDiscardableMemoryLimit(size_t bytes
) {
87 provider_
->SetDiscardableMemoryLimit(bytes
);
90 void SetBytesToReclaimUnderModeratePressure(size_t bytes
) {
91 provider_
->SetBytesToReclaimUnderModeratePressure(bytes
);
94 scoped_ptr
<DiscardableMemory
> CreateLockedMemory(size_t size
) {
95 scoped_ptr
<TestDiscardableMemory
> memory(
96 new TestDiscardableMemory(provider_
.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::DiscardableMemoryProvider
> provider_
;
107 class DiscardableMemoryProviderTest
108 : public DiscardableMemoryProviderTestBase
,
109 public testing::Test
{
111 DiscardableMemoryProviderTest() {}
114 TEST_F(DiscardableMemoryProviderTest
, 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(DiscardableMemoryProviderTest
, 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(DiscardableMemoryProviderTest
, 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(DiscardableMemoryProviderTest
, 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(DiscardableMemoryProviderTest
, 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(DiscardableMemoryProviderTest
, 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 DiscardableMemoryProviderPermutationTest
222 : public DiscardableMemoryProviderTestBase
,
223 public testing::TestWithParam
<PermutationTestData
> {
225 DiscardableMemoryProviderPermutationTest() {}
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(DiscardableMemoryProviderPermutationTest
, LRUDiscardedModeratePressure
) {
257 CreateAndUseDiscardableMemory();
259 SetBytesToReclaimUnderModeratePressure(1024);
260 MemoryPressureListener::NotifyMemoryPressure(
261 MemoryPressureListener::MEMORY_PRESSURE_MODERATE
);
262 RunLoop().RunUntilIdle();
264 EXPECT_NE(DISCARDABLE_MEMORY_LOCK_STATUS_FAILED
, discardable(2)->Lock());
265 EXPECT_NE(DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS
, discardable(1)->Lock());
266 // 0 should still be locked.
267 EXPECT_NE(static_cast<void*>(NULL
), Memory(discardable(0)));
270 // Verify that memory was discarded in the correct order after changing
272 TEST_P(DiscardableMemoryProviderPermutationTest
, LRUDiscardedExceedLimit
) {
273 CreateAndUseDiscardableMemory();
275 SetBytesToReclaimUnderModeratePressure(1024);
276 SetDiscardableMemoryLimit(2048);
278 EXPECT_NE(DISCARDABLE_MEMORY_LOCK_STATUS_FAILED
, discardable(2)->Lock());
279 EXPECT_NE(DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS
, discardable(1)->Lock());
280 // 0 should still be locked.
281 EXPECT_NE(static_cast<void*>(NULL
), Memory(discardable(0)));
284 // Verify that no more memory than necessary was discarded after changing
286 TEST_P(DiscardableMemoryProviderPermutationTest
, LRUDiscardedAmount
) {
287 SetBytesToReclaimUnderModeratePressure(2048);
288 SetDiscardableMemoryLimit(4096);
290 CreateAndUseDiscardableMemory();
292 SetDiscardableMemoryLimit(2048);
294 EXPECT_EQ(DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS
, discardable(2)->Lock());
295 EXPECT_EQ(DISCARDABLE_MEMORY_LOCK_STATUS_PURGED
, discardable(1)->Lock());
296 // 0 should still be locked.
297 EXPECT_NE(static_cast<void*>(NULL
), Memory(discardable(0)));
300 TEST_P(DiscardableMemoryProviderPermutationTest
,
301 CriticalPressureFreesAllUnlocked
) {
302 CreateAndUseDiscardableMemory();
304 MemoryPressureListener::NotifyMemoryPressure(
305 MemoryPressureListener::MEMORY_PRESSURE_CRITICAL
);
306 RunLoop().RunUntilIdle();
308 for (int i
= 0; i
< 3; ++i
) {
310 EXPECT_NE(static_cast<void*>(NULL
), Memory(discardable(i
)));
312 EXPECT_EQ(DISCARDABLE_MEMORY_LOCK_STATUS_PURGED
, discardable(i
)->Lock());
316 INSTANTIATE_TEST_CASE_P(DiscardableMemoryProviderPermutationTests
,
317 DiscardableMemoryProviderPermutationTest
,
318 ::testing::Values(PermutationTestData(0, 1, 2),
319 PermutationTestData(0, 2, 1),
320 PermutationTestData(1, 0, 2),
321 PermutationTestData(1, 2, 0),
322 PermutationTestData(2, 0, 1),
323 PermutationTestData(2, 1, 0)));
325 TEST_F(DiscardableMemoryProviderTest
, NormalDestruction
) {
328 const scoped_ptr
<DiscardableMemory
> discardable(CreateLockedMemory(size
));
329 EXPECT_TRUE(IsRegistered(discardable
.get()));
330 EXPECT_EQ(1024u, BytesAllocated());
332 EXPECT_EQ(0u, BytesAllocated());
335 TEST_F(DiscardableMemoryProviderTest
, DestructionWhileLocked
) {
338 const scoped_ptr
<DiscardableMemory
> discardable(CreateLockedMemory(size
));
339 EXPECT_TRUE(IsRegistered(discardable
.get()));
340 EXPECT_NE(static_cast<void*>(NULL
), Memory(discardable
.get()));
341 EXPECT_EQ(1024u, BytesAllocated());
342 EXPECT_FALSE(CanBePurged(discardable
.get()));
344 // Should have ignored the "locked" status and freed the discardable memory.
345 EXPECT_EQ(0u, BytesAllocated());
348 #if !defined(NDEBUG) && !defined(OS_ANDROID) && !defined(OS_IOS)
349 // Death tests are not supported with Android APKs.
350 TEST_F(DiscardableMemoryProviderTest
, UnlockedMemoryAccessCrashesInDebugMode
) {
352 const scoped_ptr
<DiscardableMemory
> discardable(CreateLockedMemory(size
));
353 EXPECT_TRUE(IsRegistered(discardable
.get()));
354 EXPECT_NE(static_cast<void*>(NULL
), Memory(discardable
.get()));
355 EXPECT_EQ(1024u, BytesAllocated());
356 EXPECT_FALSE(CanBePurged(discardable
.get()));
357 discardable
->Unlock();
358 EXPECT_TRUE(CanBePurged(discardable
.get()));
359 // We *must* die if we are asked to vend a pointer to unlocked memory.
360 EXPECT_DEATH(discardable
->Memory(), ".*Check failed.*");
364 class ThreadedDiscardableMemoryProviderTest
365 : public DiscardableMemoryProviderTest
{
367 ThreadedDiscardableMemoryProviderTest()
368 : memory_usage_thread_("memory_usage_thread"),
369 thread_sync_(true, false) {
372 virtual void SetUp() OVERRIDE
{
373 memory_usage_thread_
.Start();
376 virtual void TearDown() OVERRIDE
{
377 memory_usage_thread_
.Stop();
380 void UseMemoryHelper() {
382 const scoped_ptr
<DiscardableMemory
> discardable(CreateLockedMemory(size
));
383 EXPECT_TRUE(IsRegistered(discardable
.get()));
384 EXPECT_NE(static_cast<void*>(NULL
), Memory(discardable
.get()));
385 discardable
->Unlock();
388 void SignalHelper() {
389 thread_sync_
.Signal();
392 Thread memory_usage_thread_
;
393 WaitableEvent thread_sync_
;
396 TEST_F(ThreadedDiscardableMemoryProviderTest
, UseMemoryOnThread
) {
397 memory_usage_thread_
.message_loop()->PostTask(
399 Bind(&ThreadedDiscardableMemoryProviderTest::UseMemoryHelper
,
401 memory_usage_thread_
.message_loop()->PostTask(
403 Bind(&ThreadedDiscardableMemoryProviderTest::SignalHelper
,