Add remaining Linux configs for MB.
[chromium-blink-merge.git] / content / child / child_discardable_shared_memory_manager.cc
blobff8fe4357881edab5e704846945280c75da294a7
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.
5 #include "content/child/child_discardable_shared_memory_manager.h"
7 #include "base/atomic_sequence_num.h"
8 #include "base/bind.h"
9 #include "base/debug/crash_logging.h"
10 #include "base/memory/discardable_memory.h"
11 #include "base/memory/discardable_shared_memory.h"
12 #include "base/metrics/histogram.h"
13 #include "base/process/memory.h"
14 #include "base/process/process_metrics.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "base/trace_event/memory_dump_manager.h"
18 #include "base/trace_event/trace_event.h"
19 #include "content/common/child_process_messages.h"
21 namespace content {
22 namespace {
24 // Default allocation size.
25 const size_t kAllocationSize = 4 * 1024 * 1024;
27 // Global atomic to generate unique discardable shared memory IDs.
28 base::StaticAtomicSequenceNumber g_next_discardable_shared_memory_id;
30 class DiscardableMemoryImpl : public base::DiscardableMemory {
31 public:
32 DiscardableMemoryImpl(ChildDiscardableSharedMemoryManager* manager,
33 scoped_ptr<DiscardableSharedMemoryHeap::Span> span)
34 : manager_(manager), span_(span.Pass()), is_locked_(true) {}
36 ~DiscardableMemoryImpl() override {
37 if (is_locked_)
38 manager_->UnlockSpan(span_.get());
40 manager_->ReleaseSpan(span_.Pass());
43 // Overridden from base::DiscardableMemory:
44 bool Lock() override {
45 DCHECK(!is_locked_);
47 if (!manager_->LockSpan(span_.get()))
48 return false;
50 is_locked_ = true;
51 return true;
53 void Unlock() override {
54 DCHECK(is_locked_);
56 manager_->UnlockSpan(span_.get());
57 is_locked_ = false;
59 void* data() const override {
60 DCHECK(is_locked_);
61 return reinterpret_cast<void*>(span_->start() * base::GetPageSize());
64 private:
65 ChildDiscardableSharedMemoryManager* const manager_;
66 scoped_ptr<DiscardableSharedMemoryHeap::Span> span_;
67 bool is_locked_;
69 DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryImpl);
72 void SendDeletedDiscardableSharedMemoryMessage(
73 scoped_refptr<ThreadSafeSender> sender,
74 DiscardableSharedMemoryId id) {
75 sender->Send(new ChildProcessHostMsg_DeletedDiscardableSharedMemory(id));
78 } // namespace
80 ChildDiscardableSharedMemoryManager::ChildDiscardableSharedMemoryManager(
81 ThreadSafeSender* sender)
82 : heap_(base::GetPageSize()), sender_(sender) {
83 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
84 this, base::ThreadTaskRunnerHandle::Get());
87 ChildDiscardableSharedMemoryManager::~ChildDiscardableSharedMemoryManager() {
88 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
89 this);
90 // TODO(reveman): Determine if this DCHECK can be enabled. crbug.com/430533
91 // DCHECK_EQ(heap_.GetSize(), heap_.GetSizeOfFreeLists());
92 if (heap_.GetSize())
93 MemoryUsageChanged(0, 0);
96 scoped_ptr<base::DiscardableMemory>
97 ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
98 size_t size) {
99 base::AutoLock lock(lock_);
101 DCHECK_NE(size, 0u);
103 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.DiscardableAllocationSize",
104 size / 1024, // In KB
106 4 * 1024 * 1024, // 4 GB
107 50);
109 // Round up to multiple of page size.
110 size_t pages = (size + base::GetPageSize() - 1) / base::GetPageSize();
112 // Default allocation size in pages.
113 size_t allocation_pages = kAllocationSize / base::GetPageSize();
115 size_t slack = 0;
116 // When searching the free lists, allow a slack between required size and
117 // free span size that is less or equal to kAllocationSize. This is to
118 // avoid segments larger then kAllocationSize unless they are a perfect
119 // fit. The result is that large allocations can be reused without reducing
120 // the ability to discard memory.
121 if (pages < allocation_pages)
122 slack = allocation_pages - pages;
124 size_t heap_size_prior_to_releasing_purged_memory = heap_.GetSize();
125 for (;;) {
126 // Search free lists for suitable span.
127 scoped_ptr<DiscardableSharedMemoryHeap::Span> free_span =
128 heap_.SearchFreeLists(pages, slack);
129 if (!free_span.get())
130 break;
132 // Attempt to lock |free_span|. Delete span and search free lists again
133 // if locking failed.
134 if (free_span->shared_memory()->Lock(
135 free_span->start() * base::GetPageSize() -
136 reinterpret_cast<size_t>(free_span->shared_memory()->memory()),
137 free_span->length() * base::GetPageSize()) ==
138 base::DiscardableSharedMemory::FAILED) {
139 DCHECK(!free_span->shared_memory()->IsMemoryResident());
140 // We have to release purged memory before |free_span| can be destroyed.
141 heap_.ReleasePurgedMemory();
142 DCHECK(!free_span->shared_memory());
143 continue;
146 // Memory usage is guaranteed to have changed after having removed
147 // at least one span from the free lists.
148 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
150 return make_scoped_ptr(new DiscardableMemoryImpl(this, free_span.Pass()));
153 // Release purged memory to free up the address space before we attempt to
154 // allocate more memory.
155 heap_.ReleasePurgedMemory();
157 // Make sure crash keys are up to date in case allocation fails.
158 if (heap_.GetSize() != heap_size_prior_to_releasing_purged_memory)
159 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
161 size_t pages_to_allocate =
162 std::max(kAllocationSize / base::GetPageSize(), pages);
163 size_t allocation_size_in_bytes = pages_to_allocate * base::GetPageSize();
165 DiscardableSharedMemoryId new_id =
166 g_next_discardable_shared_memory_id.GetNext();
168 // Ask parent process to allocate a new discardable shared memory segment.
169 scoped_ptr<base::DiscardableSharedMemory> shared_memory(
170 AllocateLockedDiscardableSharedMemory(allocation_size_in_bytes, new_id));
172 // Create span for allocated memory.
173 scoped_ptr<DiscardableSharedMemoryHeap::Span> new_span(heap_.Grow(
174 shared_memory.Pass(), allocation_size_in_bytes, new_id,
175 base::Bind(&SendDeletedDiscardableSharedMemoryMessage, sender_, new_id)));
177 // Unlock and insert any left over memory into free lists.
178 if (pages < pages_to_allocate) {
179 scoped_ptr<DiscardableSharedMemoryHeap::Span> leftover =
180 heap_.Split(new_span.get(), pages);
181 leftover->shared_memory()->Unlock(
182 leftover->start() * base::GetPageSize() -
183 reinterpret_cast<size_t>(leftover->shared_memory()->memory()),
184 leftover->length() * base::GetPageSize());
185 heap_.MergeIntoFreeLists(leftover.Pass());
188 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
190 return make_scoped_ptr(new DiscardableMemoryImpl(this, new_span.Pass()));
193 bool ChildDiscardableSharedMemoryManager::OnMemoryDump(
194 const base::trace_event::MemoryDumpArgs& args,
195 base::trace_event::ProcessMemoryDump* pmd) {
196 base::AutoLock lock(lock_);
197 return heap_.OnMemoryDump(pmd);
200 void ChildDiscardableSharedMemoryManager::ReleaseFreeMemory() {
201 base::AutoLock lock(lock_);
203 size_t heap_size_prior_to_releasing_memory = heap_.GetSize();
205 // Release both purged and free memory.
206 heap_.ReleasePurgedMemory();
207 heap_.ReleaseFreeMemory();
209 if (heap_.GetSize() != heap_size_prior_to_releasing_memory)
210 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
213 bool ChildDiscardableSharedMemoryManager::LockSpan(
214 DiscardableSharedMemoryHeap::Span* span) {
215 base::AutoLock lock(lock_);
217 if (!span->shared_memory())
218 return false;
220 size_t offset = span->start() * base::GetPageSize() -
221 reinterpret_cast<size_t>(span->shared_memory()->memory());
222 size_t length = span->length() * base::GetPageSize();
224 switch (span->shared_memory()->Lock(offset, length)) {
225 case base::DiscardableSharedMemory::SUCCESS:
226 return true;
227 case base::DiscardableSharedMemory::PURGED:
228 span->shared_memory()->Unlock(offset, length);
229 return false;
230 case base::DiscardableSharedMemory::FAILED:
231 return false;
234 NOTREACHED();
235 return false;
238 void ChildDiscardableSharedMemoryManager::UnlockSpan(
239 DiscardableSharedMemoryHeap::Span* span) {
240 base::AutoLock lock(lock_);
242 DCHECK(span->shared_memory());
243 size_t offset = span->start() * base::GetPageSize() -
244 reinterpret_cast<size_t>(span->shared_memory()->memory());
245 size_t length = span->length() * base::GetPageSize();
247 return span->shared_memory()->Unlock(offset, length);
250 void ChildDiscardableSharedMemoryManager::ReleaseSpan(
251 scoped_ptr<DiscardableSharedMemoryHeap::Span> span) {
252 base::AutoLock lock(lock_);
254 // Delete span instead of merging it into free lists if memory is gone.
255 if (!span->shared_memory())
256 return;
258 heap_.MergeIntoFreeLists(span.Pass());
260 // Bytes of free memory changed.
261 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
264 scoped_ptr<base::DiscardableSharedMemory>
265 ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
266 size_t size,
267 DiscardableSharedMemoryId id) {
268 TRACE_EVENT2("renderer",
269 "ChildDiscardableSharedMemoryManager::"
270 "AllocateLockedDiscardableSharedMemory",
271 "size", size, "id", id);
273 base::SharedMemoryHandle handle = base::SharedMemory::NULLHandle();
274 sender_->Send(
275 new ChildProcessHostMsg_SyncAllocateLockedDiscardableSharedMemory(
276 size, id, &handle));
277 scoped_ptr<base::DiscardableSharedMemory> memory(
278 new base::DiscardableSharedMemory(handle));
279 if (!memory->Map(size))
280 base::TerminateBecauseOutOfMemory(size);
281 return memory.Pass();
284 void ChildDiscardableSharedMemoryManager::MemoryUsageChanged(
285 size_t new_bytes_total,
286 size_t new_bytes_free) const {
287 TRACE_COUNTER2("renderer", "DiscardableMemoryUsage", "allocated",
288 new_bytes_total - new_bytes_free, "free", new_bytes_free);
290 static const char kDiscardableMemoryAllocatedKey[] =
291 "discardable-memory-allocated";
292 base::debug::SetCrashKeyValue(kDiscardableMemoryAllocatedKey,
293 base::Uint64ToString(new_bytes_total));
295 static const char kDiscardableMemoryFreeKey[] = "discardable-memory-free";
296 base::debug::SetCrashKeyValue(kDiscardableMemoryFreeKey,
297 base::Uint64ToString(new_bytes_free));
300 } // namespace content