Fix crash on app list start page contents not existing.
[chromium-blink-merge.git] / content / child / child_discardable_shared_memory_manager.cc
blob140e9495d58f7a7665b7b686c2c65db02c0bcc95
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/memory/discardable_shared_memory.h"
8 #include "base/process/process_metrics.h"
9 #include "content/common/child_process_messages.h"
11 namespace content {
12 namespace {
14 // Default allocation size.
15 const size_t kAllocationSize = 4 * 1024 * 1024;
17 class DiscardableMemoryShmemChunkImpl
18 : public base::DiscardableMemoryShmemChunk {
19 public:
20 DiscardableMemoryShmemChunkImpl(
21 ChildDiscardableSharedMemoryManager* manager,
22 scoped_ptr<DiscardableSharedMemoryHeap::Span> span)
23 : manager_(manager), span_(span.Pass()) {}
24 ~DiscardableMemoryShmemChunkImpl() override {
25 manager_->ReleaseSpan(span_.Pass());
28 // Overridden from DiscardableMemoryShmemChunk:
29 bool Lock() override { return manager_->LockSpan(span_.get()); }
30 void Unlock() override { manager_->UnlockSpan(span_.get()); }
31 void* Memory() const override {
32 return reinterpret_cast<void*>(span_->start() * base::GetPageSize());
35 private:
36 ChildDiscardableSharedMemoryManager* manager_;
37 scoped_ptr<DiscardableSharedMemoryHeap::Span> span_;
39 DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryShmemChunkImpl);
42 } // namespace
44 ChildDiscardableSharedMemoryManager::ChildDiscardableSharedMemoryManager(
45 ThreadSafeSender* sender)
46 : heap_(base::GetPageSize()), sender_(sender) {
49 ChildDiscardableSharedMemoryManager::~ChildDiscardableSharedMemoryManager() {
52 scoped_ptr<base::DiscardableMemoryShmemChunk>
53 ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
54 size_t size) {
55 base::AutoLock lock(lock_);
57 DCHECK_NE(size, 0u);
59 // Round up to multiple of page size.
60 size_t pages = (size + base::GetPageSize() - 1) / base::GetPageSize();
62 for (;;) {
63 // Search free list for available space.
64 scoped_ptr<DiscardableSharedMemoryHeap::Span> free_span =
65 heap_.SearchFreeList(pages);
66 if (!free_span.get())
67 break;
69 // Attempt to lock |free_span|. Delete span and search free list again
70 // if locking failed.
71 if (free_span->shared_memory()->Lock(
72 free_span->start() * base::GetPageSize() -
73 reinterpret_cast<size_t>(free_span->shared_memory()->memory()),
74 free_span->length() * base::GetPageSize()) ==
75 base::DiscardableSharedMemory::FAILED) {
76 DCHECK(!free_span->shared_memory()->IsMemoryResident());
77 // We have to release free memory before |free_span| can be destroyed.
78 heap_.ReleaseFreeMemory();
79 DCHECK(!free_span->shared_memory());
80 continue;
83 return make_scoped_ptr(
84 new DiscardableMemoryShmemChunkImpl(this, free_span.Pass()));
87 // Release free memory and free up the address space. Failing to do this
88 // can result in the child process running out of address space.
89 heap_.ReleaseFreeMemory();
91 size_t pages_to_allocate =
92 std::max(kAllocationSize / base::GetPageSize(), pages);
93 size_t allocation_size_in_bytes = pages_to_allocate * base::GetPageSize();
95 // Ask parent process to allocate a new discardable shared memory segment.
96 scoped_ptr<base::DiscardableSharedMemory> shared_memory(
97 AllocateLockedDiscardableSharedMemory(allocation_size_in_bytes));
99 // Create span for allocated memory.
100 scoped_ptr<DiscardableSharedMemoryHeap::Span> new_span(
101 heap_.Grow(shared_memory.Pass(), allocation_size_in_bytes));
103 // Unlock and insert any left over memory into free list.
104 if (pages < pages_to_allocate) {
105 scoped_ptr<DiscardableSharedMemoryHeap::Span> leftover =
106 heap_.Split(new_span.get(), pages);
107 leftover->shared_memory()->Unlock(
108 leftover->start() * base::GetPageSize() -
109 reinterpret_cast<size_t>(leftover->shared_memory()->memory()),
110 leftover->length() * base::GetPageSize());
111 heap_.MergeIntoFreeList(leftover.Pass());
114 return make_scoped_ptr(
115 new DiscardableMemoryShmemChunkImpl(this, new_span.Pass()));
118 void ChildDiscardableSharedMemoryManager::ReleaseFreeMemory() {
119 base::AutoLock lock(lock_);
121 heap_.ReleaseFreeMemory();
124 bool ChildDiscardableSharedMemoryManager::LockSpan(
125 DiscardableSharedMemoryHeap::Span* span) {
126 base::AutoLock lock(lock_);
128 if (!span->shared_memory())
129 return false;
131 size_t offset = span->start() * base::GetPageSize() -
132 reinterpret_cast<size_t>(span->shared_memory()->memory());
133 size_t length = span->length() * base::GetPageSize();
135 switch (span->shared_memory()->Lock(offset, length)) {
136 case base::DiscardableSharedMemory::SUCCESS:
137 return true;
138 case base::DiscardableSharedMemory::PURGED:
139 span->shared_memory()->Unlock(offset, length);
140 return false;
141 case base::DiscardableSharedMemory::FAILED:
142 return false;
145 NOTREACHED();
146 return false;
149 void ChildDiscardableSharedMemoryManager::UnlockSpan(
150 DiscardableSharedMemoryHeap::Span* span) {
151 base::AutoLock lock(lock_);
153 DCHECK(span->shared_memory());
154 size_t offset = span->start() * base::GetPageSize() -
155 reinterpret_cast<size_t>(span->shared_memory()->memory());
156 size_t length = span->length() * base::GetPageSize();
158 return span->shared_memory()->Unlock(offset, length);
161 void ChildDiscardableSharedMemoryManager::ReleaseSpan(
162 scoped_ptr<DiscardableSharedMemoryHeap::Span> span) {
163 base::AutoLock lock(lock_);
165 // Delete span instead of merging it into free list if memory is gone.
166 if (!span->shared_memory())
167 return;
169 // Purge backing memory if span is greater than kAllocationSize. This will
170 // prevent it from being reused and associated resources will be released.
171 if (span->length() * base::GetPageSize() > kAllocationSize)
172 span->shared_memory()->Purge(base::Time::Now());
174 heap_.MergeIntoFreeList(span.Pass());
177 scoped_ptr<base::DiscardableSharedMemory>
178 ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
179 size_t size) {
180 TRACE_EVENT1("renderer",
181 "ChildDiscardableSharedMemoryManager::"
182 "AllocateLockedDiscardableSharedMemory",
183 "size",
184 size);
186 base::SharedMemoryHandle handle = base::SharedMemory::NULLHandle();
187 sender_->Send(
188 new ChildProcessHostMsg_SyncAllocateLockedDiscardableSharedMemory(
189 size, &handle));
190 scoped_ptr<base::DiscardableSharedMemory> memory(
191 new base::DiscardableSharedMemory(handle));
192 CHECK(memory->Map(size));
193 return memory.Pass();
196 } // namespace content