| OLD | NEW |
| (Empty) |
| 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 "content/common/discardable_shared_memory_heap.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 #include <utility> | |
| 9 | |
| 10 #include "base/bind.h" | |
| 11 #include "base/memory/discardable_shared_memory.h" | |
| 12 #include "base/process/process_metrics.h" | |
| 13 #include "testing/gtest/include/gtest/gtest.h" | |
| 14 | |
| 15 namespace content { | |
| 16 namespace { | |
| 17 | |
| 18 void NullTask() { | |
| 19 } | |
| 20 | |
| 21 TEST(DiscardableSharedMemoryHeapTest, Basic) { | |
| 22 size_t block_size = base::GetPageSize(); | |
| 23 DiscardableSharedMemoryHeap heap(block_size); | |
| 24 | |
| 25 // Initial size should be 0. | |
| 26 EXPECT_EQ(0u, heap.GetSize()); | |
| 27 | |
| 28 // Initial size of free lists should be 0. | |
| 29 EXPECT_EQ(0u, heap.GetSizeOfFreeLists()); | |
| 30 | |
| 31 // Free lists are initially empty. | |
| 32 EXPECT_FALSE(heap.SearchFreeLists(1, 0)); | |
| 33 | |
| 34 const size_t kBlocks = 10; | |
| 35 size_t memory_size = block_size * kBlocks; | |
| 36 int next_discardable_shared_memory_id = 0; | |
| 37 | |
| 38 std::unique_ptr<base::DiscardableSharedMemory> memory( | |
| 39 new base::DiscardableSharedMemory); | |
| 40 ASSERT_TRUE(memory->CreateAndMap(memory_size)); | |
| 41 | |
| 42 // Create new span for memory. | |
| 43 std::unique_ptr<DiscardableSharedMemoryHeap::Span> new_span( | |
| 44 heap.Grow(std::move(memory), memory_size, | |
| 45 next_discardable_shared_memory_id++, base::Bind(NullTask))); | |
| 46 | |
| 47 // Size should match |memory_size|. | |
| 48 EXPECT_EQ(memory_size, heap.GetSize()); | |
| 49 | |
| 50 // Size of free lists should still be 0. | |
| 51 EXPECT_EQ(0u, heap.GetSizeOfFreeLists()); | |
| 52 | |
| 53 // Free list should still be empty as |new_span| is currently in use. | |
| 54 EXPECT_FALSE(heap.SearchFreeLists(1, 0)); | |
| 55 | |
| 56 // Done using |new_span|. Merge it into the free lists. | |
| 57 heap.MergeIntoFreeLists(std::move(new_span)); | |
| 58 | |
| 59 // Size of free lists should now match |memory_size|. | |
| 60 EXPECT_EQ(memory_size, heap.GetSizeOfFreeLists()); | |
| 61 | |
| 62 // Free lists should not contain a span that is larger than kBlocks. | |
| 63 EXPECT_FALSE(heap.SearchFreeLists(kBlocks + 1, 0)); | |
| 64 | |
| 65 // Free lists should contain a span that satisfies the request for kBlocks. | |
| 66 std::unique_ptr<DiscardableSharedMemoryHeap::Span> span = | |
| 67 heap.SearchFreeLists(kBlocks, 0); | |
| 68 ASSERT_TRUE(span); | |
| 69 | |
| 70 // Free lists should be empty again. | |
| 71 EXPECT_FALSE(heap.SearchFreeLists(1, 0)); | |
| 72 | |
| 73 // Merge it into the free lists again. | |
| 74 heap.MergeIntoFreeLists(std::move(span)); | |
| 75 } | |
| 76 | |
| 77 TEST(DiscardableSharedMemoryHeapTest, SplitAndMerge) { | |
| 78 size_t block_size = base::GetPageSize(); | |
| 79 DiscardableSharedMemoryHeap heap(block_size); | |
| 80 | |
| 81 const size_t kBlocks = 6; | |
| 82 size_t memory_size = block_size * kBlocks; | |
| 83 int next_discardable_shared_memory_id = 0; | |
| 84 | |
| 85 std::unique_ptr<base::DiscardableSharedMemory> memory( | |
| 86 new base::DiscardableSharedMemory); | |
| 87 ASSERT_TRUE(memory->CreateAndMap(memory_size)); | |
| 88 std::unique_ptr<DiscardableSharedMemoryHeap::Span> new_span( | |
| 89 heap.Grow(std::move(memory), memory_size, | |
| 90 next_discardable_shared_memory_id++, base::Bind(NullTask))); | |
| 91 | |
| 92 // Split span into two. | |
| 93 std::unique_ptr<DiscardableSharedMemoryHeap::Span> leftover = | |
| 94 heap.Split(new_span.get(), 3); | |
| 95 ASSERT_TRUE(leftover); | |
| 96 | |
| 97 // Merge |leftover| into free lists. | |
| 98 heap.MergeIntoFreeLists(std::move(leftover)); | |
| 99 | |
| 100 // Some of the memory is still in use. | |
| 101 EXPECT_FALSE(heap.SearchFreeLists(kBlocks, 0)); | |
| 102 | |
| 103 // Merge |span| into free lists. | |
| 104 heap.MergeIntoFreeLists(std::move(new_span)); | |
| 105 | |
| 106 // Remove a 2 page span from free lists. | |
| 107 std::unique_ptr<DiscardableSharedMemoryHeap::Span> span1 = | |
| 108 heap.SearchFreeLists(2, kBlocks); | |
| 109 ASSERT_TRUE(span1); | |
| 110 | |
| 111 // Remove another 2 page span from free lists. | |
| 112 std::unique_ptr<DiscardableSharedMemoryHeap::Span> span2 = | |
| 113 heap.SearchFreeLists(2, kBlocks); | |
| 114 ASSERT_TRUE(span2); | |
| 115 | |
| 116 // Merge |span1| back into free lists. | |
| 117 heap.MergeIntoFreeLists(std::move(span1)); | |
| 118 | |
| 119 // Some of the memory is still in use. | |
| 120 EXPECT_FALSE(heap.SearchFreeLists(kBlocks, 0)); | |
| 121 | |
| 122 // Merge |span2| back into free lists. | |
| 123 heap.MergeIntoFreeLists(std::move(span2)); | |
| 124 | |
| 125 // All memory has been returned to the free lists. | |
| 126 std::unique_ptr<DiscardableSharedMemoryHeap::Span> large_span = | |
| 127 heap.SearchFreeLists(kBlocks, 0); | |
| 128 ASSERT_TRUE(large_span); | |
| 129 | |
| 130 // Merge it into the free lists again. | |
| 131 heap.MergeIntoFreeLists(std::move(large_span)); | |
| 132 } | |
| 133 | |
| 134 TEST(DiscardableSharedMemoryHeapTest, MergeSingleBlockSpan) { | |
| 135 size_t block_size = base::GetPageSize(); | |
| 136 DiscardableSharedMemoryHeap heap(block_size); | |
| 137 | |
| 138 const size_t kBlocks = 6; | |
| 139 size_t memory_size = block_size * kBlocks; | |
| 140 int next_discardable_shared_memory_id = 0; | |
| 141 | |
| 142 std::unique_ptr<base::DiscardableSharedMemory> memory( | |
| 143 new base::DiscardableSharedMemory); | |
| 144 ASSERT_TRUE(memory->CreateAndMap(memory_size)); | |
| 145 std::unique_ptr<DiscardableSharedMemoryHeap::Span> new_span( | |
| 146 heap.Grow(std::move(memory), memory_size, | |
| 147 next_discardable_shared_memory_id++, base::Bind(NullTask))); | |
| 148 | |
| 149 // Split span into two. | |
| 150 std::unique_ptr<DiscardableSharedMemoryHeap::Span> leftover = | |
| 151 heap.Split(new_span.get(), 5); | |
| 152 ASSERT_TRUE(leftover); | |
| 153 | |
| 154 // Merge |new_span| into free lists. | |
| 155 heap.MergeIntoFreeLists(std::move(new_span)); | |
| 156 | |
| 157 // Merge |leftover| into free lists. | |
| 158 heap.MergeIntoFreeLists(std::move(leftover)); | |
| 159 } | |
| 160 | |
| 161 TEST(DiscardableSharedMemoryHeapTest, Grow) { | |
| 162 size_t block_size = base::GetPageSize(); | |
| 163 DiscardableSharedMemoryHeap heap(block_size); | |
| 164 int next_discardable_shared_memory_id = 0; | |
| 165 | |
| 166 std::unique_ptr<base::DiscardableSharedMemory> memory1( | |
| 167 new base::DiscardableSharedMemory); | |
| 168 ASSERT_TRUE(memory1->CreateAndMap(block_size)); | |
| 169 heap.MergeIntoFreeLists(heap.Grow(std::move(memory1), block_size, | |
| 170 next_discardable_shared_memory_id++, | |
| 171 base::Bind(NullTask))); | |
| 172 | |
| 173 // Remove a span from free lists. | |
| 174 std::unique_ptr<DiscardableSharedMemoryHeap::Span> span1 = | |
| 175 heap.SearchFreeLists(1, 0); | |
| 176 EXPECT_TRUE(span1); | |
| 177 | |
| 178 // No more memory available. | |
| 179 EXPECT_FALSE(heap.SearchFreeLists(1, 0)); | |
| 180 | |
| 181 // Grow free lists using new memory. | |
| 182 std::unique_ptr<base::DiscardableSharedMemory> memory2( | |
| 183 new base::DiscardableSharedMemory); | |
| 184 ASSERT_TRUE(memory2->CreateAndMap(block_size)); | |
| 185 heap.MergeIntoFreeLists(heap.Grow(std::move(memory2), block_size, | |
| 186 next_discardable_shared_memory_id++, | |
| 187 base::Bind(NullTask))); | |
| 188 | |
| 189 // Memory should now be available. | |
| 190 std::unique_ptr<DiscardableSharedMemoryHeap::Span> span2 = | |
| 191 heap.SearchFreeLists(1, 0); | |
| 192 EXPECT_TRUE(span2); | |
| 193 | |
| 194 // Merge spans into the free lists again. | |
| 195 heap.MergeIntoFreeLists(std::move(span1)); | |
| 196 heap.MergeIntoFreeLists(std::move(span2)); | |
| 197 } | |
| 198 | |
| 199 TEST(DiscardableSharedMemoryHeapTest, ReleaseFreeMemory) { | |
| 200 size_t block_size = base::GetPageSize(); | |
| 201 DiscardableSharedMemoryHeap heap(block_size); | |
| 202 int next_discardable_shared_memory_id = 0; | |
| 203 | |
| 204 std::unique_ptr<base::DiscardableSharedMemory> memory( | |
| 205 new base::DiscardableSharedMemory); | |
| 206 ASSERT_TRUE(memory->CreateAndMap(block_size)); | |
| 207 std::unique_ptr<DiscardableSharedMemoryHeap::Span> span = | |
| 208 heap.Grow(std::move(memory), block_size, | |
| 209 next_discardable_shared_memory_id++, base::Bind(NullTask)); | |
| 210 | |
| 211 // Free lists should be empty. | |
| 212 EXPECT_EQ(0u, heap.GetSizeOfFreeLists()); | |
| 213 | |
| 214 heap.ReleaseFreeMemory(); | |
| 215 | |
| 216 // Size should still match |block_size|. | |
| 217 EXPECT_EQ(block_size, heap.GetSize()); | |
| 218 | |
| 219 heap.MergeIntoFreeLists(std::move(span)); | |
| 220 heap.ReleaseFreeMemory(); | |
| 221 | |
| 222 // Memory should have been released. | |
| 223 EXPECT_EQ(0u, heap.GetSize()); | |
| 224 EXPECT_EQ(0u, heap.GetSizeOfFreeLists()); | |
| 225 } | |
| 226 | |
| 227 TEST(DiscardableSharedMemoryHeapTest, ReleasePurgedMemory) { | |
| 228 size_t block_size = base::GetPageSize(); | |
| 229 DiscardableSharedMemoryHeap heap(block_size); | |
| 230 int next_discardable_shared_memory_id = 0; | |
| 231 | |
| 232 std::unique_ptr<base::DiscardableSharedMemory> memory( | |
| 233 new base::DiscardableSharedMemory); | |
| 234 ASSERT_TRUE(memory->CreateAndMap(block_size)); | |
| 235 std::unique_ptr<DiscardableSharedMemoryHeap::Span> span = | |
| 236 heap.Grow(std::move(memory), block_size, | |
| 237 next_discardable_shared_memory_id++, base::Bind(NullTask)); | |
| 238 | |
| 239 // Unlock memory so it can be purged. | |
| 240 span->shared_memory()->Unlock(0, 0); | |
| 241 | |
| 242 // Purge and release shared memory. | |
| 243 bool rv = span->shared_memory()->Purge(base::Time::Now()); | |
| 244 EXPECT_TRUE(rv); | |
| 245 heap.ReleasePurgedMemory(); | |
| 246 | |
| 247 // Shared memory backing for |span| should be gone. | |
| 248 EXPECT_FALSE(span->shared_memory()); | |
| 249 | |
| 250 // Size should be 0. | |
| 251 EXPECT_EQ(0u, heap.GetSize()); | |
| 252 } | |
| 253 | |
| 254 TEST(DiscardableSharedMemoryHeapTest, Slack) { | |
| 255 size_t block_size = base::GetPageSize(); | |
| 256 DiscardableSharedMemoryHeap heap(block_size); | |
| 257 | |
| 258 const size_t kBlocks = 6; | |
| 259 size_t memory_size = block_size * kBlocks; | |
| 260 int next_discardable_shared_memory_id = 0; | |
| 261 | |
| 262 std::unique_ptr<base::DiscardableSharedMemory> memory( | |
| 263 new base::DiscardableSharedMemory); | |
| 264 ASSERT_TRUE(memory->CreateAndMap(memory_size)); | |
| 265 heap.MergeIntoFreeLists(heap.Grow(std::move(memory), memory_size, | |
| 266 next_discardable_shared_memory_id++, | |
| 267 base::Bind(NullTask))); | |
| 268 | |
| 269 // No free span that is less or equal to 3 + 1. | |
| 270 EXPECT_FALSE(heap.SearchFreeLists(3, 1)); | |
| 271 | |
| 272 // No free span that is less or equal to 3 + 2. | |
| 273 EXPECT_FALSE(heap.SearchFreeLists(3, 2)); | |
| 274 | |
| 275 // No free span that is less or equal to 1 + 4. | |
| 276 EXPECT_FALSE(heap.SearchFreeLists(1, 4)); | |
| 277 | |
| 278 std::unique_ptr<DiscardableSharedMemoryHeap::Span> span = | |
| 279 heap.SearchFreeLists(1, 5); | |
| 280 EXPECT_TRUE(span); | |
| 281 | |
| 282 heap.MergeIntoFreeLists(std::move(span)); | |
| 283 } | |
| 284 | |
| 285 void OnDeleted(bool* deleted) { | |
| 286 *deleted = true; | |
| 287 } | |
| 288 | |
| 289 TEST(DiscardableSharedMemoryHeapTest, DeletedCallback) { | |
| 290 size_t block_size = base::GetPageSize(); | |
| 291 DiscardableSharedMemoryHeap heap(block_size); | |
| 292 int next_discardable_shared_memory_id = 0; | |
| 293 | |
| 294 std::unique_ptr<base::DiscardableSharedMemory> memory( | |
| 295 new base::DiscardableSharedMemory); | |
| 296 ASSERT_TRUE(memory->CreateAndMap(block_size)); | |
| 297 bool deleted = false; | |
| 298 std::unique_ptr<DiscardableSharedMemoryHeap::Span> span = heap.Grow( | |
| 299 std::move(memory), block_size, next_discardable_shared_memory_id++, | |
| 300 base::Bind(OnDeleted, base::Unretained(&deleted))); | |
| 301 | |
| 302 heap.MergeIntoFreeLists(std::move(span)); | |
| 303 heap.ReleaseFreeMemory(); | |
| 304 | |
| 305 EXPECT_TRUE(deleted); | |
| 306 } | |
| 307 | |
| 308 TEST(DiscardableSharedMemoryHeapTest, CreateMemoryAllocatorDumpTest) { | |
| 309 size_t block_size = base::GetPageSize(); | |
| 310 DiscardableSharedMemoryHeap heap(block_size); | |
| 311 int next_discardable_shared_memory_id = 0; | |
| 312 | |
| 313 std::unique_ptr<base::DiscardableSharedMemory> memory( | |
| 314 new base::DiscardableSharedMemory); | |
| 315 ASSERT_TRUE(memory->CreateAndMap(block_size)); | |
| 316 std::unique_ptr<DiscardableSharedMemoryHeap::Span> span = | |
| 317 heap.Grow(std::move(memory), block_size, | |
| 318 next_discardable_shared_memory_id++, base::Bind(NullTask)); | |
| 319 | |
| 320 // Check if allocator dump is created when span exists. | |
| 321 std::unique_ptr<base::trace_event::ProcessMemoryDump> pmd( | |
| 322 new base::trace_event::ProcessMemoryDump( | |
| 323 nullptr, {base::trace_event::MemoryDumpLevelOfDetail::DETAILED})); | |
| 324 EXPECT_TRUE(heap.CreateMemoryAllocatorDump(span.get(), "discardable/test1", | |
| 325 pmd.get())); | |
| 326 | |
| 327 // Unlock, Purge and release shared memory. | |
| 328 span->shared_memory()->Unlock(0, 0); | |
| 329 bool rv = span->shared_memory()->Purge(base::Time::Now()); | |
| 330 EXPECT_TRUE(rv); | |
| 331 heap.ReleasePurgedMemory(); | |
| 332 | |
| 333 // Check that allocator dump is created after memory is purged. | |
| 334 EXPECT_TRUE(heap.CreateMemoryAllocatorDump(span.get(), "discardable/test2", | |
| 335 pmd.get())); | |
| 336 } | |
| 337 | |
| 338 } // namespace | |
| 339 } // namespace content | |
| OLD | NEW |