Add a string for translation.
[chromium-blink-merge.git] / base / memory / discardable_memory_manager.cc
blob1dff3e4da0ec396a8393de9d1607ad49dd04241c
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 "base/memory/discardable_memory_manager.h"
7 #include "base/bind.h"
8 #include "base/containers/hash_tables.h"
9 #include "base/containers/mru_cache.h"
10 #include "base/debug/trace_event.h"
11 #include "base/synchronization/lock.h"
13 namespace base {
14 namespace internal {
15 namespace {
17 // This is admittedly pretty magical. It's approximately enough memory for four
18 // 2560x1600 images.
19 static const size_t kDefaultMemoryLimit = 64 * 1024 * 1024;
20 static const size_t kDefaultBytesToKeepUnderModeratePressure =
21 kDefaultMemoryLimit / 4;
23 } // namespace
25 DiscardableMemoryManager::DiscardableMemoryManager()
26 : allocations_(AllocationMap::NO_AUTO_EVICT),
27 bytes_allocated_(0),
28 memory_limit_(kDefaultMemoryLimit),
29 bytes_to_keep_under_moderate_pressure_(
30 kDefaultBytesToKeepUnderModeratePressure) {
31 BytesAllocatedChanged();
34 DiscardableMemoryManager::~DiscardableMemoryManager() {
35 DCHECK(allocations_.empty());
36 DCHECK_EQ(0u, bytes_allocated_);
39 void DiscardableMemoryManager::RegisterMemoryPressureListener() {
40 AutoLock lock(lock_);
41 DCHECK(base::MessageLoop::current());
42 DCHECK(!memory_pressure_listener_);
43 memory_pressure_listener_.reset(new MemoryPressureListener(base::Bind(
44 &DiscardableMemoryManager::OnMemoryPressure, Unretained(this))));
47 void DiscardableMemoryManager::UnregisterMemoryPressureListener() {
48 AutoLock lock(lock_);
49 DCHECK(memory_pressure_listener_);
50 memory_pressure_listener_.reset();
53 void DiscardableMemoryManager::SetMemoryLimit(size_t bytes) {
54 AutoLock lock(lock_);
55 memory_limit_ = bytes;
56 EnforcePolicyWithLockAcquired();
59 void DiscardableMemoryManager::SetBytesToKeepUnderModeratePressure(
60 size_t bytes) {
61 AutoLock lock(lock_);
62 bytes_to_keep_under_moderate_pressure_ = bytes;
65 void DiscardableMemoryManager::Register(Allocation* allocation, size_t bytes) {
66 AutoLock lock(lock_);
67 // A registered memory listener is currently required. This DCHECK can be
68 // moved or removed if we decide that it's useful to relax this condition.
69 // TODO(reveman): Enable this DCHECK when skia and blink are able to
70 // register memory pressure listeners. crbug.com/333907
71 // DCHECK(memory_pressure_listener_);
72 DCHECK(allocations_.Peek(allocation) == allocations_.end());
73 allocations_.Put(allocation, AllocationInfo(bytes));
76 void DiscardableMemoryManager::Unregister(Allocation* allocation) {
77 AutoLock lock(lock_);
78 AllocationMap::iterator it = allocations_.Peek(allocation);
79 DCHECK(it != allocations_.end());
80 const AllocationInfo& info = it->second;
82 if (info.purgable) {
83 size_t bytes_purgable = info.bytes;
84 DCHECK_LE(bytes_purgable, bytes_allocated_);
85 bytes_allocated_ -= bytes_purgable;
86 BytesAllocatedChanged();
88 allocations_.Erase(it);
91 bool DiscardableMemoryManager::AcquireLock(Allocation* allocation,
92 bool* purged) {
93 AutoLock lock(lock_);
94 // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
95 // cache.
96 AllocationMap::iterator it = allocations_.Get(allocation);
97 DCHECK(it != allocations_.end());
98 AllocationInfo* info = &it->second;
100 if (!info->bytes)
101 return false;
103 size_t bytes_required = info->purgable ? 0u : info->bytes;
105 if (memory_limit_) {
106 size_t limit = 0;
107 if (bytes_required < memory_limit_)
108 limit = memory_limit_ - bytes_required;
110 PurgeLRUWithLockAcquiredUntilUsageIsWithin(limit);
113 // Check for overflow.
114 if (std::numeric_limits<size_t>::max() - bytes_required < bytes_allocated_)
115 return false;
117 *purged = !allocation->AllocateAndAcquireLock(info->bytes);
118 info->purgable = false;
119 if (bytes_required) {
120 bytes_allocated_ += bytes_required;
121 BytesAllocatedChanged();
123 return true;
126 void DiscardableMemoryManager::ReleaseLock(Allocation* allocation) {
127 AutoLock lock(lock_);
128 // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
129 // cache.
130 AllocationMap::iterator it = allocations_.Get(allocation);
131 DCHECK(it != allocations_.end());
132 AllocationInfo* info = &it->second;
134 allocation->ReleaseLock();
135 info->purgable = true;
136 EnforcePolicyWithLockAcquired();
139 void DiscardableMemoryManager::PurgeAll() {
140 AutoLock lock(lock_);
141 PurgeLRUWithLockAcquiredUntilUsageIsWithin(0);
144 bool DiscardableMemoryManager::IsRegisteredForTest(
145 Allocation* allocation) const {
146 AutoLock lock(lock_);
147 AllocationMap::const_iterator it = allocations_.Peek(allocation);
148 return it != allocations_.end();
151 bool DiscardableMemoryManager::CanBePurgedForTest(
152 Allocation* allocation) const {
153 AutoLock lock(lock_);
154 AllocationMap::const_iterator it = allocations_.Peek(allocation);
155 return it != allocations_.end() && it->second.purgable;
158 size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const {
159 AutoLock lock(lock_);
160 return bytes_allocated_;
163 void DiscardableMemoryManager::OnMemoryPressure(
164 MemoryPressureListener::MemoryPressureLevel pressure_level) {
165 switch (pressure_level) {
166 case MemoryPressureListener::MEMORY_PRESSURE_MODERATE:
167 Purge();
168 return;
169 case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL:
170 PurgeAll();
171 return;
174 NOTREACHED();
177 void DiscardableMemoryManager::Purge() {
178 AutoLock lock(lock_);
180 PurgeLRUWithLockAcquiredUntilUsageIsWithin(
181 bytes_to_keep_under_moderate_pressure_);
184 void DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin(
185 size_t limit) {
186 TRACE_EVENT1(
187 "base",
188 "DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin",
189 "limit",
190 limit);
192 lock_.AssertAcquired();
194 size_t bytes_allocated_before_purging = bytes_allocated_;
195 for (AllocationMap::reverse_iterator it = allocations_.rbegin();
196 it != allocations_.rend();
197 ++it) {
198 Allocation* allocation = it->first;
199 AllocationInfo* info = &it->second;
201 if (bytes_allocated_ <= limit)
202 break;
203 if (!info->purgable)
204 continue;
206 size_t bytes_purgable = info->bytes;
207 DCHECK_LE(bytes_purgable, bytes_allocated_);
208 bytes_allocated_ -= bytes_purgable;
209 info->purgable = false;
210 allocation->Purge();
213 if (bytes_allocated_ != bytes_allocated_before_purging)
214 BytesAllocatedChanged();
217 void DiscardableMemoryManager::EnforcePolicyWithLockAcquired() {
218 PurgeLRUWithLockAcquiredUntilUsageIsWithin(memory_limit_);
221 void DiscardableMemoryManager::BytesAllocatedChanged() const {
222 TRACE_COUNTER_ID1("base", "DiscardableMemoryUsage", this, bytes_allocated_);
225 } // namespace internal
226 } // namespace base