Move DidStartLoading, DidStopLoading, DidChangeLoadProgress IPCs to RFHI.
[chromium-blink-merge.git] / content / common / host_discardable_shared_memory_manager.cc
blob6709c2527585d21057642a9191c0463c1a14c8fb
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/common/host_discardable_shared_memory_manager.h"
7 #include <algorithm>
9 #include "base/atomic_sequence_num.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/debug/crash_logging.h"
13 #include "base/lazy_instance.h"
14 #include "base/memory/discardable_memory.h"
15 #include "base/numerics/safe_math.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/sys_info.h"
18 #include "base/trace_event/trace_event.h"
20 namespace content {
21 namespace {
23 class DiscardableMemoryImpl : public base::DiscardableMemory {
24 public:
25 DiscardableMemoryImpl(scoped_ptr<base::DiscardableSharedMemory> shared_memory,
26 const base::Closure& deleted_callback)
27 : shared_memory_(shared_memory.Pass()),
28 deleted_callback_(deleted_callback),
29 is_locked_(true) {}
31 ~DiscardableMemoryImpl() override {
32 if (is_locked_)
33 shared_memory_->Unlock(0, 0);
35 deleted_callback_.Run();
38 // Overridden from base::DiscardableMemory:
39 bool Lock() override {
40 DCHECK(!is_locked_);
42 if (shared_memory_->Lock(0, 0) != base::DiscardableSharedMemory::SUCCESS)
43 return false;
45 is_locked_ = true;
46 return true;
48 void Unlock() override {
49 DCHECK(is_locked_);
51 shared_memory_->Unlock(0, 0);
52 is_locked_ = false;
54 void* data() const override {
55 DCHECK(is_locked_);
56 return shared_memory_->memory();
59 private:
60 scoped_ptr<base::DiscardableSharedMemory> shared_memory_;
61 const base::Closure deleted_callback_;
62 bool is_locked_;
64 DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryImpl);
67 base::LazyInstance<HostDiscardableSharedMemoryManager>
68 g_discardable_shared_memory_manager = LAZY_INSTANCE_INITIALIZER;
70 const int64_t kMaxDefaultMemoryLimit = 512 * 1024 * 1024;
72 const int kEnforceMemoryPolicyDelayMs = 1000;
74 // Global atomic to generate unique discardable shared memory IDs.
75 base::StaticAtomicSequenceNumber g_next_discardable_shared_memory_id;
77 } // namespace
79 HostDiscardableSharedMemoryManager::MemorySegment::MemorySegment(
80 scoped_ptr<base::DiscardableSharedMemory> memory)
81 : memory_(memory.Pass()) {
84 HostDiscardableSharedMemoryManager::MemorySegment::~MemorySegment() {
87 HostDiscardableSharedMemoryManager::HostDiscardableSharedMemoryManager()
88 : memory_limit_(
89 // Allow 25% of physical memory to be used for discardable memory.
90 std::min(base::SysInfo::AmountOfPhysicalMemory() / 4,
91 kMaxDefaultMemoryLimit)),
92 bytes_allocated_(0),
93 memory_pressure_listener_(new base::MemoryPressureListener(
94 base::Bind(&HostDiscardableSharedMemoryManager::OnMemoryPressure,
95 base::Unretained(this)))),
96 enforce_memory_policy_pending_(false),
97 weak_ptr_factory_(this) {
98 DCHECK_NE(memory_limit_, 0u);
101 HostDiscardableSharedMemoryManager::~HostDiscardableSharedMemoryManager() {
104 HostDiscardableSharedMemoryManager*
105 HostDiscardableSharedMemoryManager::current() {
106 return g_discardable_shared_memory_manager.Pointer();
109 scoped_ptr<base::DiscardableMemory>
110 HostDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
111 size_t size) {
112 DiscardableSharedMemoryId new_id =
113 g_next_discardable_shared_memory_id.GetNext();
114 base::ProcessHandle current_process_handle = base::GetCurrentProcessHandle();
116 // Note: Use DiscardableSharedMemoryHeap for in-process allocation
117 // of discardable memory if the cost of each allocation is too high.
118 base::SharedMemoryHandle handle;
119 AllocateLockedDiscardableSharedMemory(current_process_handle, size, new_id,
120 &handle);
121 CHECK(base::SharedMemory::IsHandleValid(handle));
122 scoped_ptr<base::DiscardableSharedMemory> memory(
123 new base::DiscardableSharedMemory(handle));
124 CHECK(memory->Map(size));
125 return make_scoped_ptr(new DiscardableMemoryImpl(
126 memory.Pass(),
127 base::Bind(
128 &HostDiscardableSharedMemoryManager::DeletedDiscardableSharedMemory,
129 base::Unretained(this), new_id, current_process_handle)));
132 void HostDiscardableSharedMemoryManager::
133 AllocateLockedDiscardableSharedMemoryForChild(
134 base::ProcessHandle process_handle,
135 size_t size,
136 DiscardableSharedMemoryId id,
137 base::SharedMemoryHandle* shared_memory_handle) {
138 AllocateLockedDiscardableSharedMemory(process_handle, size, id,
139 shared_memory_handle);
142 void HostDiscardableSharedMemoryManager::ChildDeletedDiscardableSharedMemory(
143 DiscardableSharedMemoryId id,
144 base::ProcessHandle process_handle) {
145 DeletedDiscardableSharedMemory(id, process_handle);
148 void HostDiscardableSharedMemoryManager::ProcessRemoved(
149 base::ProcessHandle process_handle) {
150 base::AutoLock lock(lock_);
152 ProcessMap::iterator process_it = processes_.find(process_handle);
153 if (process_it == processes_.end())
154 return;
156 size_t bytes_allocated_before_releasing_memory = bytes_allocated_;
158 for (auto& segment_it : process_it->second)
159 ReleaseMemory(segment_it.second->memory());
161 processes_.erase(process_it);
163 if (bytes_allocated_ != bytes_allocated_before_releasing_memory)
164 BytesAllocatedChanged(bytes_allocated_);
167 void HostDiscardableSharedMemoryManager::SetMemoryLimit(size_t limit) {
168 base::AutoLock lock(lock_);
170 memory_limit_ = limit;
171 ReduceMemoryUsageUntilWithinMemoryLimit();
174 void HostDiscardableSharedMemoryManager::EnforceMemoryPolicy() {
175 base::AutoLock lock(lock_);
177 enforce_memory_policy_pending_ = false;
178 ReduceMemoryUsageUntilWithinMemoryLimit();
181 size_t HostDiscardableSharedMemoryManager::GetBytesAllocated() {
182 base::AutoLock lock(lock_);
184 return bytes_allocated_;
187 void HostDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
188 base::ProcessHandle process_handle,
189 size_t size,
190 DiscardableSharedMemoryId id,
191 base::SharedMemoryHandle* shared_memory_handle) {
192 base::AutoLock lock(lock_);
194 // Make sure |id| is not already in use.
195 MemorySegmentMap& process_segments = processes_[process_handle];
196 if (process_segments.find(id) != process_segments.end()) {
197 LOG(ERROR) << "Invalid discardable shared memory ID";
198 *shared_memory_handle = base::SharedMemory::NULLHandle();
199 return;
202 // Memory usage must be reduced to prevent the addition of |size| from
203 // taking usage above the limit. Usage should be reduced to 0 in cases
204 // where |size| is greater than the limit.
205 size_t limit = 0;
206 // Note: the actual mapped size can be larger than requested and cause
207 // |bytes_allocated_| to temporarily be larger than |memory_limit_|. The
208 // error is minimized by incrementing |bytes_allocated_| with the actual
209 // mapped size rather than |size| below.
210 if (size < memory_limit_)
211 limit = memory_limit_ - size;
213 if (bytes_allocated_ > limit)
214 ReduceMemoryUsageUntilWithinLimit(limit);
216 scoped_ptr<base::DiscardableSharedMemory> memory(
217 new base::DiscardableSharedMemory);
218 if (!memory->CreateAndMap(size)) {
219 *shared_memory_handle = base::SharedMemory::NULLHandle();
220 return;
223 if (!memory->ShareToProcess(process_handle, shared_memory_handle)) {
224 LOG(ERROR) << "Cannot share discardable memory segment";
225 *shared_memory_handle = base::SharedMemory::NULLHandle();
226 return;
229 base::CheckedNumeric<size_t> checked_bytes_allocated = bytes_allocated_;
230 checked_bytes_allocated += memory->mapped_size();
231 if (!checked_bytes_allocated.IsValid()) {
232 *shared_memory_handle = base::SharedMemory::NULLHandle();
233 return;
236 bytes_allocated_ = checked_bytes_allocated.ValueOrDie();
237 BytesAllocatedChanged(bytes_allocated_);
239 scoped_refptr<MemorySegment> segment(new MemorySegment(memory.Pass()));
240 process_segments[id] = segment.get();
241 segments_.push_back(segment.get());
242 std::push_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
244 if (bytes_allocated_ > memory_limit_)
245 ScheduleEnforceMemoryPolicy();
248 void HostDiscardableSharedMemoryManager::DeletedDiscardableSharedMemory(
249 DiscardableSharedMemoryId id,
250 base::ProcessHandle process_handle) {
251 base::AutoLock lock(lock_);
253 MemorySegmentMap& process_segments = processes_[process_handle];
255 MemorySegmentMap::iterator segment_it = process_segments.find(id);
256 if (segment_it == process_segments.end()) {
257 LOG(ERROR) << "Invalid discardable shared memory ID";
258 return;
261 size_t bytes_allocated_before_releasing_memory = bytes_allocated_;
263 ReleaseMemory(segment_it->second->memory());
265 process_segments.erase(segment_it);
267 if (bytes_allocated_ != bytes_allocated_before_releasing_memory)
268 BytesAllocatedChanged(bytes_allocated_);
271 void HostDiscardableSharedMemoryManager::OnMemoryPressure(
272 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
273 base::AutoLock lock(lock_);
275 switch (memory_pressure_level) {
276 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
277 break;
278 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
279 // Purge memory until usage is within half of |memory_limit_|.
280 ReduceMemoryUsageUntilWithinLimit(memory_limit_ / 2);
281 break;
282 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
283 // Purge everything possible when pressure is critical.
284 ReduceMemoryUsageUntilWithinLimit(0);
285 break;
289 void
290 HostDiscardableSharedMemoryManager::ReduceMemoryUsageUntilWithinMemoryLimit() {
291 lock_.AssertAcquired();
293 if (bytes_allocated_ <= memory_limit_)
294 return;
296 ReduceMemoryUsageUntilWithinLimit(memory_limit_);
297 if (bytes_allocated_ > memory_limit_)
298 ScheduleEnforceMemoryPolicy();
301 void HostDiscardableSharedMemoryManager::ReduceMemoryUsageUntilWithinLimit(
302 size_t limit) {
303 TRACE_EVENT1("renderer_host",
304 "HostDiscardableSharedMemoryManager::"
305 "ReduceMemoryUsageUntilWithinLimit",
306 "bytes_allocated",
307 bytes_allocated_);
309 // Usage time of currently locked segments are updated to this time and
310 // we stop eviction attempts as soon as we come across a segment that we've
311 // previously tried to evict but was locked.
312 base::Time current_time = Now();
314 lock_.AssertAcquired();
315 size_t bytes_allocated_before_purging = bytes_allocated_;
316 while (!segments_.empty()) {
317 if (bytes_allocated_ <= limit)
318 break;
320 // Stop eviction attempts when the LRU segment is currently in use.
321 if (segments_.front()->memory()->last_known_usage() >= current_time)
322 break;
324 std::pop_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
325 scoped_refptr<MemorySegment> segment = segments_.back();
326 segments_.pop_back();
328 // Attempt to purge LRU segment. When successful, released the memory.
329 if (segment->memory()->Purge(current_time)) {
330 #if defined(DISCARDABLE_SHARED_MEMORY_SHRINKING)
331 size_t size = segment->memory()->mapped_size();
332 DCHECK_GE(bytes_allocated_, size);
333 bytes_allocated_ -= size;
334 // Shrink memory segment. This will immediately release the memory to
335 // the OS.
336 segment->memory()->Shrink();
337 DCHECK_EQ(segment->memory()->mapped_size(), 0u);
338 #endif
339 ReleaseMemory(segment->memory());
340 continue;
343 // Add memory segment (with updated usage timestamp) back on heap after
344 // failed attempt to purge it.
345 segments_.push_back(segment.get());
346 std::push_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
349 if (bytes_allocated_ != bytes_allocated_before_purging)
350 BytesAllocatedChanged(bytes_allocated_);
353 void HostDiscardableSharedMemoryManager::ReleaseMemory(
354 base::DiscardableSharedMemory* memory) {
355 lock_.AssertAcquired();
357 size_t size = memory->mapped_size();
358 DCHECK_GE(bytes_allocated_, size);
359 bytes_allocated_ -= size;
361 // This will unmap the memory segment and drop our reference. The result
362 // is that the memory will be released to the OS if the child process is
363 // no longer referencing it.
364 // Note: We intentionally leave the segment in the |segments| vector to
365 // avoid reconstructing the heap. The element will be removed from the heap
366 // when its last usage time is older than all other segments.
367 memory->Close();
370 void HostDiscardableSharedMemoryManager::BytesAllocatedChanged(
371 size_t new_bytes_allocated) const {
372 TRACE_COUNTER1("renderer_host", "TotalDiscardableMemoryUsage",
373 new_bytes_allocated);
375 static const char kTotalDiscardableMemoryAllocatedKey[] =
376 "total-discardable-memory-allocated";
377 base::debug::SetCrashKeyValue(kTotalDiscardableMemoryAllocatedKey,
378 base::Uint64ToString(new_bytes_allocated));
381 base::Time HostDiscardableSharedMemoryManager::Now() const {
382 return base::Time::Now();
385 void HostDiscardableSharedMemoryManager::ScheduleEnforceMemoryPolicy() {
386 lock_.AssertAcquired();
388 if (enforce_memory_policy_pending_)
389 return;
391 enforce_memory_policy_pending_ = true;
392 base::MessageLoop::current()->PostDelayedTask(
393 FROM_HERE,
394 base::Bind(&HostDiscardableSharedMemoryManager::EnforceMemoryPolicy,
395 weak_ptr_factory_.GetWeakPtr()),
396 base::TimeDelta::FromMilliseconds(kEnforceMemoryPolicyDelayMs));
399 } // namespace content