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 memory_pressure_listener_( | |
39 base::Bind(&DiscardableMemoryProvider::NotifyMemoryPressure)) { | |
40 } | |
41 | |
42 DiscardableMemoryProvider::~DiscardableMemoryProvider() { | |
43 DCHECK_EQ(0u, allocations_.size()); | |
willchan no longer on Chromium
2013/10/21 18:30:27
DCHECK(allocations_.empty()); size() is linear in
reveman
2013/10/22 00:11:42
Done.
| |
44 DCHECK_EQ(0u, bytes_allocated_); | |
45 } | |
46 | |
47 // static | |
48 DiscardableMemoryProvider* DiscardableMemoryProvider::GetInstance() { | |
49 if (s_provider_for_test) | |
50 return s_provider_for_test; | |
51 return Singleton<DiscardableMemoryProvider>::get(); | |
52 } | |
53 | |
54 // static | |
55 void DiscardableMemoryProvider::SetInstanceForTest( | |
56 DiscardableMemoryProvider* provider) { | |
57 s_provider_for_test = provider; | |
58 } | |
59 | |
60 // static | |
61 void DiscardableMemoryProvider::NotifyMemoryPressure( | |
62 MemoryPressureListener::MemoryPressureLevel pressure_level) { | |
63 switch (pressure_level) { | |
64 case MemoryPressureListener::MEMORY_PRESSURE_MODERATE: | |
65 DiscardableMemoryProvider::GetInstance()->Purge(); | |
66 return; | |
67 case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL: | |
68 DiscardableMemoryProvider::GetInstance()->PurgeAll(); | |
69 return; | |
70 } | |
71 | |
72 NOTREACHED(); | |
73 } | |
74 | |
75 void DiscardableMemoryProvider::SetDiscardableMemoryLimit(size_t bytes) { | |
76 AutoLock lock(lock_); | |
77 discardable_memory_limit_ = bytes; | |
78 EnforcePolicy(); | |
79 } | |
80 | |
81 void DiscardableMemoryProvider::SetBytesToReclaimUnderModeratePressure( | |
82 size_t bytes) { | |
83 AutoLock lock(lock_); | |
84 bytes_to_reclaim_under_moderate_pressure_ = bytes; | |
85 EnforcePolicy(); | |
86 } | |
87 | |
88 void DiscardableMemoryProvider::Register( | |
89 const DiscardableMemory* discardable, size_t bytes) { | |
90 AutoLock lock(lock_); | |
91 DCHECK(allocations_.Peek(discardable) == allocations_.end()); | |
92 allocations_.Put(discardable, Allocation(bytes)); | |
93 } | |
94 | |
95 void DiscardableMemoryProvider::Unregister( | |
96 const DiscardableMemory* discardable) { | |
97 AutoLock lock(lock_); | |
98 AllocationMap::iterator it = allocations_.Peek(discardable); | |
99 if (it == allocations_.end()) | |
100 return; | |
101 | |
102 if (it->second.memory) { | |
103 size_t bytes = it->second.bytes; | |
104 DCHECK_LE(bytes, bytes_allocated_); | |
105 bytes_allocated_ -= bytes; | |
106 free(it->second.memory); | |
107 } | |
108 allocations_.Erase(it); | |
109 } | |
110 | |
111 scoped_ptr<uint8, FreeDeleter> DiscardableMemoryProvider::Acquire( | |
112 const DiscardableMemory* discardable, | |
113 bool* purged) { | |
114 AutoLock lock(lock_); | |
115 // NB: |allocations_| is an MRU cache, and use of |Get| here updates that | |
116 // cache. | |
117 AllocationMap::iterator it = allocations_.Get(discardable); | |
118 CHECK(it != allocations_.end()); | |
119 | |
120 if (it->second.memory) { | |
121 scoped_ptr<uint8, FreeDeleter> memory(it->second.memory); | |
122 it->second.memory = NULL; | |
123 *purged = false; | |
124 return memory.Pass(); | |
125 } | |
126 | |
127 size_t bytes = it->second.bytes; | |
128 if (!bytes) | |
129 return scoped_ptr<uint8, FreeDeleter>(); | |
130 | |
131 if (discardable_memory_limit_) { | |
132 if (bytes > discardable_memory_limit_) | |
willchan no longer on Chromium
2013/10/21 21:29:12
I'm confused. The comment for this variable says:
reveman
2013/10/22 00:11:42
Talked to vollick@ and I don't think we should hav
| |
133 return scoped_ptr<uint8, FreeDeleter>(); | |
134 | |
135 size_t limit = discardable_memory_limit_ - bytes; | |
136 PurgeLRU(limit); | |
137 | |
138 if (bytes_allocated_ > limit) | |
139 return scoped_ptr<uint8, FreeDeleter>(); | |
140 } | |
141 | |
142 bytes_allocated_ += bytes; | |
143 *purged = true; | |
144 return scoped_ptr<uint8, FreeDeleter>(static_cast<uint8*>(malloc(bytes))); | |
145 } | |
146 | |
147 void DiscardableMemoryProvider::Release( | |
148 const DiscardableMemory* discardable, | |
149 scoped_ptr<uint8, FreeDeleter> memory) { | |
150 AutoLock lock(lock_); | |
151 // NB: |allocations_| is an MRU cache, and use of |Get| here updates that | |
152 // cache. | |
153 AllocationMap::iterator it = allocations_.Get(discardable); | |
154 CHECK(it != allocations_.end()); | |
155 | |
156 DCHECK(!it->second.memory); | |
157 it->second.memory = memory.release(); | |
158 | |
159 EnforcePolicy(); | |
160 } | |
161 | |
162 bool DiscardableMemoryProvider::IsRegisteredForTest( | |
willchan no longer on Chromium
2013/10/21 21:29:12
Please order functions in the .cc file to match th
reveman
2013/10/22 00:11:42
Done.
| |
163 const DiscardableMemory* discardable) const { | |
164 AutoLock lock(lock_); | |
165 AllocationMap::const_iterator it = allocations_.Peek(discardable); | |
166 return it != allocations_.end(); | |
167 } | |
168 | |
169 bool DiscardableMemoryProvider::CanBePurgedForTest( | |
170 const DiscardableMemory* discardable) const { | |
171 AutoLock lock(lock_); | |
172 AllocationMap::const_iterator it = allocations_.Peek(discardable); | |
173 return it != allocations_.end() && it->second.memory; | |
174 } | |
175 | |
176 size_t DiscardableMemoryProvider::GetBytesAllocatedForTest() const { | |
177 AutoLock lock(lock_); | |
178 return bytes_allocated_; | |
179 } | |
180 | |
181 void DiscardableMemoryProvider::PurgeLRU(size_t limit) { | |
182 TRACE_EVENT1("base", "DiscardableMemoryProvider::PurgeLRU", "limit", limit); | |
183 | |
184 lock_.AssertAcquired(); | |
willchan no longer on Chromium
2013/10/21 21:29:12
Sometimes people use a naming convention to make i
reveman
2013/10/22 00:11:42
Added "WithLockAcquired" to the name of the functi
| |
185 | |
186 for (AllocationMap::reverse_iterator it = allocations_.rbegin(); | |
187 it != allocations_.rend(); | |
188 ++it) { | |
189 if (bytes_allocated_ <= limit) | |
190 break; | |
191 if (!it->second.memory) | |
192 continue; | |
193 | |
194 size_t bytes = it->second.bytes; | |
195 DCHECK_LE(bytes, bytes_allocated_); | |
196 bytes_allocated_ -= bytes; | |
197 free(it->second.memory); | |
198 it->second.memory = NULL; | |
199 } | |
200 } | |
201 | |
202 void DiscardableMemoryProvider::Purge() { | |
203 AutoLock lock(lock_); | |
204 | |
205 if (bytes_to_reclaim_under_moderate_pressure_ == 0) | |
206 return; | |
207 | |
208 size_t limit = 0; | |
209 if (bytes_to_reclaim_under_moderate_pressure_ < discardable_memory_limit_) | |
210 limit = bytes_allocated_ - bytes_to_reclaim_under_moderate_pressure_; | |
211 | |
212 PurgeLRU(limit); | |
213 } | |
214 | |
215 void DiscardableMemoryProvider::PurgeAll() { | |
216 AutoLock lock(lock_); | |
217 PurgeLRU(0); | |
218 } | |
219 | |
220 void DiscardableMemoryProvider::EnforcePolicy() { | |
221 lock_.AssertAcquired(); | |
222 | |
223 bool exceeded_bound = bytes_allocated_ > discardable_memory_limit_; | |
224 if (!exceeded_bound || !bytes_to_reclaim_under_moderate_pressure_) | |
225 return; | |
226 | |
227 size_t limit = 0; | |
228 if (bytes_to_reclaim_under_moderate_pressure_ < discardable_memory_limit_) | |
229 limit = bytes_allocated_ - bytes_to_reclaim_under_moderate_pressure_; | |
230 | |
231 PurgeLRU(limit); | |
232 } | |
233 | |
234 } // namespace internal | |
235 } // namespace base | |
OLD | NEW |