Update Chrome code to use new WebScreenOrientation types.
[chromium-blink-merge.git] / base / memory / discardable_memory_manager.cc
blob87d327094ecf1c695f743d9fc6c4135b84392362
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"
12 #include "base/sys_info.h"
14 namespace base {
15 namespace internal {
17 namespace {
19 // This is admittedly pretty magical. It's approximately enough memory for four
20 // 2560x1600 images.
21 static const size_t kDefaultDiscardableMemoryLimit = 64 * 1024 * 1024;
22 static const size_t kDefaultBytesToKeepUnderModeratePressure =
23 kDefaultDiscardableMemoryLimit / 4;
25 } // namespace
27 DiscardableMemoryManager::DiscardableMemoryManager()
28 : allocations_(AllocationMap::NO_AUTO_EVICT),
29 bytes_allocated_(0),
30 discardable_memory_limit_(kDefaultDiscardableMemoryLimit),
31 bytes_to_keep_under_moderate_pressure_(
32 kDefaultBytesToKeepUnderModeratePressure) {
33 BytesAllocatedChanged();
36 DiscardableMemoryManager::~DiscardableMemoryManager() {
37 DCHECK(allocations_.empty());
38 DCHECK_EQ(0u, bytes_allocated_);
41 void DiscardableMemoryManager::RegisterMemoryPressureListener() {
42 AutoLock lock(lock_);
43 DCHECK(base::MessageLoop::current());
44 DCHECK(!memory_pressure_listener_);
45 memory_pressure_listener_.reset(
46 new MemoryPressureListener(
47 base::Bind(&DiscardableMemoryManager::OnMemoryPressure,
48 Unretained(this))));
51 void DiscardableMemoryManager::UnregisterMemoryPressureListener() {
52 AutoLock lock(lock_);
53 DCHECK(memory_pressure_listener_);
54 memory_pressure_listener_.reset();
57 void DiscardableMemoryManager::SetDiscardableMemoryLimit(size_t bytes) {
58 AutoLock lock(lock_);
59 discardable_memory_limit_ = bytes;
60 EnforcePolicyWithLockAcquired();
63 void DiscardableMemoryManager::SetBytesToKeepUnderModeratePressure(
64 size_t bytes) {
65 AutoLock lock(lock_);
66 bytes_to_keep_under_moderate_pressure_ = bytes;
69 void DiscardableMemoryManager::Register(
70 const DiscardableMemory* discardable, size_t bytes) {
71 AutoLock lock(lock_);
72 // A registered memory listener is currently required. This DCHECK can be
73 // moved or removed if we decide that it's useful to relax this condition.
74 // TODO(reveman): Enable this DCHECK when skia and blink are able to
75 // register memory pressure listeners. crbug.com/333907
76 // DCHECK(memory_pressure_listener_);
77 DCHECK(allocations_.Peek(discardable) == allocations_.end());
78 allocations_.Put(discardable, Allocation(bytes));
81 void DiscardableMemoryManager::Unregister(
82 const DiscardableMemory* discardable) {
83 AutoLock lock(lock_);
84 AllocationMap::iterator it = allocations_.Peek(discardable);
85 if (it == allocations_.end())
86 return;
88 if (it->second.memory) {
89 size_t bytes = it->second.bytes;
90 DCHECK_LE(bytes, bytes_allocated_);
91 bytes_allocated_ -= bytes;
92 BytesAllocatedChanged();
93 free(it->second.memory);
95 allocations_.Erase(it);
98 scoped_ptr<uint8, FreeDeleter> DiscardableMemoryManager::Acquire(
99 const DiscardableMemory* discardable,
100 bool* purged) {
101 AutoLock lock(lock_);
102 // NB: |allocations_| is an MRU cache, and use of |Get| here updates that
103 // cache.
104 AllocationMap::iterator it = allocations_.Get(discardable);
105 CHECK(it != allocations_.end());
107 if (it->second.memory) {
108 scoped_ptr<uint8, FreeDeleter> memory(it->second.memory);
109 it->second.memory = NULL;
110 *purged = false;
111 return memory.Pass();
114 size_t bytes = it->second.bytes;
115 if (!bytes)
116 return scoped_ptr<uint8, FreeDeleter>();
118 if (discardable_memory_limit_) {
119 size_t limit = 0;
120 if (bytes < discardable_memory_limit_)
121 limit = discardable_memory_limit_ - bytes;
123 PurgeLRUWithLockAcquiredUntilUsageIsWithin(limit);
126 // Check for overflow.
127 if (std::numeric_limits<size_t>::max() - bytes < bytes_allocated_)
128 return scoped_ptr<uint8, FreeDeleter>();
130 scoped_ptr<uint8, FreeDeleter> memory(static_cast<uint8*>(malloc(bytes)));
131 if (!memory)
132 return scoped_ptr<uint8, FreeDeleter>();
134 bytes_allocated_ += bytes;
135 BytesAllocatedChanged();
137 *purged = true;
138 return memory.Pass();
141 void DiscardableMemoryManager::Release(
142 const DiscardableMemory* discardable,
143 scoped_ptr<uint8, FreeDeleter> memory) {
144 AutoLock lock(lock_);
145 // NB: |allocations_| is an MRU cache, and use of |Get| here updates that
146 // cache.
147 AllocationMap::iterator it = allocations_.Get(discardable);
148 CHECK(it != allocations_.end());
150 DCHECK(!it->second.memory);
151 it->second.memory = memory.release();
153 EnforcePolicyWithLockAcquired();
156 void DiscardableMemoryManager::PurgeAll() {
157 AutoLock lock(lock_);
158 PurgeLRUWithLockAcquiredUntilUsageIsWithin(0);
161 bool DiscardableMemoryManager::IsRegisteredForTest(
162 const DiscardableMemory* discardable) const {
163 AutoLock lock(lock_);
164 AllocationMap::const_iterator it = allocations_.Peek(discardable);
165 return it != allocations_.end();
168 bool DiscardableMemoryManager::CanBePurgedForTest(
169 const DiscardableMemory* discardable) const {
170 AutoLock lock(lock_);
171 AllocationMap::const_iterator it = allocations_.Peek(discardable);
172 return it != allocations_.end() && it->second.memory;
175 size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const {
176 AutoLock lock(lock_);
177 return bytes_allocated_;
180 void DiscardableMemoryManager::OnMemoryPressure(
181 MemoryPressureListener::MemoryPressureLevel pressure_level) {
182 switch (pressure_level) {
183 case MemoryPressureListener::MEMORY_PRESSURE_MODERATE:
184 Purge();
185 return;
186 case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL:
187 PurgeAll();
188 return;
191 NOTREACHED();
194 void DiscardableMemoryManager::Purge() {
195 AutoLock lock(lock_);
197 PurgeLRUWithLockAcquiredUntilUsageIsWithin(
198 bytes_to_keep_under_moderate_pressure_);
201 void DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin(
202 size_t limit) {
203 TRACE_EVENT1(
204 "base",
205 "DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin",
206 "limit", limit);
208 lock_.AssertAcquired();
210 size_t bytes_allocated_before_purging = bytes_allocated_;
211 for (AllocationMap::reverse_iterator it = allocations_.rbegin();
212 it != allocations_.rend();
213 ++it) {
214 if (bytes_allocated_ <= limit)
215 break;
216 if (!it->second.memory)
217 continue;
219 size_t bytes = it->second.bytes;
220 DCHECK_LE(bytes, bytes_allocated_);
221 bytes_allocated_ -= bytes;
222 free(it->second.memory);
223 it->second.memory = NULL;
226 if (bytes_allocated_ != bytes_allocated_before_purging)
227 BytesAllocatedChanged();
230 void DiscardableMemoryManager::EnforcePolicyWithLockAcquired() {
231 PurgeLRUWithLockAcquiredUntilUsageIsWithin(discardable_memory_limit_);
234 void DiscardableMemoryManager::BytesAllocatedChanged() const {
235 TRACE_COUNTER_ID1("base",
236 "DiscardableMemoryUsage",
237 this,
238 bytes_allocated_);
241 } // namespace internal
242 } // namespace base