PepperVideoDestinationHost: Check that recieved frames have strictly increasing times...
[chromium-blink-merge.git] / content / common / host_discardable_shared_memory_manager.cc
blobbd7095a9a448af4c47a8c4bb2086dbbfa87abc07
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/callback.h"
10 #include "base/debug/crash_logging.h"
11 #include "base/debug/trace_event.h"
12 #include "base/lazy_instance.h"
13 #include "base/numerics/safe_math.h"
14 #include "base/strings/string_number_conversions.h"
16 namespace content {
17 namespace {
19 base::LazyInstance<HostDiscardableSharedMemoryManager>
20 g_discardable_shared_memory_manager = LAZY_INSTANCE_INITIALIZER;
22 const size_t kDefaultMemoryLimit = 512 * 1024 * 1024;
24 const int kEnforceMemoryPolicyDelayMs = 1000;
26 } // namespace
28 HostDiscardableSharedMemoryManager::MemorySegment::MemorySegment(
29 linked_ptr<base::DiscardableSharedMemory> memory,
30 base::ProcessHandle process_handle)
31 : memory(memory), process_handle(process_handle) {
34 HostDiscardableSharedMemoryManager::MemorySegment::~MemorySegment() {
37 HostDiscardableSharedMemoryManager::HostDiscardableSharedMemoryManager()
38 : memory_limit_(kDefaultMemoryLimit),
39 bytes_allocated_(0),
40 memory_pressure_listener_(new base::MemoryPressureListener(
41 base::Bind(&HostDiscardableSharedMemoryManager::OnMemoryPressure,
42 base::Unretained(this)))),
43 enforce_memory_policy_pending_(false),
44 weak_ptr_factory_(this) {
47 HostDiscardableSharedMemoryManager::~HostDiscardableSharedMemoryManager() {
50 HostDiscardableSharedMemoryManager*
51 HostDiscardableSharedMemoryManager::current() {
52 return g_discardable_shared_memory_manager.Pointer();
55 scoped_ptr<base::DiscardableMemoryShmemChunk>
56 HostDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
57 size_t size) {
58 // TODO(reveman): Need to implement this for discardable memory support in
59 // the browser process.
60 NOTIMPLEMENTED();
61 return nullptr;
64 void HostDiscardableSharedMemoryManager::
65 AllocateLockedDiscardableSharedMemoryForChild(
66 base::ProcessHandle process_handle,
67 size_t size,
68 base::SharedMemoryHandle* shared_memory_handle) {
69 base::AutoLock lock(lock_);
71 // Memory usage must be reduced to prevent the addition of |size| from
72 // taking usage above the limit. Usage should be reduced to 0 in cases
73 // where |size| is greater than the limit.
74 size_t limit = 0;
75 // Note: the actual mapped size can be larger than requested and cause
76 // |bytes_allocated_| to temporarily be larger than |memory_limit_|. The
77 // error is minimized by incrementing |bytes_allocated_| with the actual
78 // mapped size rather than |size| below.
79 if (size < memory_limit_)
80 limit = memory_limit_ - size;
82 if (bytes_allocated_ > limit)
83 ReduceMemoryUsageUntilWithinLimit(limit);
85 linked_ptr<base::DiscardableSharedMemory> memory(
86 new base::DiscardableSharedMemory);
87 if (!memory->CreateAndMap(size)) {
88 *shared_memory_handle = base::SharedMemory::NULLHandle();
89 return;
92 if (!memory->ShareToProcess(process_handle, shared_memory_handle)) {
93 LOG(ERROR) << "Cannot share discardable memory segment";
94 *shared_memory_handle = base::SharedMemory::NULLHandle();
95 return;
98 base::CheckedNumeric<size_t> checked_bytes_allocated = bytes_allocated_;
99 checked_bytes_allocated += memory->mapped_size();
100 if (!checked_bytes_allocated.IsValid()) {
101 *shared_memory_handle = base::SharedMemory::NULLHandle();
102 return;
105 bytes_allocated_ = checked_bytes_allocated.ValueOrDie();
106 BytesAllocatedChanged(bytes_allocated_);
108 segments_.push_back(MemorySegment(memory, process_handle));
109 std::push_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
111 if (bytes_allocated_ > memory_limit_)
112 ScheduleEnforceMemoryPolicy();
115 void HostDiscardableSharedMemoryManager::ProcessRemoved(
116 base::ProcessHandle process_handle) {
117 base::AutoLock lock(lock_);
119 size_t bytes_allocated_before_purging = bytes_allocated_;
120 for (auto& segment : segments_) {
121 // Skip segments that belong to a different process.
122 if (segment.process_handle != process_handle)
123 continue;
125 size_t size = segment.memory->mapped_size();
126 DCHECK_GE(bytes_allocated_, size);
128 // This will unmap the memory segment and drop our reference. The result
129 // is that the memory will be released to the OS if the child process is
130 // no longer referencing it.
131 // Note: We intentionally leave the segment in the vector to avoid
132 // reconstructing the heap. The element will be removed from the heap
133 // when its last usage time is older than all other segments.
134 segment.memory->Close();
135 bytes_allocated_ -= size;
138 if (bytes_allocated_ != bytes_allocated_before_purging)
139 BytesAllocatedChanged(bytes_allocated_);
142 void HostDiscardableSharedMemoryManager::SetMemoryLimit(size_t limit) {
143 base::AutoLock lock(lock_);
145 memory_limit_ = limit;
146 ReduceMemoryUsageUntilWithinMemoryLimit();
149 void HostDiscardableSharedMemoryManager::EnforceMemoryPolicy() {
150 base::AutoLock lock(lock_);
152 enforce_memory_policy_pending_ = false;
153 ReduceMemoryUsageUntilWithinMemoryLimit();
156 void HostDiscardableSharedMemoryManager::OnMemoryPressure(
157 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
158 base::AutoLock lock(lock_);
160 switch (memory_pressure_level) {
161 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
162 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
163 break;
164 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
165 // Purge everything possible when pressure is critical.
166 ReduceMemoryUsageUntilWithinLimit(0);
167 break;
171 void
172 HostDiscardableSharedMemoryManager::ReduceMemoryUsageUntilWithinMemoryLimit() {
173 if (bytes_allocated_ <= memory_limit_)
174 return;
176 ReduceMemoryUsageUntilWithinLimit(memory_limit_);
177 if (bytes_allocated_ > memory_limit_)
178 ScheduleEnforceMemoryPolicy();
181 void HostDiscardableSharedMemoryManager::ReduceMemoryUsageUntilWithinLimit(
182 size_t limit) {
183 TRACE_EVENT1("renderer_host",
184 "HostDiscardableSharedMemoryManager::"
185 "ReduceMemoryUsageUntilWithinLimit",
186 "bytes_allocated",
187 bytes_allocated_);
189 // Usage time of currently locked segments are updated to this time and
190 // we stop eviction attempts as soon as we come across a segment that we've
191 // previously tried to evict but was locked.
192 base::Time current_time = Now();
194 lock_.AssertAcquired();
195 size_t bytes_allocated_before_purging = bytes_allocated_;
196 while (!segments_.empty()) {
197 if (bytes_allocated_ <= limit)
198 break;
200 // Stop eviction attempts when the LRU segment is currently in use.
201 if (segments_.front().memory->last_known_usage() >= current_time)
202 break;
204 std::pop_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
205 MemorySegment segment = segments_.back();
206 segments_.pop_back();
208 // Attempt to purge and truncate LRU segment. When successful, as much
209 // memory as possible will be released to the OS. How much memory is
210 // released depends on the platform. The child process should perform
211 // periodic cleanup to ensure that all memory is release within a
212 // reasonable amount of time.
213 if (segment.memory->PurgeAndTruncate(current_time)) {
214 size_t size = segment.memory->mapped_size();
215 DCHECK_GE(bytes_allocated_, size);
216 bytes_allocated_ -= size;
217 continue;
220 // Add memory segment (with updated usage timestamp) back on heap after
221 // failed attempt to purge it.
222 segments_.push_back(segment);
223 std::push_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
226 if (bytes_allocated_ != bytes_allocated_before_purging)
227 BytesAllocatedChanged(bytes_allocated_);
230 void HostDiscardableSharedMemoryManager::BytesAllocatedChanged(
231 size_t new_bytes_allocated) const {
232 TRACE_COUNTER_ID1(
233 "base", "TotalDiscardableMemoryUsage", this, new_bytes_allocated);
235 static const char kTotalDiscardableMemoryUsageKey[] = "total-dm-usage";
236 base::debug::SetCrashKeyValue(kTotalDiscardableMemoryUsageKey,
237 base::Uint64ToString(new_bytes_allocated));
240 base::Time HostDiscardableSharedMemoryManager::Now() const {
241 return base::Time::Now();
244 void HostDiscardableSharedMemoryManager::ScheduleEnforceMemoryPolicy() {
245 if (enforce_memory_policy_pending_)
246 return;
248 enforce_memory_policy_pending_ = true;
249 base::MessageLoop::current()->PostDelayedTask(
250 FROM_HERE,
251 base::Bind(&HostDiscardableSharedMemoryManager::EnforceMemoryPolicy,
252 weak_ptr_factory_.GetWeakPtr()),
253 base::TimeDelta::FromMilliseconds(kEnforceMemoryPolicyDelayMs));
256 } // namespace content