OLD | NEW |
(Empty) | |
| 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. |
| 4 |
| 5 #include "base/memory/discardable_memory_provider.h" |
| 6 |
| 7 #include "base/memory/discardable_memory.h" |
| 8 #include "base/message_loop.h" |
| 9 #include "base/run_loop.h" |
| 10 #include "testing/gtest/include/gtest/gtest.h" |
| 11 |
| 12 namespace base { |
| 13 |
| 14 class DiscardableMemoryProviderTest : public testing::Test { |
| 15 public: |
| 16 DiscardableMemoryProviderTest() |
| 17 : message_loop_(MessageLoop::TYPE_IO), |
| 18 provider_(new DiscardableMemoryProvider) { |
| 19 // We set a provider here for two reasons: |
| 20 // 1. It ensures that one test cannot affect the next, and |
| 21 // 2. Since the provider listens for pressure notifications on the thread |
| 22 // it was created on, if we create it on the test thread, we can run |
| 23 // the test thread's message loop until idle when we want to process |
| 24 // on of these notifications. |
| 25 DiscardableMemoryProvider::SetInstanceForTest(provider_.get()); |
| 26 } |
| 27 |
| 28 virtual ~DiscardableMemoryProviderTest() { |
| 29 DiscardableMemoryProvider::SetInstanceForTest(NULL); |
| 30 } |
| 31 |
| 32 protected: |
| 33 void Register(DiscardableMemory* discardable) { |
| 34 DiscardableMemoryProvider::GetInstance()->Register(discardable); |
| 35 } |
| 36 |
| 37 const DiscardableMemoryProvider::AllocationMap& allocations() const { |
| 38 return DiscardableMemoryProvider::GetInstance()->allocations_; |
| 39 } |
| 40 |
| 41 size_t bytes_allocated() const { |
| 42 return DiscardableMemoryProvider::GetInstance()->bytes_allocated_; |
| 43 } |
| 44 |
| 45 void* Memory(const DiscardableMemory& discardable) const { |
| 46 return discardable.memory_; |
| 47 } |
| 48 |
| 49 void SetDiscardableMemoryLimit(size_t bytes) { |
| 50 DiscardableMemoryProvider::GetInstance()-> |
| 51 SetDiscardableMemoryLimit(bytes); |
| 52 } |
| 53 |
| 54 void SetBytesToReclaimUnderModeratePressure(size_t bytes) { |
| 55 DiscardableMemoryProvider::GetInstance()-> |
| 56 SetBytesToReclaimUnderModeratePressure(bytes); |
| 57 } |
| 58 |
| 59 private: |
| 60 MessageLoop message_loop_; |
| 61 scoped_ptr<DiscardableMemoryProvider> provider_; |
| 62 }; |
| 63 |
| 64 TEST_F(DiscardableMemoryProviderTest, Register) { |
| 65 DiscardableMemory discardable; |
| 66 Register(&discardable); |
| 67 ASSERT_NE(allocations().end(), allocations().Peek(&discardable)); |
| 68 ASSERT_EQ(0u, bytes_allocated()); |
| 69 } |
| 70 |
| 71 TEST_F(DiscardableMemoryProviderTest, InitializeAndLock) { |
| 72 DiscardableMemory discardable; |
| 73 ASSERT_EQ(static_cast<void*>(NULL), Memory(discardable)); |
| 74 size_t size = 1024; |
| 75 ASSERT_TRUE(discardable.InitializeAndLock(size)); |
| 76 ASSERT_NE(allocations().end(), allocations().Peek(&discardable)); |
| 77 ASSERT_NE(static_cast<void*>(NULL), Memory(discardable)); |
| 78 ASSERT_EQ(1024u, bytes_allocated()); |
| 79 ASSERT_TRUE(discardable.is_locked()); |
| 80 } |
| 81 |
| 82 TEST_F(DiscardableMemoryProviderTest, InitializeAndLockZeroSize) { |
| 83 DiscardableMemory discardable; |
| 84 ASSERT_EQ(static_cast<void*>(NULL), Memory(discardable)); |
| 85 size_t size = 0; |
| 86 ASSERT_FALSE(discardable.InitializeAndLock(size)); |
| 87 ASSERT_NE(allocations().end(), allocations().Peek(&discardable)); |
| 88 ASSERT_EQ(static_cast<void*>(NULL), Memory(discardable)); |
| 89 ASSERT_EQ(0u, bytes_allocated()); |
| 90 ASSERT_FALSE(discardable.is_locked()); |
| 91 } |
| 92 |
| 93 TEST_F(DiscardableMemoryProviderTest, LockBeforeInitialization) { |
| 94 DiscardableMemory discardable; |
| 95 ASSERT_EQ(DISCARDABLE_MEMORY_FAILED, discardable.Lock()); |
| 96 } |
| 97 |
| 98 TEST_F(DiscardableMemoryProviderTest, LockAfterUnlock) { |
| 99 DiscardableMemory discardable; |
| 100 ASSERT_EQ(static_cast<void*>(NULL), Memory(discardable)); |
| 101 size_t size = 1024; |
| 102 ASSERT_TRUE(discardable.InitializeAndLock(size)); |
| 103 ASSERT_NE(allocations().end(), allocations().Peek(&discardable)); |
| 104 ASSERT_NE(static_cast<void*>(NULL), Memory(discardable)); |
| 105 ASSERT_EQ(1024u, bytes_allocated()); |
| 106 ASSERT_TRUE(discardable.is_locked()); |
| 107 |
| 108 // Now to unlock so we can lock later. |
| 109 discardable.Unlock(); |
| 110 ASSERT_FALSE(discardable.is_locked()); |
| 111 |
| 112 ASSERT_EQ(DISCARDABLE_MEMORY_SUCCESS, discardable.Lock()); |
| 113 ASSERT_TRUE(discardable.is_locked()); |
| 114 } |
| 115 |
| 116 TEST_F(DiscardableMemoryProviderTest, LockAfterPurge) { |
| 117 DiscardableMemory discardable; |
| 118 ASSERT_EQ(static_cast<void*>(NULL), Memory(discardable)); |
| 119 size_t size = 1024; |
| 120 ASSERT_TRUE(discardable.InitializeAndLock(size)); |
| 121 ASSERT_NE(allocations().end(), allocations().Peek(&discardable)); |
| 122 ASSERT_NE(static_cast<void*>(NULL), Memory(discardable)); |
| 123 ASSERT_EQ(1024u, bytes_allocated()); |
| 124 ASSERT_TRUE(discardable.is_locked()); |
| 125 |
| 126 // Now to unlock so we can lock later. |
| 127 discardable.Unlock(); |
| 128 ASSERT_FALSE(discardable.is_locked()); |
| 129 |
| 130 // Force the system to purge. |
| 131 MemoryPressureListener::NotifyMemoryPressure( |
| 132 MemoryPressureListener::MEMORY_PRESSURE_CRITICAL); |
| 133 |
| 134 // Required because ObserverListThreadSafe notifies via PostTask. |
| 135 RunLoop().RunUntilIdle(); |
| 136 |
| 137 ASSERT_EQ(DISCARDABLE_MEMORY_PURGED, discardable.Lock()); |
| 138 ASSERT_TRUE(discardable.is_locked()); |
| 139 } |
| 140 |
| 141 TEST_F(DiscardableMemoryProviderTest, LockAfterPurgeAndCannotReallocate) { |
| 142 DiscardableMemory discardable; |
| 143 ASSERT_EQ(static_cast<void*>(NULL), Memory(discardable)); |
| 144 size_t size = 1024; |
| 145 ASSERT_TRUE(discardable.InitializeAndLock(size)); |
| 146 ASSERT_NE(allocations().end(), allocations().Peek(&discardable)); |
| 147 ASSERT_NE(static_cast<void*>(NULL), Memory(discardable)); |
| 148 ASSERT_EQ(1024u, bytes_allocated()); |
| 149 ASSERT_TRUE(discardable.is_locked()); |
| 150 |
| 151 // Now to unlock so we can lock later. |
| 152 discardable.Unlock(); |
| 153 ASSERT_FALSE(discardable.is_locked()); |
| 154 |
| 155 // Force the system to purge. |
| 156 MemoryPressureListener::NotifyMemoryPressure( |
| 157 MemoryPressureListener::MEMORY_PRESSURE_CRITICAL); |
| 158 |
| 159 // Required because ObserverListThreadSafe notifies via PostTask. |
| 160 RunLoop().RunUntilIdle(); |
| 161 |
| 162 // Set max allowed allocation to 1 byte. This will make reallocation fail. |
| 163 SetDiscardableMemoryLimit(1); |
| 164 |
| 165 ASSERT_EQ(DISCARDABLE_MEMORY_FAILED, discardable.Lock()); |
| 166 ASSERT_FALSE(discardable.is_locked()); |
| 167 } |
| 168 |
| 169 #define DiscardableMemoryProviderPermutionTest(name, d0, d1, d2, pressure) \ |
| 170 TEST_F(DiscardableMemoryProviderTest, name##_##d0##_##d1##_##d2) { \ |
| 171 DiscardableMemory discardables[3]; \ |
| 172 for (int i = 0; i < 3; ++i) { \ |
| 173 ASSERT_EQ(static_cast<void*>(NULL), Memory(discardables[i])); \ |
| 174 ASSERT_TRUE(discardables[i].InitializeAndLock(1024)); \ |
| 175 discardables[i].Unlock(); \ |
| 176 ASSERT_NE(static_cast<void*>(NULL), Memory(discardables[i])); \ |
| 177 } \ |
| 178 int ordering[] = { d0, d1, d2 }; \ |
| 179 for (int i = 0; i < 3; ++i) { \ |
| 180 int current_index = ordering[i]; \ |
| 181 ASSERT_NE(DISCARDABLE_MEMORY_FAILED, discardables[current_index].Lock()); \ |
| 182 if (i > 0) \ |
| 183 discardables[current_index].Unlock(); \ |
| 184 } \ |
| 185 SetBytesToReclaimUnderModeratePressure(1024); \ |
| 186 if (pressure) { \ |
| 187 MemoryPressureListener::NotifyMemoryPressure( \ |
| 188 MemoryPressureListener::MEMORY_PRESSURE_MODERATE); \ |
| 189 RunLoop().RunUntilIdle(); \ |
| 190 } else { \ |
| 191 SetDiscardableMemoryLimit(2048); \ |
| 192 } \ |
| 193 for (int i = 0; i < 3; ++i) { \ |
| 194 if (i == 1) \ |
| 195 ASSERT_EQ(static_cast<void*>(NULL), Memory(discardables[ordering[i]])); \ |
| 196 else \ |
| 197 ASSERT_NE(static_cast<void*>(NULL), Memory(discardables[ordering[i]])); \ |
| 198 } \ |
| 199 } |
| 200 |
| 201 #define DiscardableMemoryProviderPermutions(name, pressure) \ |
| 202 DiscardableMemoryProviderPermutionTest(name, 0, 1, 2, pressure); \ |
| 203 DiscardableMemoryProviderPermutionTest(name, 0, 2, 1, pressure); \ |
| 204 DiscardableMemoryProviderPermutionTest(name, 1, 0, 2, pressure); \ |
| 205 DiscardableMemoryProviderPermutionTest(name, 1, 2, 0, pressure); \ |
| 206 DiscardableMemoryProviderPermutionTest(name, 2, 0, 1, pressure); \ |
| 207 DiscardableMemoryProviderPermutionTest(name, 2, 1, 0, pressure); |
| 208 |
| 209 DiscardableMemoryProviderPermutions(LRUDiscardedModeratePressure, true); |
| 210 DiscardableMemoryProviderPermutions(LRUDiscardedExceedLimit, false); |
| 211 |
| 212 TEST_F(DiscardableMemoryProviderTest, CriticalPressureFreesAllUnlocked) { |
| 213 DiscardableMemory discardables[3]; |
| 214 for (int i = 0; i < 3; ++i) { |
| 215 ASSERT_EQ(static_cast<void*>(NULL), Memory(discardables[i])); |
| 216 ASSERT_TRUE(discardables[i].InitializeAndLock(1024)); |
| 217 discardables[i].Unlock(); |
| 218 ASSERT_NE(static_cast<void*>(NULL), Memory(discardables[i])); |
| 219 } |
| 220 |
| 221 for (int i = 0; i < 3; ++i) { |
| 222 ASSERT_NE(DISCARDABLE_MEMORY_FAILED, discardables[i].Lock()); |
| 223 if (i > 0) |
| 224 discardables[i].Unlock(); |
| 225 } |
| 226 |
| 227 MemoryPressureListener::NotifyMemoryPressure( |
| 228 MemoryPressureListener::MEMORY_PRESSURE_CRITICAL); |
| 229 RunLoop().RunUntilIdle(); |
| 230 |
| 231 for (int i = 0; i < 3; ++i) { |
| 232 if (discardables[i].is_locked()) |
| 233 ASSERT_NE(static_cast<void*>(NULL), Memory(discardables[i])); |
| 234 else |
| 235 ASSERT_EQ(static_cast<void*>(NULL), Memory(discardables[i])); |
| 236 } |
| 237 } |
| 238 |
| 239 TEST_F(DiscardableMemoryProviderTest, NormalDestruction) { |
| 240 { |
| 241 DiscardableMemory discardable; |
| 242 size_t size = 1024; |
| 243 ASSERT_TRUE(discardable.InitializeAndLock(size)); |
| 244 ASSERT_NE(allocations().end(), allocations().Peek(&discardable)); |
| 245 ASSERT_EQ(1024u, bytes_allocated()); |
| 246 } |
| 247 ASSERT_TRUE(allocations().empty()); |
| 248 ASSERT_EQ(0u, bytes_allocated()); |
| 249 } |
| 250 |
| 251 TEST_F(DiscardableMemoryProviderTest, DestructionWhileLocked) { |
| 252 { |
| 253 DiscardableMemory discardable; |
| 254 ASSERT_EQ(static_cast<void*>(NULL), Memory(discardable)); |
| 255 size_t size = 1024; |
| 256 ASSERT_TRUE(discardable.InitializeAndLock(size)); |
| 257 ASSERT_NE(allocations().end(), allocations().Peek(&discardable)); |
| 258 ASSERT_NE(static_cast<void*>(NULL), Memory(discardable)); |
| 259 ASSERT_EQ(1024u, bytes_allocated()); |
| 260 ASSERT_TRUE(discardable.is_locked()); |
| 261 } |
| 262 // Should have ignored the "locked" status and freed the discardable memory. |
| 263 ASSERT_TRUE(allocations().empty()); |
| 264 ASSERT_EQ(0u, bytes_allocated()); |
| 265 } |
| 266 |
| 267 TEST_F(DiscardableMemoryProviderTest, MemoryBeforeLock) { |
| 268 DiscardableMemory discardable; |
| 269 // We *must* die if we are asked to vend a pointer to unlocked memory. |
| 270 EXPECT_DEATH(discardable.Memory(), ".*Check failed.*"); |
| 271 } |
| 272 |
| 273 TEST_F(DiscardableMemoryProviderTest, MemoryAfterUnlock) { |
| 274 DiscardableMemory discardable; |
| 275 ASSERT_EQ(static_cast<void*>(NULL), Memory(discardable)); |
| 276 size_t size = 1024; |
| 277 ASSERT_TRUE(discardable.InitializeAndLock(size)); |
| 278 ASSERT_NE(allocations().end(), allocations().Peek(&discardable)); |
| 279 ASSERT_NE(static_cast<void*>(NULL), Memory(discardable)); |
| 280 ASSERT_EQ(1024u, bytes_allocated()); |
| 281 ASSERT_TRUE(discardable.is_locked()); |
| 282 discardable.Unlock(); |
| 283 ASSERT_FALSE(discardable.is_locked()); |
| 284 // We *must* die if we are asked to vend a pointer to unlocked memory. |
| 285 EXPECT_DEATH(discardable.Memory(), ".*Check failed.*"); |
| 286 } |
| 287 |
| 288 } // namespace base |
OLD | NEW |