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. 4 5#include "base/memory/discardable_memory_manager.h" 6 7#include "base/bind.h" 8#include "base/synchronization/waitable_event.h" 9#include "base/threading/thread.h" 10#include "testing/gtest/include/gtest/gtest.h" 11 12namespace base { 13namespace { 14 15class TestAllocationImpl : public internal::DiscardableMemoryManagerAllocation { 16 public: 17 TestAllocationImpl() : is_allocated_(false), is_locked_(false) {} 18 virtual ~TestAllocationImpl() { DCHECK(!is_locked_); } 19 20 // Overridden from internal::DiscardableMemoryManagerAllocation: 21 virtual bool AllocateAndAcquireLock() OVERRIDE { 22 bool was_allocated = is_allocated_; 23 is_allocated_ = true; 24 DCHECK(!is_locked_); 25 is_locked_ = true; 26 return was_allocated; 27 } 28 virtual void ReleaseLock() OVERRIDE { 29 DCHECK(is_locked_); 30 is_locked_ = false; 31 } 32 virtual void Purge() OVERRIDE { 33 DCHECK(is_allocated_); 34 is_allocated_ = false; 35 } 36 37 bool is_locked() const { return is_locked_; } 38 39 private: 40 bool is_allocated_; 41 bool is_locked_; 42}; 43 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. 46const size_t kDefaultMemoryLimit = 1024; 47const size_t kDefaultSoftMemoryLimit = kDefaultMemoryLimit; 48 49class TestDiscardableMemoryManagerImpl 50 : public internal::DiscardableMemoryManager { 51 public: 52 TestDiscardableMemoryManagerImpl() 53 : DiscardableMemoryManager(kDefaultMemoryLimit, 54 kDefaultSoftMemoryLimit, 55 TimeDelta::Max()) {} 56 57 void SetNow(TimeTicks now) { now_ = now; } 58 59 private: 60 // Overriden from internal::DiscardableMemoryManager: 61 virtual TimeTicks Now() const OVERRIDE { return now_; } 62 63 TimeTicks now_; 64}; 65 66class DiscardableMemoryManagerTestBase { 67 public: 68 DiscardableMemoryManagerTestBase() {} 69 70 protected: 71 enum LockStatus { 72 LOCK_STATUS_FAILED, 73 LOCK_STATUS_PURGED, 74 LOCK_STATUS_SUCCESS 75 }; 76 77 size_t BytesAllocated() const { return manager_.GetBytesAllocatedForTest(); } 78 79 void SetMemoryLimit(size_t bytes) { manager_.SetMemoryLimit(bytes); } 80 81 void SetSoftMemoryLimit(size_t bytes) { manager_.SetSoftMemoryLimit(bytes); } 82 83 void SetHardMemoryLimitExpirationTime(TimeDelta time) { 84 manager_.SetHardMemoryLimitExpirationTime(time); 85 } 86 87 void Register(TestAllocationImpl* allocation, size_t bytes) { 88 manager_.Register(allocation, bytes); 89 } 90 91 void Unregister(TestAllocationImpl* allocation) { 92 manager_.Unregister(allocation); 93 } 94 95 bool IsRegistered(TestAllocationImpl* allocation) const { 96 return manager_.IsRegisteredForTest(allocation); 97 } 98 99 LockStatus Lock(TestAllocationImpl* allocation) { 100 bool purged; 101 if (!manager_.AcquireLock(allocation, &purged)) 102 return LOCK_STATUS_FAILED; 103 return purged ? LOCK_STATUS_PURGED : LOCK_STATUS_SUCCESS; 104 } 105 106 void Unlock(TestAllocationImpl* allocation) { 107 manager_.ReleaseLock(allocation); 108 } 109 110 LockStatus RegisterAndLock(TestAllocationImpl* allocation, size_t bytes) { 111 manager_.Register(allocation, bytes); 112 return Lock(allocation); 113 } 114 115 bool CanBePurged(TestAllocationImpl* allocation) const { 116 return manager_.CanBePurgedForTest(allocation); 117 } 118 119 void SetNow(TimeTicks now) { manager_.SetNow(now); } 120 121 void PurgeAll() { return manager_.PurgeAll(); } 122 123 bool ReduceMemoryUsage() { return manager_.ReduceMemoryUsage(); } 124 125 void ReduceMemoryUsageUntilWithinLimit(size_t bytes) { 126 manager_.ReduceMemoryUsageUntilWithinLimit(bytes); 127 } 128 129 private: 130 TestDiscardableMemoryManagerImpl manager_; 131}; 132 133class DiscardableMemoryManagerTest : public DiscardableMemoryManagerTestBase, 134 public testing::Test { 135 public: 136 DiscardableMemoryManagerTest() {} 137}; 138 139TEST_F(DiscardableMemoryManagerTest, CreateAndLock) { 140 size_t size = 1024; 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)); 148 Unlock(&allocation); 149 Unregister(&allocation); 150} 151 152TEST_F(DiscardableMemoryManagerTest, CreateZeroSize) { 153 size_t size = 0; 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); 160} 161 162TEST_F(DiscardableMemoryManagerTest, LockAfterUnlock) { 163 size_t size = 1024; 164 TestAllocationImpl allocation; 165 RegisterAndLock(&allocation, size); 166 EXPECT_EQ(1024u, BytesAllocated()); 167 EXPECT_FALSE(CanBePurged(&allocation)); 168 169 // Now unlock so we can lock later. 170 Unlock(&allocation); 171 EXPECT_TRUE(CanBePurged(&allocation)); 172 173 EXPECT_EQ(LOCK_STATUS_SUCCESS, Lock(&allocation)); 174 EXPECT_FALSE(CanBePurged(&allocation)); 175 Unlock(&allocation); 176 Unregister(&allocation); 177} 178 179TEST_F(DiscardableMemoryManagerTest, LockAfterPurge) { 180 size_t size = 1024; 181 TestAllocationImpl allocation; 182 RegisterAndLock(&allocation, size); 183 EXPECT_EQ(1024u, BytesAllocated()); 184 EXPECT_FALSE(CanBePurged(&allocation)); 185 186 // Now unlock so we can lock later. 187 Unlock(&allocation); 188 EXPECT_TRUE(CanBePurged(&allocation)); 189 190 // Force the system to purge. 191 PurgeAll(); 192 193 EXPECT_EQ(LOCK_STATUS_PURGED, Lock(&allocation)); 194 EXPECT_FALSE(CanBePurged(&allocation)); 195 196 Unlock(&allocation); 197 Unregister(&allocation); 198} 199 200TEST_F(DiscardableMemoryManagerTest, LockAfterPurgeAndCannotReallocate) { 201 size_t size = 1024; 202 TestAllocationImpl allocation; 203 RegisterAndLock(&allocation, size); 204 EXPECT_EQ(1024u, BytesAllocated()); 205 EXPECT_FALSE(CanBePurged(&allocation)); 206 207 // Now unlock so we can lock later. 208 Unlock(&allocation); 209 EXPECT_TRUE(CanBePurged(&allocation)); 210 211 // Set max allowed allocation to 1 byte. This will cause the memory to be 212 // purged. 213 SetMemoryLimit(1); 214 215 EXPECT_EQ(LOCK_STATUS_PURGED, Lock(&allocation)); 216 EXPECT_FALSE(CanBePurged(&allocation)); 217 218 Unlock(&allocation); 219 Unregister(&allocation); 220} 221 222TEST_F(DiscardableMemoryManagerTest, Overflow) { 223 size_t size = 1024; 224 { 225 TestAllocationImpl allocation; 226 RegisterAndLock(&allocation, size); 227 EXPECT_EQ(1024u, BytesAllocated()); 228 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()); 234 235 Unlock(&allocation); 236 EXPECT_EQ(LOCK_STATUS_PURGED, Lock(&massive_allocation)); 237 Unlock(&massive_allocation); 238 Unregister(&massive_allocation); 239 Unregister(&allocation); 240 } 241 EXPECT_EQ(0u, BytesAllocated()); 242} 243 244class PermutationTestData { 245 public: 246 PermutationTestData(unsigned d0, unsigned d1, unsigned d2) { 247 ordering_[0] = d0; 248 ordering_[1] = d1; 249 ordering_[2] = d2; 250 } 251 252 const unsigned* ordering() const { return ordering_; } 253 254 private: 255 unsigned ordering_[3]; 256}; 257 258class DiscardableMemoryManagerPermutationTest 259 : public DiscardableMemoryManagerTestBase, 260 public testing::TestWithParam<PermutationTestData> { 261 public: 262 DiscardableMemoryManagerPermutationTest() {} 263 264 protected: 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]); 270 } 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. 275 if (i > 0) 276 Unlock(&allocation_[index]); 277 } 278 } 279 280 TestAllocationImpl* allocation(unsigned position) { 281 return &allocation_[GetParam().ordering()[position]]; 282 } 283 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]); 289 } 290 } 291 292 private: 293 TestAllocationImpl allocation_[3]; 294}; 295 296// Verify that memory was discarded in the correct order after reducing usage to 297// limit. 298TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscarded) { 299 RegisterAndUseAllocations(); 300 301 SetMemoryLimit(2048); 302 303 ReduceMemoryUsageUntilWithinLimit(1024); 304 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()); 309 310 UnlockAndUnregisterAllocations(); 311} 312 313// Verify that memory was discarded in the correct order after changing 314// memory limit. 315TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedExceedLimit) { 316 RegisterAndUseAllocations(); 317 318 SetMemoryLimit(2048); 319 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()); 324 325 UnlockAndUnregisterAllocations(); 326} 327 328// Verify that no more memory than necessary was discarded after changing 329// memory limit. 330TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedAmount) { 331 SetMemoryLimit(4096); 332 333 RegisterAndUseAllocations(); 334 335 SetMemoryLimit(2048); 336 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()); 341 342 UnlockAndUnregisterAllocations(); 343} 344 345TEST_P(DiscardableMemoryManagerPermutationTest, PurgeFreesAllUnlocked) { 346 RegisterAndUseAllocations(); 347 348 PurgeAll(); 349 350 for (int i = 0; i < 3; ++i) { 351 if (i == 0) 352 EXPECT_TRUE(allocation(i)->is_locked()); 353 else 354 EXPECT_EQ(LOCK_STATUS_PURGED, Lock(allocation(i))); 355 } 356 357 UnlockAndUnregisterAllocations(); 358} 359 360INSTANTIATE_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))); 368 369TEST_F(DiscardableMemoryManagerTest, NormalDestruction) { 370 { 371 size_t size = 1024; 372 TestAllocationImpl allocation; 373 Register(&allocation, size); 374 Unregister(&allocation); 375 } 376 EXPECT_EQ(0u, BytesAllocated()); 377} 378 379TEST_F(DiscardableMemoryManagerTest, DestructionAfterLocked) { 380 { 381 size_t size = 1024; 382 TestAllocationImpl allocation; 383 RegisterAndLock(&allocation, size); 384 EXPECT_EQ(1024u, BytesAllocated()); 385 EXPECT_FALSE(CanBePurged(&allocation)); 386 Unlock(&allocation); 387 Unregister(&allocation); 388 } 389 EXPECT_EQ(0u, BytesAllocated()); 390} 391 392TEST_F(DiscardableMemoryManagerTest, DestructionAfterPurged) { 393 { 394 size_t size = 1024; 395 TestAllocationImpl allocation; 396 RegisterAndLock(&allocation, size); 397 EXPECT_EQ(1024u, BytesAllocated()); 398 Unlock(&allocation); 399 EXPECT_TRUE(CanBePurged(&allocation)); 400 SetMemoryLimit(0); 401 EXPECT_EQ(0u, BytesAllocated()); 402 Unregister(&allocation); 403 } 404 EXPECT_EQ(0u, BytesAllocated()); 405} 406 407TEST_F(DiscardableMemoryManagerTest, ReduceMemoryUsage) { 408 SetMemoryLimit(3072); 409 SetSoftMemoryLimit(1024); 410 SetHardMemoryLimitExpirationTime(TimeDelta::FromInternalValue(1)); 411 412 size_t size = 1024; 413 TestAllocationImpl allocation[3]; 414 RegisterAndLock(&allocation[0], size); 415 RegisterAndLock(&allocation[1], size); 416 RegisterAndLock(&allocation[2], size); 417 EXPECT_EQ(3072u, BytesAllocated()); 418 419 // Above soft limit but nothing that can be purged. 420 EXPECT_FALSE(ReduceMemoryUsage()); 421 422 SetNow(TimeTicks::FromInternalValue(0)); 423 Unlock(&allocation[0]); 424 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()); 428 429 SetNow(TimeTicks::FromInternalValue(1)); 430 Unlock(&allocation[1]); 431 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()); 437 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()); 444 445 Unlock(&allocation[2]); 446 447 Unregister(&allocation[0]); 448 Unregister(&allocation[1]); 449 Unregister(&allocation[2]); 450} 451 452class ThreadedDiscardableMemoryManagerTest 453 : public DiscardableMemoryManagerTest { 454 public: 455 ThreadedDiscardableMemoryManagerTest() 456 : memory_usage_thread_("memory_usage_thread"), 457 thread_sync_(true, false) {} 458 459 virtual void SetUp() OVERRIDE { memory_usage_thread_.Start(); } 460 461 virtual void TearDown() OVERRIDE { memory_usage_thread_.Stop(); } 462 463 void UseMemoryHelper() { 464 size_t size = 1024; 465 TestAllocationImpl allocation; 466 RegisterAndLock(&allocation, size); 467 Unlock(&allocation); 468 Unregister(&allocation); 469 } 470 471 void SignalHelper() { thread_sync_.Signal(); } 472 473 Thread memory_usage_thread_; 474 WaitableEvent thread_sync_; 475}; 476 477TEST_F(ThreadedDiscardableMemoryManagerTest, UseMemoryOnThread) { 478 memory_usage_thread_.message_loop()->PostTask( 479 FROM_HERE, 480 Bind(&ThreadedDiscardableMemoryManagerTest::UseMemoryHelper, 481 Unretained(this))); 482 memory_usage_thread_.message_loop()->PostTask( 483 FROM_HERE, 484 Bind(&ThreadedDiscardableMemoryManagerTest::SignalHelper, 485 Unretained(this))); 486 thread_sync_.Wait(); 487} 488 489} // namespace 490} // namespace base 491