[Ozone-Gbm] Explicitly crash if trying software rendering on GBM
[chromium-blink-merge.git] / content / common / host_discardable_shared_memory_manager.cc
blobb1388f4e488217ace28a44c9854403313ecf582d
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/lazy_instance.h"
12 #include "base/numerics/safe_math.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/sys_info.h"
15 #include "base/trace_event/trace_event.h"
17 namespace content {
18 namespace {
20 base::LazyInstance<HostDiscardableSharedMemoryManager>
21 g_discardable_shared_memory_manager = LAZY_INSTANCE_INITIALIZER;
23 const int64_t kMaxDefaultMemoryLimit = 512 * 1024 * 1024;
25 const int kEnforceMemoryPolicyDelayMs = 1000;
27 } // namespace
29 HostDiscardableSharedMemoryManager::MemorySegment::MemorySegment(
30 linked_ptr<base::DiscardableSharedMemory> memory,
31 base::ProcessHandle process_handle)
32 : memory(memory), process_handle(process_handle) {
35 HostDiscardableSharedMemoryManager::MemorySegment::~MemorySegment() {
38 HostDiscardableSharedMemoryManager::HostDiscardableSharedMemoryManager()
39 : memory_limit_(
40 // Allow 25% of physical memory to be used for discardable memory.
41 std::min(base::SysInfo::AmountOfPhysicalMemory() / 4,
42 kMaxDefaultMemoryLimit)),
43 bytes_allocated_(0),
44 memory_pressure_listener_(new base::MemoryPressureListener(
45 base::Bind(&HostDiscardableSharedMemoryManager::OnMemoryPressure,
46 base::Unretained(this)))),
47 enforce_memory_policy_pending_(false),
48 weak_ptr_factory_(this) {
49 DCHECK_NE(memory_limit_, 0u);
52 HostDiscardableSharedMemoryManager::~HostDiscardableSharedMemoryManager() {
55 HostDiscardableSharedMemoryManager*
56 HostDiscardableSharedMemoryManager::current() {
57 return g_discardable_shared_memory_manager.Pointer();
60 scoped_ptr<base::DiscardableMemoryShmemChunk>
61 HostDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
62 size_t size) {
63 // TODO(reveman): Need to implement this for discardable memory support in
64 // the browser process.
65 NOTIMPLEMENTED();
66 return nullptr;
69 void HostDiscardableSharedMemoryManager::
70 AllocateLockedDiscardableSharedMemoryForChild(
71 base::ProcessHandle process_handle,
72 size_t size,
73 base::SharedMemoryHandle* shared_memory_handle) {
74 base::AutoLock lock(lock_);
76 // Memory usage must be reduced to prevent the addition of |size| from
77 // taking usage above the limit. Usage should be reduced to 0 in cases
78 // where |size| is greater than the limit.
79 size_t limit = 0;
80 // Note: the actual mapped size can be larger than requested and cause
81 // |bytes_allocated_| to temporarily be larger than |memory_limit_|. The
82 // error is minimized by incrementing |bytes_allocated_| with the actual
83 // mapped size rather than |size| below.
84 if (size < memory_limit_)
85 limit = memory_limit_ - size;
87 if (bytes_allocated_ > limit)
88 ReduceMemoryUsageUntilWithinLimit(limit);
90 linked_ptr<base::DiscardableSharedMemory> memory(
91 new base::DiscardableSharedMemory);
92 if (!memory->CreateAndMap(size)) {
93 *shared_memory_handle = base::SharedMemory::NULLHandle();
94 return;
97 if (!memory->ShareToProcess(process_handle, shared_memory_handle)) {
98 LOG(ERROR) << "Cannot share discardable memory segment";
99 *shared_memory_handle = base::SharedMemory::NULLHandle();
100 return;
103 base::CheckedNumeric<size_t> checked_bytes_allocated = bytes_allocated_;
104 checked_bytes_allocated += memory->mapped_size();
105 if (!checked_bytes_allocated.IsValid()) {
106 *shared_memory_handle = base::SharedMemory::NULLHandle();
107 return;
110 bytes_allocated_ = checked_bytes_allocated.ValueOrDie();
111 BytesAllocatedChanged(bytes_allocated_);
113 segments_.push_back(MemorySegment(memory, process_handle));
114 std::push_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
116 if (bytes_allocated_ > memory_limit_)
117 ScheduleEnforceMemoryPolicy();
120 void HostDiscardableSharedMemoryManager::ProcessRemoved(
121 base::ProcessHandle process_handle) {
122 base::AutoLock lock(lock_);
124 size_t bytes_allocated_before_purging = bytes_allocated_;
125 for (auto& segment : segments_) {
126 // Skip segments that belong to a different process.
127 if (segment.process_handle != process_handle)
128 continue;
130 size_t size = segment.memory->mapped_size();
131 DCHECK_GE(bytes_allocated_, size);
133 // This will unmap the memory segment and drop our reference. The result
134 // is that the memory will be released to the OS if the child process is
135 // no longer referencing it.
136 // Note: We intentionally leave the segment in the vector to avoid
137 // reconstructing the heap. The element will be removed from the heap
138 // when its last usage time is older than all other segments.
139 segment.memory->Close();
140 bytes_allocated_ -= size;
143 if (bytes_allocated_ != bytes_allocated_before_purging)
144 BytesAllocatedChanged(bytes_allocated_);
147 void HostDiscardableSharedMemoryManager::SetMemoryLimit(size_t limit) {
148 base::AutoLock lock(lock_);
150 memory_limit_ = limit;
151 ReduceMemoryUsageUntilWithinMemoryLimit();
154 void HostDiscardableSharedMemoryManager::EnforceMemoryPolicy() {
155 base::AutoLock lock(lock_);
157 enforce_memory_policy_pending_ = false;
158 ReduceMemoryUsageUntilWithinMemoryLimit();
161 void HostDiscardableSharedMemoryManager::OnMemoryPressure(
162 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
163 base::AutoLock lock(lock_);
165 switch (memory_pressure_level) {
166 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
167 break;
168 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
169 // Purge memory until usage is within half of |memory_limit_|.
170 ReduceMemoryUsageUntilWithinLimit(memory_limit_ / 2);
171 break;
172 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
173 // Purge everything possible when pressure is critical.
174 ReduceMemoryUsageUntilWithinLimit(0);
175 break;
179 void
180 HostDiscardableSharedMemoryManager::ReduceMemoryUsageUntilWithinMemoryLimit() {
181 if (bytes_allocated_ <= memory_limit_)
182 return;
184 ReduceMemoryUsageUntilWithinLimit(memory_limit_);
185 if (bytes_allocated_ > memory_limit_)
186 ScheduleEnforceMemoryPolicy();
189 void HostDiscardableSharedMemoryManager::ReduceMemoryUsageUntilWithinLimit(
190 size_t limit) {
191 TRACE_EVENT1("renderer_host",
192 "HostDiscardableSharedMemoryManager::"
193 "ReduceMemoryUsageUntilWithinLimit",
194 "bytes_allocated",
195 bytes_allocated_);
197 // Usage time of currently locked segments are updated to this time and
198 // we stop eviction attempts as soon as we come across a segment that we've
199 // previously tried to evict but was locked.
200 base::Time current_time = Now();
202 lock_.AssertAcquired();
203 size_t bytes_allocated_before_purging = bytes_allocated_;
204 while (!segments_.empty()) {
205 if (bytes_allocated_ <= limit)
206 break;
208 // Stop eviction attempts when the LRU segment is currently in use.
209 if (segments_.front().memory->last_known_usage() >= current_time)
210 break;
212 std::pop_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
213 MemorySegment segment = segments_.back();
214 segments_.pop_back();
216 // Attempt to purge and truncate LRU segment. When successful, as much
217 // memory as possible will be released to the OS. How much memory is
218 // released depends on the platform. The child process should perform
219 // periodic cleanup to ensure that all memory is release within a
220 // reasonable amount of time.
221 if (segment.memory->PurgeAndTruncate(current_time)) {
222 size_t size = segment.memory->mapped_size();
223 DCHECK_GE(bytes_allocated_, size);
224 bytes_allocated_ -= size;
225 continue;
228 // Add memory segment (with updated usage timestamp) back on heap after
229 // failed attempt to purge it.
230 segments_.push_back(segment);
231 std::push_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
234 if (bytes_allocated_ != bytes_allocated_before_purging)
235 BytesAllocatedChanged(bytes_allocated_);
238 void HostDiscardableSharedMemoryManager::BytesAllocatedChanged(
239 size_t new_bytes_allocated) const {
240 TRACE_COUNTER_ID1("renderer_host", "TotalDiscardableMemoryUsage", this,
241 new_bytes_allocated);
243 static const char kTotalDiscardableMemoryUsageKey[] = "total-dm-usage";
244 base::debug::SetCrashKeyValue(kTotalDiscardableMemoryUsageKey,
245 base::Uint64ToString(new_bytes_allocated));
248 base::Time HostDiscardableSharedMemoryManager::Now() const {
249 return base::Time::Now();
252 void HostDiscardableSharedMemoryManager::ScheduleEnforceMemoryPolicy() {
253 if (enforce_memory_policy_pending_)
254 return;
256 enforce_memory_policy_pending_ = true;
257 base::MessageLoop::current()->PostDelayedTask(
258 FROM_HERE,
259 base::Bind(&HostDiscardableSharedMemoryManager::EnforceMemoryPolicy,
260 weak_ptr_factory_.GetWeakPtr()),
261 base::TimeDelta::FromMilliseconds(kEnforceMemoryPolicyDelayMs));
264 } // namespace content