aw: Rendering test harness and end-to-end smoke test
[chromium-blink-merge.git] / content / common / host_discardable_shared_memory_manager.cc
blobc56fd113f596419b4aa47746d8fa67f644a4f16e
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::DiscardableSharedMemory>
56 HostDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
57 size_t size) {
58 // TODO(reveman): Need to implement this for discardable memory support in
59 // the browser process.
60 NOTIMPLEMENTED();
61 return scoped_ptr<base::DiscardableSharedMemory>();
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_MODERATE:
162 break;
163 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
164 // Purge everything possible when pressure is critical.
165 ReduceMemoryUsageUntilWithinLimit(0);
166 break;
170 void
171 HostDiscardableSharedMemoryManager::ReduceMemoryUsageUntilWithinMemoryLimit() {
172 if (bytes_allocated_ <= memory_limit_)
173 return;
175 ReduceMemoryUsageUntilWithinLimit(memory_limit_);
176 if (bytes_allocated_ > memory_limit_)
177 ScheduleEnforceMemoryPolicy();
180 void HostDiscardableSharedMemoryManager::ReduceMemoryUsageUntilWithinLimit(
181 size_t limit) {
182 TRACE_EVENT1("renderer_host",
183 "HostDiscardableSharedMemoryManager::"
184 "ReduceMemoryUsageUntilWithinLimit",
185 "bytes_allocated",
186 bytes_allocated_);
188 // Usage time of currently locked segments are updated to this time and
189 // we stop eviction attempts as soon as we come across a segment that we've
190 // previously tried to evict but was locked.
191 base::Time current_time = Now();
193 lock_.AssertAcquired();
194 size_t bytes_allocated_before_purging = bytes_allocated_;
195 while (!segments_.empty()) {
196 if (bytes_allocated_ <= limit)
197 break;
199 // Stop eviction attempts when the LRU segment is currently in use.
200 if (segments_.front().memory->last_known_usage() >= current_time)
201 break;
203 std::pop_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
204 MemorySegment segment = segments_.back();
205 segments_.pop_back();
207 // Attempt to purge and truncate LRU segment. When successful, as much
208 // memory as possible will be released to the OS. How much memory is
209 // released depends on the platform. The child process should perform
210 // periodic cleanup to ensure that all memory is release within a
211 // reasonable amount of time.
212 if (segment.memory->PurgeAndTruncate(current_time)) {
213 size_t size = segment.memory->mapped_size();
214 DCHECK_GE(bytes_allocated_, size);
215 bytes_allocated_ -= size;
216 continue;
219 // Add memory segment (with updated usage timestamp) back on heap after
220 // failed attempt to purge it.
221 segments_.push_back(segment);
222 std::push_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
225 if (bytes_allocated_ != bytes_allocated_before_purging)
226 BytesAllocatedChanged(bytes_allocated_);
229 void HostDiscardableSharedMemoryManager::BytesAllocatedChanged(
230 size_t new_bytes_allocated) const {
231 TRACE_COUNTER_ID1(
232 "base", "TotalDiscardableMemoryUsage", this, new_bytes_allocated);
234 static const char kTotalDiscardableMemoryUsageKey[] = "total-dm-usage";
235 base::debug::SetCrashKeyValue(kTotalDiscardableMemoryUsageKey,
236 base::Uint64ToString(new_bytes_allocated));
239 base::Time HostDiscardableSharedMemoryManager::Now() const {
240 return base::Time::Now();
243 void HostDiscardableSharedMemoryManager::ScheduleEnforceMemoryPolicy() {
244 if (enforce_memory_policy_pending_)
245 return;
247 enforce_memory_policy_pending_ = true;
248 base::MessageLoop::current()->PostDelayedTask(
249 FROM_HERE,
250 base::Bind(&HostDiscardableSharedMemoryManager::EnforceMemoryPolicy,
251 weak_ptr_factory_.GetWeakPtr()),
252 base::TimeDelta::FromMilliseconds(kEnforceMemoryPolicyDelayMs));
255 } // namespace content