Refactors gesture conversion functions to ui/events/blink
[chromium-blink-merge.git] / content / common / host_discardable_shared_memory_manager.cc
blob4c5c220f6006585511278238fc2a3b6650e94102
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/profiler/scoped_tracker.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/sys_info.h"
19 #include "base/trace_event/trace_event.h"
21 namespace content {
22 namespace {
24 class DiscardableMemoryImpl : public base::DiscardableMemory {
25 public:
26 DiscardableMemoryImpl(scoped_ptr<base::DiscardableSharedMemory> shared_memory,
27 const base::Closure& deleted_callback)
28 : shared_memory_(shared_memory.Pass()),
29 deleted_callback_(deleted_callback),
30 is_locked_(true) {}
32 ~DiscardableMemoryImpl() override {
33 if (is_locked_)
34 shared_memory_->Unlock(0, 0);
36 deleted_callback_.Run();
39 // Overridden from base::DiscardableMemory:
40 bool Lock() override {
41 DCHECK(!is_locked_);
43 if (shared_memory_->Lock(0, 0) != base::DiscardableSharedMemory::SUCCESS)
44 return false;
46 is_locked_ = true;
47 return true;
49 void Unlock() override {
50 DCHECK(is_locked_);
52 shared_memory_->Unlock(0, 0);
53 is_locked_ = false;
55 void* Memory() const override {
56 DCHECK(is_locked_);
57 return shared_memory_->memory();
60 private:
61 scoped_ptr<base::DiscardableSharedMemory> shared_memory_;
62 const base::Closure deleted_callback_;
63 bool is_locked_;
65 DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryImpl);
68 base::LazyInstance<HostDiscardableSharedMemoryManager>
69 g_discardable_shared_memory_manager = LAZY_INSTANCE_INITIALIZER;
71 const int64_t kMaxDefaultMemoryLimit = 512 * 1024 * 1024;
73 const int kEnforceMemoryPolicyDelayMs = 1000;
75 // Global atomic to generate unique discardable shared memory IDs.
76 base::StaticAtomicSequenceNumber g_next_discardable_shared_memory_id;
78 } // namespace
80 HostDiscardableSharedMemoryManager::MemorySegment::MemorySegment(
81 scoped_ptr<base::DiscardableSharedMemory> memory)
82 : memory_(memory.Pass()) {
85 HostDiscardableSharedMemoryManager::MemorySegment::~MemorySegment() {
88 HostDiscardableSharedMemoryManager::HostDiscardableSharedMemoryManager()
89 : memory_limit_(
90 // Allow 25% of physical memory to be used for discardable memory.
91 std::min(base::SysInfo::AmountOfPhysicalMemory() / 4,
92 kMaxDefaultMemoryLimit)),
93 bytes_allocated_(0),
94 memory_pressure_listener_(new base::MemoryPressureListener(
95 base::Bind(&HostDiscardableSharedMemoryManager::OnMemoryPressure,
96 base::Unretained(this)))),
97 enforce_memory_policy_pending_(false),
98 weak_ptr_factory_(this) {
99 DCHECK_NE(memory_limit_, 0u);
102 HostDiscardableSharedMemoryManager::~HostDiscardableSharedMemoryManager() {
105 HostDiscardableSharedMemoryManager*
106 HostDiscardableSharedMemoryManager::current() {
107 return g_discardable_shared_memory_manager.Pointer();
110 scoped_ptr<base::DiscardableMemory>
111 HostDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
112 size_t size) {
113 DiscardableSharedMemoryId new_id =
114 g_next_discardable_shared_memory_id.GetNext();
115 base::ProcessHandle current_process_handle = base::GetCurrentProcessHandle();
117 // Note: Use DiscardableSharedMemoryHeap for in-process allocation
118 // of discardable memory if the cost of each allocation is too high.
119 base::SharedMemoryHandle handle;
120 AllocateLockedDiscardableSharedMemory(current_process_handle, size, new_id,
121 &handle);
122 CHECK(base::SharedMemory::IsHandleValid(handle));
123 scoped_ptr<base::DiscardableSharedMemory> memory(
124 new base::DiscardableSharedMemory(handle));
125 CHECK(memory->Map(size));
126 return make_scoped_ptr(new DiscardableMemoryImpl(
127 memory.Pass(),
128 base::Bind(
129 &HostDiscardableSharedMemoryManager::DeletedDiscardableSharedMemory,
130 base::Unretained(this), new_id, current_process_handle)));
133 void HostDiscardableSharedMemoryManager::
134 AllocateLockedDiscardableSharedMemoryForChild(
135 base::ProcessHandle process_handle,
136 size_t size,
137 DiscardableSharedMemoryId id,
138 base::SharedMemoryHandle* shared_memory_handle) {
139 AllocateLockedDiscardableSharedMemory(process_handle, size, id,
140 shared_memory_handle);
143 void HostDiscardableSharedMemoryManager::ChildDeletedDiscardableSharedMemory(
144 DiscardableSharedMemoryId id,
145 base::ProcessHandle process_handle) {
146 DeletedDiscardableSharedMemory(id, process_handle);
149 void HostDiscardableSharedMemoryManager::ProcessRemoved(
150 base::ProcessHandle process_handle) {
151 base::AutoLock lock(lock_);
153 ProcessMap::iterator process_it = processes_.find(process_handle);
154 if (process_it == processes_.end())
155 return;
157 size_t bytes_allocated_before_releasing_memory = bytes_allocated_;
159 for (auto& segment_it : process_it->second)
160 ReleaseMemory(segment_it.second->memory());
162 processes_.erase(process_it);
164 if (bytes_allocated_ != bytes_allocated_before_releasing_memory)
165 BytesAllocatedChanged(bytes_allocated_);
168 void HostDiscardableSharedMemoryManager::SetMemoryLimit(size_t limit) {
169 base::AutoLock lock(lock_);
171 memory_limit_ = limit;
172 ReduceMemoryUsageUntilWithinMemoryLimit();
175 void HostDiscardableSharedMemoryManager::EnforceMemoryPolicy() {
176 base::AutoLock lock(lock_);
178 enforce_memory_policy_pending_ = false;
179 ReduceMemoryUsageUntilWithinMemoryLimit();
182 size_t HostDiscardableSharedMemoryManager::GetBytesAllocated() {
183 base::AutoLock lock(lock_);
185 return bytes_allocated_;
188 void HostDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
189 base::ProcessHandle process_handle,
190 size_t size,
191 DiscardableSharedMemoryId id,
192 base::SharedMemoryHandle* shared_memory_handle) {
193 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405
194 // is fixed.
195 tracked_objects::ScopedTracker tracking_profile1(
196 FROM_HERE_WITH_EXPLICIT_FUNCTION(
197 "466405 AllocateLockedDiscardableSharedMemory::Start"));
198 base::AutoLock lock(lock_);
200 // Make sure |id| is not already in use.
201 MemorySegmentMap& process_segments = processes_[process_handle];
202 if (process_segments.find(id) != process_segments.end()) {
203 LOG(ERROR) << "Invalid discardable shared memory ID";
204 *shared_memory_handle = base::SharedMemory::NULLHandle();
205 return;
208 // Memory usage must be reduced to prevent the addition of |size| from
209 // taking usage above the limit. Usage should be reduced to 0 in cases
210 // where |size| is greater than the limit.
211 size_t limit = 0;
212 // Note: the actual mapped size can be larger than requested and cause
213 // |bytes_allocated_| to temporarily be larger than |memory_limit_|. The
214 // error is minimized by incrementing |bytes_allocated_| with the actual
215 // mapped size rather than |size| below.
216 if (size < memory_limit_)
217 limit = memory_limit_ - size;
219 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405
220 // is fixed.
221 tracked_objects::ScopedTracker tracking_profile2(
222 FROM_HERE_WITH_EXPLICIT_FUNCTION(
223 "466405 AllocateLockedDiscardableSharedMemory::ReduceMemoryUsage"));
224 if (bytes_allocated_ > limit)
225 ReduceMemoryUsageUntilWithinLimit(limit);
227 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405
228 // is fixed.
229 tracked_objects::ScopedTracker tracking_profile3(
230 FROM_HERE_WITH_EXPLICIT_FUNCTION(
231 "466405 AllocateLockedDiscardableSharedMemory::NewMemory"));
232 scoped_ptr<base::DiscardableSharedMemory> memory(
233 new base::DiscardableSharedMemory);
234 if (!memory->CreateAndMap(size)) {
235 *shared_memory_handle = base::SharedMemory::NULLHandle();
236 return;
239 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405
240 // is fixed.
241 tracked_objects::ScopedTracker tracking_profile4(
242 FROM_HERE_WITH_EXPLICIT_FUNCTION(
243 "466405 AllocateLockedDiscardableSharedMemory::ShareToProcess"));
244 if (!memory->ShareToProcess(process_handle, shared_memory_handle)) {
245 LOG(ERROR) << "Cannot share discardable memory segment";
246 *shared_memory_handle = base::SharedMemory::NULLHandle();
247 return;
250 base::CheckedNumeric<size_t> checked_bytes_allocated = bytes_allocated_;
251 checked_bytes_allocated += memory->mapped_size();
252 if (!checked_bytes_allocated.IsValid()) {
253 *shared_memory_handle = base::SharedMemory::NULLHandle();
254 return;
257 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405
258 // is fixed.
259 tracked_objects::ScopedTracker tracking_profile5(
260 FROM_HERE_WITH_EXPLICIT_FUNCTION(
261 "466405 "
262 "AllocateLockedDiscardableSharedMemory::BytesAllocatedChanged"));
263 bytes_allocated_ = checked_bytes_allocated.ValueOrDie();
264 BytesAllocatedChanged(bytes_allocated_);
266 scoped_refptr<MemorySegment> segment(new MemorySegment(memory.Pass()));
267 process_segments[id] = segment.get();
268 segments_.push_back(segment.get());
269 std::push_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
271 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405
272 // is fixed.
273 tracked_objects::ScopedTracker tracking_profile6(
274 FROM_HERE_WITH_EXPLICIT_FUNCTION(
275 "466405 "
276 "AllocateLockedDiscardableSharedMemory::"
277 "ScheduleEnforceMemoryPolicy"));
278 if (bytes_allocated_ > memory_limit_)
279 ScheduleEnforceMemoryPolicy();
282 void HostDiscardableSharedMemoryManager::DeletedDiscardableSharedMemory(
283 DiscardableSharedMemoryId id,
284 base::ProcessHandle process_handle) {
285 base::AutoLock lock(lock_);
287 MemorySegmentMap& process_segments = processes_[process_handle];
289 MemorySegmentMap::iterator segment_it = process_segments.find(id);
290 if (segment_it == process_segments.end()) {
291 LOG(ERROR) << "Invalid discardable shared memory ID";
292 return;
295 size_t bytes_allocated_before_releasing_memory = bytes_allocated_;
297 ReleaseMemory(segment_it->second->memory());
299 process_segments.erase(segment_it);
301 if (bytes_allocated_ != bytes_allocated_before_releasing_memory)
302 BytesAllocatedChanged(bytes_allocated_);
305 void HostDiscardableSharedMemoryManager::OnMemoryPressure(
306 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
307 base::AutoLock lock(lock_);
309 switch (memory_pressure_level) {
310 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
311 break;
312 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
313 // Purge memory until usage is within half of |memory_limit_|.
314 ReduceMemoryUsageUntilWithinLimit(memory_limit_ / 2);
315 break;
316 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
317 // Purge everything possible when pressure is critical.
318 ReduceMemoryUsageUntilWithinLimit(0);
319 break;
323 void
324 HostDiscardableSharedMemoryManager::ReduceMemoryUsageUntilWithinMemoryLimit() {
325 lock_.AssertAcquired();
327 if (bytes_allocated_ <= memory_limit_)
328 return;
330 ReduceMemoryUsageUntilWithinLimit(memory_limit_);
331 if (bytes_allocated_ > memory_limit_)
332 ScheduleEnforceMemoryPolicy();
335 void HostDiscardableSharedMemoryManager::ReduceMemoryUsageUntilWithinLimit(
336 size_t limit) {
337 TRACE_EVENT1("renderer_host",
338 "HostDiscardableSharedMemoryManager::"
339 "ReduceMemoryUsageUntilWithinLimit",
340 "bytes_allocated",
341 bytes_allocated_);
343 // Usage time of currently locked segments are updated to this time and
344 // we stop eviction attempts as soon as we come across a segment that we've
345 // previously tried to evict but was locked.
346 base::Time current_time = Now();
348 lock_.AssertAcquired();
349 size_t bytes_allocated_before_purging = bytes_allocated_;
350 while (!segments_.empty()) {
351 if (bytes_allocated_ <= limit)
352 break;
354 // Stop eviction attempts when the LRU segment is currently in use.
355 if (segments_.front()->memory()->last_known_usage() >= current_time)
356 break;
358 std::pop_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
359 scoped_refptr<MemorySegment> segment = segments_.back();
360 segments_.pop_back();
362 // Attempt to purge LRU segment. When successful, released the memory.
363 if (segment->memory()->Purge(current_time)) {
364 ReleaseMemory(segment->memory());
365 continue;
368 // Add memory segment (with updated usage timestamp) back on heap after
369 // failed attempt to purge it.
370 segments_.push_back(segment.get());
371 std::push_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
374 if (bytes_allocated_ != bytes_allocated_before_purging)
375 BytesAllocatedChanged(bytes_allocated_);
378 void HostDiscardableSharedMemoryManager::ReleaseMemory(
379 base::DiscardableSharedMemory* memory) {
380 lock_.AssertAcquired();
382 size_t size = memory->mapped_size();
383 DCHECK_GE(bytes_allocated_, size);
384 bytes_allocated_ -= size;
386 #if defined(DISCARDABLE_SHARED_MEMORY_SHRINKING)
387 // Shrink memory segment. This will immediately release the memory to the OS.
388 memory->Shrink();
389 #endif
391 // This will unmap the memory segment and drop our reference. The result
392 // is that the memory will be released to the OS if the child process is
393 // no longer referencing it.
394 // Note: We intentionally leave the segment in the |segments| vector to
395 // avoid reconstructing the heap. The element will be removed from the heap
396 // when its last usage time is older than all other segments.
397 memory->Close();
400 void HostDiscardableSharedMemoryManager::BytesAllocatedChanged(
401 size_t new_bytes_allocated) const {
402 TRACE_COUNTER1("renderer_host", "TotalDiscardableMemoryUsage",
403 new_bytes_allocated);
405 static const char kTotalDiscardableMemoryAllocatedKey[] =
406 "total-discardable-memory-allocated";
407 base::debug::SetCrashKeyValue(kTotalDiscardableMemoryAllocatedKey,
408 base::Uint64ToString(new_bytes_allocated));
411 base::Time HostDiscardableSharedMemoryManager::Now() const {
412 return base::Time::Now();
415 void HostDiscardableSharedMemoryManager::ScheduleEnforceMemoryPolicy() {
416 lock_.AssertAcquired();
418 if (enforce_memory_policy_pending_)
419 return;
421 enforce_memory_policy_pending_ = true;
422 base::MessageLoop::current()->PostDelayedTask(
423 FROM_HERE,
424 base::Bind(&HostDiscardableSharedMemoryManager::EnforceMemoryPolicy,
425 weak_ptr_factory_.GetWeakPtr()),
426 base::TimeDelta::FromMilliseconds(kEnforceMemoryPolicyDelayMs));
429 } // namespace content