Adding Test Fixture for initial test cases for the App Remoting Test Driver. Also...
[chromium-blink-merge.git] / content / child / child_discardable_shared_memory_manager.cc
blob14dfe97e7df975420594e4de8ed44e358451f88d
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/process_metrics.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/trace_event/trace_event.h"
16 #include "content/common/child_process_messages.h"
18 namespace content {
19 namespace {
21 // Default allocation size.
22 #if defined(OS_ANDROID)
23 // Larger allocation size on Android to avoid reaching the FD-limit.
24 const size_t kAllocationSize = 32 * 1024 * 1024;
25 #else
26 const size_t kAllocationSize = 4 * 1024 * 1024;
27 #endif
29 // Global atomic to generate unique discardable shared memory IDs.
30 base::StaticAtomicSequenceNumber g_next_discardable_shared_memory_id;
32 class DiscardableMemoryImpl : public base::DiscardableMemory {
33 public:
34 DiscardableMemoryImpl(ChildDiscardableSharedMemoryManager* manager,
35 scoped_ptr<DiscardableSharedMemoryHeap::Span> span)
36 : manager_(manager), span_(span.Pass()), is_locked_(true) {}
38 ~DiscardableMemoryImpl() override {
39 if (is_locked_)
40 manager_->UnlockSpan(span_.get());
42 manager_->ReleaseSpan(span_.Pass());
45 // Overridden from base::DiscardableMemory:
46 bool Lock() override {
47 DCHECK(!is_locked_);
49 if (!manager_->LockSpan(span_.get()))
50 return false;
52 is_locked_ = true;
53 return true;
55 void Unlock() override {
56 DCHECK(is_locked_);
58 manager_->UnlockSpan(span_.get());
59 is_locked_ = false;
61 void* Memory() const override {
62 DCHECK(is_locked_);
63 return reinterpret_cast<void*>(span_->start() * base::GetPageSize());
66 private:
67 ChildDiscardableSharedMemoryManager* const manager_;
68 scoped_ptr<DiscardableSharedMemoryHeap::Span> span_;
69 bool is_locked_;
71 DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryImpl);
74 } // namespace
76 ChildDiscardableSharedMemoryManager::ChildDiscardableSharedMemoryManager(
77 ThreadSafeSender* sender)
78 : heap_(base::GetPageSize()), sender_(sender), weak_ptr_factory_(this) {
81 ChildDiscardableSharedMemoryManager::~ChildDiscardableSharedMemoryManager() {
82 // Cancel all DeletedDiscardableSharedMemory callbacks.
83 weak_ptr_factory_.InvalidateWeakPtrs();
85 // TODO(reveman): Determine if this DCHECK can be enabled. crbug.com/430533
86 // DCHECK_EQ(heap_.GetSize(), heap_.GetSizeOfFreeLists());
87 if (heap_.GetSize())
88 MemoryUsageChanged(0, 0);
91 scoped_ptr<base::DiscardableMemory>
92 ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
93 size_t size) {
94 base::AutoLock lock(lock_);
96 DCHECK_NE(size, 0u);
98 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.DiscardableAllocationSize",
99 size / 1024, // In KB
101 4 * 1024 * 1024, // 4 GB
102 50);
104 // Round up to multiple of page size.
105 size_t pages = (size + base::GetPageSize() - 1) / base::GetPageSize();
107 // Default allocation size in pages.
108 size_t allocation_pages = kAllocationSize / base::GetPageSize();
110 size_t slack = 0;
111 // When searching the free lists, allow a slack between required size and
112 // free span size that is less or equal to kAllocationSize. This is to
113 // avoid segments larger then kAllocationSize unless they are a perfect
114 // fit. The result is that large allocations can be reused without reducing
115 // the ability to discard memory.
116 if (pages < allocation_pages)
117 slack = allocation_pages - pages;
119 size_t heap_size_prior_to_releasing_purged_memory = heap_.GetSize();
120 for (;;) {
121 // Search free lists for suitable span.
122 scoped_ptr<DiscardableSharedMemoryHeap::Span> free_span =
123 heap_.SearchFreeLists(pages, slack);
124 if (!free_span.get())
125 break;
127 // Attempt to lock |free_span|. Delete span and search free lists again
128 // if locking failed.
129 if (free_span->shared_memory()->Lock(
130 free_span->start() * base::GetPageSize() -
131 reinterpret_cast<size_t>(free_span->shared_memory()->memory()),
132 free_span->length() * base::GetPageSize()) ==
133 base::DiscardableSharedMemory::FAILED) {
134 DCHECK(!free_span->shared_memory()->IsMemoryResident());
135 // We have to release purged memory before |free_span| can be destroyed.
136 heap_.ReleasePurgedMemory();
137 DCHECK(!free_span->shared_memory());
138 continue;
141 // Memory usage is guaranteed to have changed after having removed
142 // at least one span from the free lists.
143 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
145 return make_scoped_ptr(new DiscardableMemoryImpl(this, free_span.Pass()));
148 // Release purged memory to free up the address space before we attempt to
149 // allocate more memory.
150 heap_.ReleasePurgedMemory();
152 // Make sure crash keys are up to date in case allocation fails.
153 if (heap_.GetSize() != heap_size_prior_to_releasing_purged_memory)
154 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
156 size_t pages_to_allocate =
157 std::max(kAllocationSize / base::GetPageSize(), pages);
158 size_t allocation_size_in_bytes = pages_to_allocate * base::GetPageSize();
160 DiscardableSharedMemoryId new_id =
161 g_next_discardable_shared_memory_id.GetNext();
163 // Ask parent process to allocate a new discardable shared memory segment.
164 scoped_ptr<base::DiscardableSharedMemory> shared_memory(
165 AllocateLockedDiscardableSharedMemory(allocation_size_in_bytes, new_id));
167 // Create span for allocated memory.
168 scoped_ptr<DiscardableSharedMemoryHeap::Span> new_span(heap_.Grow(
169 shared_memory.Pass(), allocation_size_in_bytes,
170 base::Bind(
171 &ChildDiscardableSharedMemoryManager::DeletedDiscardableSharedMemory,
172 weak_ptr_factory_.GetWeakPtr(), new_id)));
174 // Unlock and insert any left over memory into free lists.
175 if (pages < pages_to_allocate) {
176 scoped_ptr<DiscardableSharedMemoryHeap::Span> leftover =
177 heap_.Split(new_span.get(), pages);
178 leftover->shared_memory()->Unlock(
179 leftover->start() * base::GetPageSize() -
180 reinterpret_cast<size_t>(leftover->shared_memory()->memory()),
181 leftover->length() * base::GetPageSize());
182 heap_.MergeIntoFreeLists(leftover.Pass());
185 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
187 return make_scoped_ptr(new DiscardableMemoryImpl(this, new_span.Pass()));
190 void ChildDiscardableSharedMemoryManager::ReleaseFreeMemory() {
191 base::AutoLock lock(lock_);
193 size_t heap_size_prior_to_releasing_memory = heap_.GetSize();
195 // Release both purged and free memory.
196 heap_.ReleasePurgedMemory();
197 heap_.ReleaseFreeMemory();
199 if (heap_.GetSize() != heap_size_prior_to_releasing_memory)
200 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
203 bool ChildDiscardableSharedMemoryManager::LockSpan(
204 DiscardableSharedMemoryHeap::Span* span) {
205 base::AutoLock lock(lock_);
207 if (!span->shared_memory())
208 return false;
210 size_t offset = span->start() * base::GetPageSize() -
211 reinterpret_cast<size_t>(span->shared_memory()->memory());
212 size_t length = span->length() * base::GetPageSize();
214 switch (span->shared_memory()->Lock(offset, length)) {
215 case base::DiscardableSharedMemory::SUCCESS:
216 return true;
217 case base::DiscardableSharedMemory::PURGED:
218 span->shared_memory()->Unlock(offset, length);
219 return false;
220 case base::DiscardableSharedMemory::FAILED:
221 return false;
224 NOTREACHED();
225 return false;
228 void ChildDiscardableSharedMemoryManager::UnlockSpan(
229 DiscardableSharedMemoryHeap::Span* span) {
230 base::AutoLock lock(lock_);
232 DCHECK(span->shared_memory());
233 size_t offset = span->start() * base::GetPageSize() -
234 reinterpret_cast<size_t>(span->shared_memory()->memory());
235 size_t length = span->length() * base::GetPageSize();
237 return span->shared_memory()->Unlock(offset, length);
240 void ChildDiscardableSharedMemoryManager::ReleaseSpan(
241 scoped_ptr<DiscardableSharedMemoryHeap::Span> span) {
242 base::AutoLock lock(lock_);
244 // Delete span instead of merging it into free lists if memory is gone.
245 if (!span->shared_memory())
246 return;
248 heap_.MergeIntoFreeLists(span.Pass());
250 // Bytes of free memory changed.
251 MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
254 scoped_ptr<base::DiscardableSharedMemory>
255 ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
256 size_t size,
257 DiscardableSharedMemoryId id) {
258 TRACE_EVENT2("renderer",
259 "ChildDiscardableSharedMemoryManager::"
260 "AllocateLockedDiscardableSharedMemory",
261 "size", size, "id", id);
263 base::SharedMemoryHandle handle = base::SharedMemory::NULLHandle();
264 sender_->Send(
265 new ChildProcessHostMsg_SyncAllocateLockedDiscardableSharedMemory(
266 size, id, &handle));
267 CHECK(base::SharedMemory::IsHandleValid(handle));
268 scoped_ptr<base::DiscardableSharedMemory> memory(
269 new base::DiscardableSharedMemory(handle));
270 CHECK(memory->Map(size));
271 return memory.Pass();
274 void ChildDiscardableSharedMemoryManager::DeletedDiscardableSharedMemory(
275 DiscardableSharedMemoryId id) {
276 sender_->Send(new ChildProcessHostMsg_DeletedDiscardableSharedMemory(id));
279 void ChildDiscardableSharedMemoryManager::MemoryUsageChanged(
280 size_t new_bytes_total,
281 size_t new_bytes_free) const {
282 TRACE_COUNTER2("renderer", "DiscardableMemoryUsage", "allocated",
283 new_bytes_total - new_bytes_free, "free", new_bytes_free);
285 static const char kDiscardableMemoryAllocatedKey[] =
286 "discardable-memory-allocated";
287 base::debug::SetCrashKeyValue(kDiscardableMemoryAllocatedKey,
288 base::Uint64ToString(new_bytes_total));
290 static const char kDiscardableMemoryFreeKey[] = "discardable-memory-free";
291 base::debug::SetCrashKeyValue(kDiscardableMemoryFreeKey,
292 base::Uint64ToString(new_bytes_free));
295 } // namespace content