[Do not revert] Roll-back V8 to version 4.4.63.
[chromium-blink-merge.git] / content / child / child_discardable_shared_memory_manager.cc
blob6c67be86edb8bd714a19fc575b437a79ce744eb3
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 #if defined(OS_ANDROID)
26 // Larger allocation size on Android to avoid reaching the FD-limit.
27 const size_t kAllocationSize = 32 * 1024 * 1024;
28 #else
29 const size_t kAllocationSize = 4 * 1024 * 1024;
30 #endif
32 // Global atomic to generate unique discardable shared memory IDs.
33 base::StaticAtomicSequenceNumber g_next_discardable_shared_memory_id;
35 class DiscardableMemoryImpl : public base::DiscardableMemory {
36 public:
37 DiscardableMemoryImpl(ChildDiscardableSharedMemoryManager* manager,
38 scoped_ptr<DiscardableSharedMemoryHeap::Span> span)
39 : manager_(manager), span_(span.Pass()), is_locked_(true) {}
41 ~DiscardableMemoryImpl() override {
42 if (is_locked_)
43 manager_->UnlockSpan(span_.get());
45 manager_->ReleaseSpan(span_.Pass());
48 // Overridden from base::DiscardableMemory:
49 bool Lock() override {
50 DCHECK(!is_locked_);
52 if (!manager_->LockSpan(span_.get()))
53 return false;
55 is_locked_ = true;
56 return true;
58 void Unlock() override {
59 DCHECK(is_locked_);
61 manager_->UnlockSpan(span_.get());
62 is_locked_ = false;
64 void* data() const override {
65 DCHECK(is_locked_);
66 return reinterpret_cast<void*>(span_->start() * base::GetPageSize());
69 private:
70 ChildDiscardableSharedMemoryManager* const manager_;
71 scoped_ptr<DiscardableSharedMemoryHeap::Span> span_;
72 bool is_locked_;
74 DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryImpl);
77 void SendDeletedDiscardableSharedMemoryMessage(
78 scoped_refptr<ThreadSafeSender> sender,
79 DiscardableSharedMemoryId id) {
80 sender->Send(new ChildProcessHostMsg_DeletedDiscardableSharedMemory(id));
83 } // namespace
85 ChildDiscardableSharedMemoryManager::ChildDiscardableSharedMemoryManager(
86 ThreadSafeSender* sender)
87 : heap_(base::GetPageSize()), sender_(sender) {
88 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
89 this, base::ThreadTaskRunnerHandle::Get());
92 ChildDiscardableSharedMemoryManager::~ChildDiscardableSharedMemoryManager() {
93 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
94 this);
95 // TODO(reveman): Determine if this DCHECK can be enabled. crbug.com/430533
96 // DCHECK_EQ(heap_.GetSize(), heap_.GetSizeOfFreeLists());
97 if (heap_.GetSize())
98 MemoryUsageChanged(0, 0);
101 scoped_ptr<base::DiscardableMemory>
102 ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
103 size_t size) {
104 base::AutoLock lock(lock_);
106 DCHECK_NE(size, 0u);
108 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.DiscardableAllocationSize",
109 size / 1024, // In KB
111 4 * 1024 * 1024, // 4 GB
112 50);
114 // Round up to multiple of page size.
115 size_t pages = (size + base::GetPageSize() - 1) / base::GetPageSize();
117 // Default allocation size in pages.
118 size_t allocation_pages = kAllocationSize / base::GetPageSize();
120 size_t slack = 0;
121 // When searching the free lists, allow a slack between required size and
122 // free span size that is less or equal to kAllocationSize. This is to
123 // avoid segments larger then kAllocationSize unless they are a perfect
124 // fit. The result is that large allocations can be reused without reducing
125 // the ability to discard memory.
126 if (pages < allocation_pages)
127 slack = allocation_pages - pages;
129 size_t heap_size_prior_to_releasing_purged_memory = heap_.GetSize();
130 for (;;) {
131 // Search free lists for suitable span.
132 scoped_ptr<DiscardableSharedMemoryHeap::Span> free_span =
133 heap_.SearchFreeLists(pages, slack);
134 if (!free_span.get())
135 break;
137 // Attempt to lock |free_span|. Delete span and search free lists again
138 // if locking failed.
139 if (free_span->shared_memory()->Lock(
140 free_span->start() * base::GetPageSize() -
141 reinterpret_cast<size_t>(free_span->shared_memory()->memory()),
142 free_span->length() * base::GetPageSize()) ==
143 base::DiscardableSharedMemory::FAILED) {
144 DCHECK(!free_span->shared_memory()->IsMemoryResident());
145 // We have to release purged memory before |free_span| can be destroyed.
146 heap_.ReleasePurgedMemory();
147 DCHECK(!free_span->shared_memory());
148 continue;
151 // Memory usage is guaranteed to have changed after having removed
152 // at least one span from the free lists.
153 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
155 return make_scoped_ptr(new DiscardableMemoryImpl(this, free_span.Pass()));
158 // Release purged memory to free up the address space before we attempt to
159 // allocate more memory.
160 heap_.ReleasePurgedMemory();
162 // Make sure crash keys are up to date in case allocation fails.
163 if (heap_.GetSize() != heap_size_prior_to_releasing_purged_memory)
164 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
166 size_t pages_to_allocate =
167 std::max(kAllocationSize / base::GetPageSize(), pages);
168 size_t allocation_size_in_bytes = pages_to_allocate * base::GetPageSize();
170 DiscardableSharedMemoryId new_id =
171 g_next_discardable_shared_memory_id.GetNext();
173 // Ask parent process to allocate a new discardable shared memory segment.
174 scoped_ptr<base::DiscardableSharedMemory> shared_memory(
175 AllocateLockedDiscardableSharedMemory(allocation_size_in_bytes, new_id));
177 // Create span for allocated memory.
178 scoped_ptr<DiscardableSharedMemoryHeap::Span> new_span(heap_.Grow(
179 shared_memory.Pass(), allocation_size_in_bytes, new_id,
180 base::Bind(&SendDeletedDiscardableSharedMemoryMessage, sender_, new_id)));
182 // Unlock and insert any left over memory into free lists.
183 if (pages < pages_to_allocate) {
184 scoped_ptr<DiscardableSharedMemoryHeap::Span> leftover =
185 heap_.Split(new_span.get(), pages);
186 leftover->shared_memory()->Unlock(
187 leftover->start() * base::GetPageSize() -
188 reinterpret_cast<size_t>(leftover->shared_memory()->memory()),
189 leftover->length() * base::GetPageSize());
190 heap_.MergeIntoFreeLists(leftover.Pass());
193 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
195 return make_scoped_ptr(new DiscardableMemoryImpl(this, new_span.Pass()));
198 bool ChildDiscardableSharedMemoryManager::OnMemoryDump(
199 base::trace_event::ProcessMemoryDump* pmd) {
200 return heap_.OnMemoryDump(pmd);
203 void ChildDiscardableSharedMemoryManager::ReleaseFreeMemory() {
204 base::AutoLock lock(lock_);
206 size_t heap_size_prior_to_releasing_memory = heap_.GetSize();
208 // Release both purged and free memory.
209 heap_.ReleasePurgedMemory();
210 heap_.ReleaseFreeMemory();
212 if (heap_.GetSize() != heap_size_prior_to_releasing_memory)
213 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
216 bool ChildDiscardableSharedMemoryManager::LockSpan(
217 DiscardableSharedMemoryHeap::Span* span) {
218 base::AutoLock lock(lock_);
220 if (!span->shared_memory())
221 return false;
223 size_t offset = span->start() * base::GetPageSize() -
224 reinterpret_cast<size_t>(span->shared_memory()->memory());
225 size_t length = span->length() * base::GetPageSize();
227 switch (span->shared_memory()->Lock(offset, length)) {
228 case base::DiscardableSharedMemory::SUCCESS:
229 return true;
230 case base::DiscardableSharedMemory::PURGED:
231 span->shared_memory()->Unlock(offset, length);
232 return false;
233 case base::DiscardableSharedMemory::FAILED:
234 return false;
237 NOTREACHED();
238 return false;
241 void ChildDiscardableSharedMemoryManager::UnlockSpan(
242 DiscardableSharedMemoryHeap::Span* span) {
243 base::AutoLock lock(lock_);
245 DCHECK(span->shared_memory());
246 size_t offset = span->start() * base::GetPageSize() -
247 reinterpret_cast<size_t>(span->shared_memory()->memory());
248 size_t length = span->length() * base::GetPageSize();
250 return span->shared_memory()->Unlock(offset, length);
253 void ChildDiscardableSharedMemoryManager::ReleaseSpan(
254 scoped_ptr<DiscardableSharedMemoryHeap::Span> span) {
255 base::AutoLock lock(lock_);
257 // Delete span instead of merging it into free lists if memory is gone.
258 if (!span->shared_memory())
259 return;
261 heap_.MergeIntoFreeLists(span.Pass());
263 // Bytes of free memory changed.
264 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
267 scoped_ptr<base::DiscardableSharedMemory>
268 ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
269 size_t size,
270 DiscardableSharedMemoryId id) {
271 TRACE_EVENT2("renderer",
272 "ChildDiscardableSharedMemoryManager::"
273 "AllocateLockedDiscardableSharedMemory",
274 "size", size, "id", id);
276 base::SharedMemoryHandle handle = base::SharedMemory::NULLHandle();
277 sender_->Send(
278 new ChildProcessHostMsg_SyncAllocateLockedDiscardableSharedMemory(
279 size, id, &handle));
280 scoped_ptr<base::DiscardableSharedMemory> memory(
281 new base::DiscardableSharedMemory(handle));
282 if (!memory->Map(size))
283 base::TerminateBecauseOutOfMemory(size);
284 return memory.Pass();
287 void ChildDiscardableSharedMemoryManager::MemoryUsageChanged(
288 size_t new_bytes_total,
289 size_t new_bytes_free) const {
290 TRACE_COUNTER2("renderer", "DiscardableMemoryUsage", "allocated",
291 new_bytes_total - new_bytes_free, "free", new_bytes_free);
293 static const char kDiscardableMemoryAllocatedKey[] =
294 "discardable-memory-allocated";
295 base::debug::SetCrashKeyValue(kDiscardableMemoryAllocatedKey,
296 base::Uint64ToString(new_bytes_total));
298 static const char kDiscardableMemoryFreeKey[] = "discardable-memory-free";
299 base::debug::SetCrashKeyValue(kDiscardableMemoryFreeKey,
300 base::Uint64ToString(new_bytes_free));
303 } // namespace content