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"
14 // Default allocation size.
15 const size_t kAllocationSize
= 4 * 1024 * 1024;
17 class DiscardableMemoryShmemChunkImpl
18 : public base::DiscardableMemoryShmemChunk
{
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());
36 ChildDiscardableSharedMemoryManager
* manager_
;
37 scoped_ptr
<DiscardableSharedMemoryHeap::Span
> span_
;
39 DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryShmemChunkImpl
);
44 ChildDiscardableSharedMemoryManager::ChildDiscardableSharedMemoryManager(
45 ThreadSafeSender
* sender
)
46 : heap_(base::GetPageSize()), sender_(sender
) {
49 ChildDiscardableSharedMemoryManager::~ChildDiscardableSharedMemoryManager() {
52 scoped_ptr
<base::DiscardableMemoryShmemChunk
>
53 ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
55 base::AutoLock
lock(lock_
);
59 // Round up to multiple of page size.
60 size_t pages
= (size
+ base::GetPageSize() - 1) / base::GetPageSize();
63 // Search free list for available space.
64 scoped_ptr
<DiscardableSharedMemoryHeap::Span
> free_span
=
65 heap_
.SearchFreeList(pages
);
69 // Attempt to lock |free_span|. Delete span and search free list again
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());
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())
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
:
138 case base::DiscardableSharedMemory::PURGED
:
139 span
->shared_memory()->Unlock(offset
, length
);
141 case base::DiscardableSharedMemory::FAILED
:
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())
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(
180 TRACE_EVENT1("renderer",
181 "ChildDiscardableSharedMemoryManager::"
182 "AllocateLockedDiscardableSharedMemory",
186 base::SharedMemoryHandle handle
= base::SharedMemory::NULLHandle();
188 new ChildProcessHostMsg_SyncAllocateLockedDiscardableSharedMemory(
190 scoped_ptr
<base::DiscardableSharedMemory
> memory(
191 new base::DiscardableSharedMemory(handle
));
192 CHECK(memory
->Map(size
));
193 return memory
.Pass();
196 } // namespace content