OLD | NEW |
---|---|
(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 | |
OLD | NEW |