Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(314)

Side by Side Diff: base/memory/discardable_memory_provider.cc

Issue 17106004: Add discardable memory emulation for non-android/mac platforms (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: s/ASSERT_/EXPECT_/ Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2013 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.
4
5 #include "base/memory/discardable_memory_provider.h"
6
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/memory/discardable_memory.h"
12 #include "base/synchronization/lock.h"
13 #include "base/sys_info.h"
14
15 namespace base {
16 namespace internal {
17
18 namespace {
19
20 // If this is given a valid value via SetInstanceForTest, this pointer will be
21 // returned by GetInstance rather than the usual singleton.
22 static DiscardableMemoryProvider* s_provider_for_test = NULL;
23
24 // This is admittedly pretty magical. It's approximately enough memory for two
25 // 2560x1600 images.
26 static const size_t kDefaultDiscardableMemoryLimit = 32 * 1024 * 1024;
27 static const size_t kDefaultBytesToReclaimUnderModeratePressure =
28 kDefaultDiscardableMemoryLimit / 2;
29
30 } // namespace
31
32 DiscardableMemoryProvider::DiscardableMemoryProvider()
33 : allocations_(AllocationMap::NO_AUTO_EVICT),
34 bytes_allocated_(0),
35 discardable_memory_limit_(kDefaultDiscardableMemoryLimit),
36 bytes_to_reclaim_under_moderate_pressure_(
37 kDefaultBytesToReclaimUnderModeratePressure),
38 enforcing_policy_(false),
39 memory_pressure_listener_(
40 base::Bind(&DiscardableMemoryProvider::NotifyMemoryPressure)) {
41 }
42
43 DiscardableMemoryProvider::~DiscardableMemoryProvider() {
44 DCHECK_EQ(0u, allocations_.size());
45 DCHECK_EQ(0u, bytes_allocated_);
46 }
47
48 // static
49 DiscardableMemoryProvider* DiscardableMemoryProvider::GetInstance() {
50 if (s_provider_for_test)
51 return s_provider_for_test;
52 return Singleton<DiscardableMemoryProvider>::get();
willchan no longer on Chromium 2013/10/17 02:06:13 WDYT about using a LeakyLazyInstance instead? Is t
reveman 2013/10/19 21:17:04 Not really. I guess we need to use a LeakyLazyInst
willchan no longer on Chromium 2013/10/21 18:30:27 Not deleting on shutdown is preferable. It avoids
reveman 2013/10/22 00:11:41 Thanks for the explanation. That makes sense. It's
53 }
54
55 // static
56 void DiscardableMemoryProvider::SetInstanceForTest(
57 DiscardableMemoryProvider* provider) {
58 s_provider_for_test = provider;
59 }
60
61 // static
62 void DiscardableMemoryProvider::NotifyMemoryPressure(
63 MemoryPressureListener::MemoryPressureLevel pressure_level) {
64 switch (pressure_level) {
65 case MemoryPressureListener::MEMORY_PRESSURE_MODERATE:
66 DiscardableMemoryProvider::GetInstance()->Purge();
67 return;
68 case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL:
69 DiscardableMemoryProvider::GetInstance()->PurgeAll();
70 return;
71 }
72
73 NOTREACHED();
74 }
75
76 void DiscardableMemoryProvider::SetDiscardableMemoryLimit(size_t bytes) {
77 {
78 AutoLock lock(bytes_allocated_lock_);
79 discardable_memory_limit_ = bytes;
80 }
81 EnforcePolicy();
82 }
83
84 void DiscardableMemoryProvider::SetBytesToReclaimUnderModeratePressure(
85 size_t bytes) {
86 {
87 AutoLock lock(bytes_allocated_lock_);
88 bytes_to_reclaim_under_moderate_pressure_ = bytes;
89 }
90 EnforcePolicy();
91 }
92
93 void DiscardableMemoryProvider::Register(
94 const DiscardableMemory* discardable, size_t bytes) {
95 AutoLock lock(allocations_lock_);
96 DCHECK(allocations_.Peek(discardable) == allocations_.end());
97 allocations_.Put(discardable, Allocation(bytes));
98 }
99
100 void DiscardableMemoryProvider::Unregister(
101 const DiscardableMemory* discardable) {
102 AutoLock lock(allocations_lock_);
willchan no longer on Chromium 2013/10/17 02:06:13 You acquire this lock here first. Later on, you ma
reveman 2013/10/19 21:17:04 Yes, the use of multiple locks here is broken. Red
103 AllocationMap::iterator it = allocations_.Peek(discardable);
104 if (it == allocations_.end())
105 return;
106
107 if (it->second.memory) {
108 AutoLock bytes_lock(bytes_allocated_lock_);
109 size_t bytes = it->second.bytes;
110 DCHECK_LE(bytes, bytes_allocated_);
111 bytes_allocated_ -= bytes;
112 free(it->second.memory);
113 }
114 allocations_.Erase(it);
115 }
116
117 scoped_ptr<uint8, FreeDeleter> DiscardableMemoryProvider::Acquire(
118 const DiscardableMemory* discardable,
119 bool* purged) {
120 AutoLock lock(allocations_lock_);
121 // NB: |allocations_| is an MRU cache, and use of |Get| here updates that
122 // cache.
123 AllocationMap::iterator it = allocations_.Get(discardable);
124 CHECK(it != allocations_.end());
125
126 if (it->second.memory) {
127 scoped_ptr<uint8, FreeDeleter> memory(it->second.memory);
128 it->second.memory = NULL;
129 *purged = false;
130 return memory.Pass();
131 }
132
133 size_t bytes = it->second.bytes;
134 if (!bytes)
135 return scoped_ptr<uint8, FreeDeleter>();
136
137 AutoLock bytes_lock(bytes_allocated_lock_);
138
139 if (discardable_memory_limit_) {
140 if (bytes > discardable_memory_limit_)
141 return scoped_ptr<uint8, FreeDeleter>();
142
143 size_t limit = discardable_memory_limit_ - bytes;
144 PurgeLRU(limit);
145
146 if (bytes_allocated_ > limit)
147 return scoped_ptr<uint8, FreeDeleter>();
148 }
149
150 bytes_allocated_ += bytes;
151 *purged = true;
152 return scoped_ptr<uint8, FreeDeleter>(static_cast<uint8*>(malloc(bytes)));
153 }
154
155 void DiscardableMemoryProvider::Release(
156 const DiscardableMemory* discardable,
157 scoped_ptr<uint8, FreeDeleter> memory) {
158 {
159 AutoLock lock(allocations_lock_);
160 // NB: |allocations_| is an MRU cache, and use of |Get| here updates that
161 // cache.
162 AllocationMap::iterator it = allocations_.Get(discardable);
163 CHECK(it != allocations_.end());
164
165 DCHECK(!it->second.memory);
166 it->second.memory = memory.release();
167 }
168
169 EnforcePolicy();
170 }
171
172 bool DiscardableMemoryProvider::IsRegisteredForTest(
173 const DiscardableMemory* discardable) {
174 AutoLock lock(allocations_lock_);
175 AllocationMap::iterator it = allocations_.Peek(discardable);
176 return it != allocations_.end();
177 }
178
179 bool DiscardableMemoryProvider::CanBePurgedForTest(
180 const DiscardableMemory* discardable) {
181 AutoLock lock(allocations_lock_);
182 AllocationMap::iterator it = allocations_.Peek(discardable);
183 return it != allocations_.end() && it->second.memory;
184 }
185
186 size_t DiscardableMemoryProvider::GetBytesAllocatedForTest() {
187 AutoLock lock(bytes_allocated_lock_);
188 return bytes_allocated_;
189 }
190
191 void DiscardableMemoryProvider::PurgeLRU(size_t limit) {
192 TRACE_EVENT1("base", "DiscardableMemoryProvider::PurgeLRU", "limit", limit);
193
194 allocations_lock_.AssertAcquired();
195 bytes_allocated_lock_.AssertAcquired();
196
197 for (AllocationMap::reverse_iterator it = allocations_.rbegin();
198 it != allocations_.rend();
199 ++it) {
200 if (bytes_allocated_ <= limit)
201 break;
202 if (!it->second.memory)
203 continue;
204
205 size_t bytes = it->second.bytes;
206 DCHECK_LE(bytes, bytes_allocated_);
207 bytes_allocated_ -= bytes;
208 free(it->second.memory);
209 it->second.memory = NULL;
210 }
211 }
212
213 void DiscardableMemoryProvider::Purge() {
214 size_t limit = 0;
215 AutoLock bytes_lock(bytes_allocated_lock_);
216 if (bytes_to_reclaim_under_moderate_pressure_ == 0)
217 return;
218
219 if (bytes_to_reclaim_under_moderate_pressure_ < discardable_memory_limit_)
220 limit = bytes_allocated_ - bytes_to_reclaim_under_moderate_pressure_;
221
222 AutoLock lock(allocations_lock_);
223 PurgeLRU(limit);
224 }
225
226 void DiscardableMemoryProvider::PurgeAll() {
227 AutoLock bytes_lock(bytes_allocated_lock_);
228 AutoLock lock(allocations_lock_);
229 PurgeLRU(0);
230 }
231
232 void DiscardableMemoryProvider::EnforcePolicy() {
233 {
234 AutoLock lock(bytes_allocated_lock_);
235 if (enforcing_policy_)
236 return;
237 }
238
239 bool exceeded_bound = false;
240 {
241 AutoLock lock(bytes_allocated_lock_);
242 enforcing_policy_ = true;
243 if (discardable_memory_limit_ == 0) {
244 enforcing_policy_ = false;
245 return;
246 }
247 exceeded_bound = bytes_allocated_ > discardable_memory_limit_;
248 }
249
250 if (exceeded_bound)
251 Purge();
252
253 {
254 AutoLock lock(bytes_allocated_lock_);
255 enforcing_policy_ = false;
256 }
257 }
258
259 } // namespace internal
260 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698