Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(60)

Side by Side Diff: content/child/child_discardable_shared_memory_manager.cc

Issue 2459733002: Move discardable memory to //components from //content (Closed)
Patch Set: Fix build error Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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/child/child_discardable_shared_memory_manager.h"
6
7 #include <inttypes.h>
8
9 #include <algorithm>
10 #include <utility>
11
12 #include "base/atomic_sequence_num.h"
13 #include "base/bind.h"
14 #include "base/debug/crash_logging.h"
15 #include "base/macros.h"
16 #include "base/memory/discardable_memory.h"
17 #include "base/memory/discardable_shared_memory.h"
18 #include "base/memory/ptr_util.h"
19 #include "base/metrics/histogram_macros.h"
20 #include "base/process/memory.h"
21 #include "base/process/process_metrics.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/threading/thread_task_runner_handle.h"
25 #include "base/trace_event/memory_dump_manager.h"
26 #include "base/trace_event/trace_event.h"
27 #include "content/common/child_process_messages.h"
28
29 namespace content {
30 namespace {
31
32 // Default allocation size.
33 const size_t kAllocationSize = 4 * 1024 * 1024;
34
35 // Global atomic to generate unique discardable shared memory IDs.
36 base::StaticAtomicSequenceNumber g_next_discardable_shared_memory_id;
37
38 class DiscardableMemoryImpl : public base::DiscardableMemory {
39 public:
40 DiscardableMemoryImpl(ChildDiscardableSharedMemoryManager* manager,
41 std::unique_ptr<DiscardableSharedMemoryHeap::Span> span)
42 : manager_(manager), span_(std::move(span)), is_locked_(true) {}
43
44 ~DiscardableMemoryImpl() override {
45 if (is_locked_)
46 manager_->UnlockSpan(span_.get());
47
48 manager_->ReleaseSpan(std::move(span_));
49 }
50
51 // Overridden from base::DiscardableMemory:
52 bool Lock() override {
53 DCHECK(!is_locked_);
54
55 if (!manager_->LockSpan(span_.get()))
56 return false;
57
58 is_locked_ = true;
59 return true;
60 }
61 void Unlock() override {
62 DCHECK(is_locked_);
63
64 manager_->UnlockSpan(span_.get());
65 is_locked_ = false;
66 }
67 void* data() const override {
68 DCHECK(is_locked_);
69 return reinterpret_cast<void*>(span_->start() * base::GetPageSize());
70 }
71
72 base::trace_event::MemoryAllocatorDump* CreateMemoryAllocatorDump(
73 const char* name,
74 base::trace_event::ProcessMemoryDump* pmd) const override {
75 return manager_->CreateMemoryAllocatorDump(span_.get(), name, pmd);
76 }
77
78 private:
79 ChildDiscardableSharedMemoryManager* const manager_;
80 std::unique_ptr<DiscardableSharedMemoryHeap::Span> span_;
81 bool is_locked_;
82
83 DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryImpl);
84 };
85
86 void SendDeletedDiscardableSharedMemoryMessage(
87 scoped_refptr<ThreadSafeSender> sender,
88 DiscardableSharedMemoryId id) {
89 sender->Send(new ChildProcessHostMsg_DeletedDiscardableSharedMemory(id));
90 }
91
92 } // namespace
93
94 ChildDiscardableSharedMemoryManager::ChildDiscardableSharedMemoryManager(
95 ThreadSafeSender* sender)
96 : heap_(base::GetPageSize()), sender_(sender) {
97 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
98 this, "ChildDiscardableSharedMemoryManager",
99 base::ThreadTaskRunnerHandle::Get());
100 }
101
102 ChildDiscardableSharedMemoryManager::~ChildDiscardableSharedMemoryManager() {
103 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
104 this);
105 // TODO(reveman): Determine if this DCHECK can be enabled. crbug.com/430533
106 // DCHECK_EQ(heap_.GetSize(), heap_.GetSizeOfFreeLists());
107 if (heap_.GetSize())
108 MemoryUsageChanged(0, 0);
109 }
110
111 std::unique_ptr<base::DiscardableMemory>
112 ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
113 size_t size) {
114 base::AutoLock lock(lock_);
115
116 DCHECK_NE(size, 0u);
117
118 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.DiscardableAllocationSize",
119 size / 1024, // In KB
120 1,
121 4 * 1024 * 1024, // 4 GB
122 50);
123
124 // Round up to multiple of page size.
125 size_t pages =
126 std::max((size + base::GetPageSize() - 1) / base::GetPageSize(),
127 static_cast<size_t>(1));
128
129 // Default allocation size in pages.
130 size_t allocation_pages = kAllocationSize / base::GetPageSize();
131
132 size_t slack = 0;
133 // When searching the free lists, allow a slack between required size and
134 // free span size that is less or equal to kAllocationSize. This is to
135 // avoid segments larger then kAllocationSize unless they are a perfect
136 // fit. The result is that large allocations can be reused without reducing
137 // the ability to discard memory.
138 if (pages < allocation_pages)
139 slack = allocation_pages - pages;
140
141 size_t heap_size_prior_to_releasing_purged_memory = heap_.GetSize();
142 for (;;) {
143 // Search free lists for suitable span.
144 std::unique_ptr<DiscardableSharedMemoryHeap::Span> free_span =
145 heap_.SearchFreeLists(pages, slack);
146 if (!free_span.get())
147 break;
148
149 // Attempt to lock |free_span|. Delete span and search free lists again
150 // if locking failed.
151 if (free_span->shared_memory()->Lock(
152 free_span->start() * base::GetPageSize() -
153 reinterpret_cast<size_t>(free_span->shared_memory()->memory()),
154 free_span->length() * base::GetPageSize()) ==
155 base::DiscardableSharedMemory::FAILED) {
156 DCHECK(!free_span->shared_memory()->IsMemoryResident());
157 // We have to release purged memory before |free_span| can be destroyed.
158 heap_.ReleasePurgedMemory();
159 DCHECK(!free_span->shared_memory());
160 continue;
161 }
162
163 free_span->set_is_locked(true);
164
165 // Memory usage is guaranteed to have changed after having removed
166 // at least one span from the free lists.
167 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
168
169 return base::MakeUnique<DiscardableMemoryImpl>(this, std::move(free_span));
170 }
171
172 // Release purged memory to free up the address space before we attempt to
173 // allocate more memory.
174 heap_.ReleasePurgedMemory();
175
176 // Make sure crash keys are up to date in case allocation fails.
177 if (heap_.GetSize() != heap_size_prior_to_releasing_purged_memory)
178 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
179
180 size_t pages_to_allocate =
181 std::max(kAllocationSize / base::GetPageSize(), pages);
182 size_t allocation_size_in_bytes = pages_to_allocate * base::GetPageSize();
183
184 DiscardableSharedMemoryId new_id =
185 g_next_discardable_shared_memory_id.GetNext();
186
187 // Ask parent process to allocate a new discardable shared memory segment.
188 std::unique_ptr<base::DiscardableSharedMemory> shared_memory(
189 AllocateLockedDiscardableSharedMemory(allocation_size_in_bytes, new_id));
190
191 // Create span for allocated memory.
192 std::unique_ptr<DiscardableSharedMemoryHeap::Span> new_span(heap_.Grow(
193 std::move(shared_memory), allocation_size_in_bytes, new_id,
194 base::Bind(&SendDeletedDiscardableSharedMemoryMessage, sender_, new_id)));
195 new_span->set_is_locked(true);
196
197 // Unlock and insert any left over memory into free lists.
198 if (pages < pages_to_allocate) {
199 std::unique_ptr<DiscardableSharedMemoryHeap::Span> leftover =
200 heap_.Split(new_span.get(), pages);
201 leftover->shared_memory()->Unlock(
202 leftover->start() * base::GetPageSize() -
203 reinterpret_cast<size_t>(leftover->shared_memory()->memory()),
204 leftover->length() * base::GetPageSize());
205 leftover->set_is_locked(false);
206 heap_.MergeIntoFreeLists(std::move(leftover));
207 }
208
209 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
210
211 return base::MakeUnique<DiscardableMemoryImpl>(this, std::move(new_span));
212 }
213
214 bool ChildDiscardableSharedMemoryManager::OnMemoryDump(
215 const base::trace_event::MemoryDumpArgs& args,
216 base::trace_event::ProcessMemoryDump* pmd) {
217 base::AutoLock lock(lock_);
218 if (args.level_of_detail ==
219 base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND) {
220 base::trace_event::MemoryAllocatorDump* total_dump =
221 pmd->CreateAllocatorDump(
222 base::StringPrintf("discardable/child_0x%" PRIXPTR,
223 reinterpret_cast<uintptr_t>(this)));
224 const size_t total_size = heap_.GetSize();
225 const size_t freelist_size = heap_.GetSizeOfFreeLists();
226 total_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
227 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
228 total_size - freelist_size);
229 total_dump->AddScalar("freelist_size",
230 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
231 freelist_size);
232 return true;
233 }
234
235 return heap_.OnMemoryDump(pmd);
236 }
237
238 ChildDiscardableSharedMemoryManager::Statistics
239 ChildDiscardableSharedMemoryManager::GetStatistics() const {
240 base::AutoLock lock(lock_);
241 Statistics stats;
242 stats.total_size = heap_.GetSize();
243 stats.freelist_size = heap_.GetSizeOfFreeLists();
244 return stats;
245 }
246
247 void ChildDiscardableSharedMemoryManager::ReleaseFreeMemory() {
248 base::AutoLock lock(lock_);
249
250 size_t heap_size_prior_to_releasing_memory = heap_.GetSize();
251
252 // Release both purged and free memory.
253 heap_.ReleasePurgedMemory();
254 heap_.ReleaseFreeMemory();
255
256 if (heap_.GetSize() != heap_size_prior_to_releasing_memory)
257 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
258 }
259
260 bool ChildDiscardableSharedMemoryManager::LockSpan(
261 DiscardableSharedMemoryHeap::Span* span) {
262 base::AutoLock lock(lock_);
263
264 if (!span->shared_memory())
265 return false;
266
267 size_t offset = span->start() * base::GetPageSize() -
268 reinterpret_cast<size_t>(span->shared_memory()->memory());
269 size_t length = span->length() * base::GetPageSize();
270
271 switch (span->shared_memory()->Lock(offset, length)) {
272 case base::DiscardableSharedMemory::SUCCESS:
273 span->set_is_locked(true);
274 return true;
275 case base::DiscardableSharedMemory::PURGED:
276 span->shared_memory()->Unlock(offset, length);
277 span->set_is_locked(false);
278 return false;
279 case base::DiscardableSharedMemory::FAILED:
280 return false;
281 }
282
283 NOTREACHED();
284 return false;
285 }
286
287 void ChildDiscardableSharedMemoryManager::UnlockSpan(
288 DiscardableSharedMemoryHeap::Span* span) {
289 base::AutoLock lock(lock_);
290
291 DCHECK(span->shared_memory());
292 size_t offset = span->start() * base::GetPageSize() -
293 reinterpret_cast<size_t>(span->shared_memory()->memory());
294 size_t length = span->length() * base::GetPageSize();
295
296 span->set_is_locked(false);
297 return span->shared_memory()->Unlock(offset, length);
298 }
299
300 void ChildDiscardableSharedMemoryManager::ReleaseSpan(
301 std::unique_ptr<DiscardableSharedMemoryHeap::Span> span) {
302 base::AutoLock lock(lock_);
303
304 // Delete span instead of merging it into free lists if memory is gone.
305 if (!span->shared_memory())
306 return;
307
308 heap_.MergeIntoFreeLists(std::move(span));
309
310 // Bytes of free memory changed.
311 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
312 }
313
314 base::trace_event::MemoryAllocatorDump*
315 ChildDiscardableSharedMemoryManager::CreateMemoryAllocatorDump(
316 DiscardableSharedMemoryHeap::Span* span,
317 const char* name,
318 base::trace_event::ProcessMemoryDump* pmd) const {
319 base::AutoLock lock(lock_);
320 return heap_.CreateMemoryAllocatorDump(span, name, pmd);
321 }
322
323 std::unique_ptr<base::DiscardableSharedMemory>
324 ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
325 size_t size,
326 DiscardableSharedMemoryId id) {
327 TRACE_EVENT2("renderer",
328 "ChildDiscardableSharedMemoryManager::"
329 "AllocateLockedDiscardableSharedMemory",
330 "size", size, "id", id);
331
332 base::SharedMemoryHandle handle = base::SharedMemory::NULLHandle();
333 sender_->Send(
334 new ChildProcessHostMsg_SyncAllocateLockedDiscardableSharedMemory(
335 size, id, &handle));
336 std::unique_ptr<base::DiscardableSharedMemory> memory(
337 new base::DiscardableSharedMemory(handle));
338 if (!memory->Map(size))
339 base::TerminateBecauseOutOfMemory(size);
340 return memory;
341 }
342
343 void ChildDiscardableSharedMemoryManager::MemoryUsageChanged(
344 size_t new_bytes_total,
345 size_t new_bytes_free) const {
346 static const char kDiscardableMemoryAllocatedKey[] =
347 "discardable-memory-allocated";
348 base::debug::SetCrashKeyValue(kDiscardableMemoryAllocatedKey,
349 base::Uint64ToString(new_bytes_total));
350
351 static const char kDiscardableMemoryFreeKey[] = "discardable-memory-free";
352 base::debug::SetCrashKeyValue(kDiscardableMemoryFreeKey,
353 base::Uint64ToString(new_bytes_free));
354 }
355
356 } // namespace content
OLDNEW
« no previous file with comments | « content/child/child_discardable_shared_memory_manager.h ('k') | content/child/child_thread_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698