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 AutoLock lock(allocations_lock_); | |
45 AllocationMap::iterator it = allocations_.begin(); | |
46 for (; it != allocations_.end(); ++it) | |
willchan no longer on Chromium
2013/10/01 18:47:25
Mind putting braces around this? I know it works,
reveman
2013/10/09 22:40:24
Done.
| |
47 if (it->first->memory_) | |
48 it->first->Deallocate(); | |
49 } | |
50 | |
51 // static | |
52 DiscardableMemoryProvider* DiscardableMemoryProvider::GetInstance() { | |
53 if (s_provider_for_test) | |
54 return s_provider_for_test; | |
55 return Singleton<DiscardableMemoryProvider>::get(); | |
56 } | |
57 | |
58 // static | |
59 void DiscardableMemoryProvider::SetInstanceForTest( | |
60 DiscardableMemoryProvider* provider) { | |
61 s_provider_for_test = provider; | |
62 } | |
63 | |
64 // static | |
65 void DiscardableMemoryProvider::NotifyMemoryPressure( | |
willchan no longer on Chromium
2013/10/01 18:47:25
OK, there's a bug here. NotifyMemoryPressure will
reveman
2013/10/09 22:40:24
This is supposed to be fixed in my latest patch. P
| |
66 MemoryPressureListener::MemoryPressureLevel pressure_level) { | |
67 switch (pressure_level) { | |
68 case MemoryPressureListener::MEMORY_PRESSURE_MODERATE: | |
69 DiscardableMemoryProvider::GetInstance()->PurgeLRU(); | |
70 break; | |
71 case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL: | |
72 DiscardableMemoryProvider::GetInstance()->PurgeAll(); | |
73 break; | |
74 default: | |
75 NOTREACHED(); | |
reveman
2013/10/09 22:40:24
Note: I removed the default case and moved the NOT
| |
76 } | |
77 } | |
78 | |
79 void DiscardableMemoryProvider::SetDiscardableMemoryLimit(size_t bytes) { | |
80 { | |
81 AutoLock lock(bytes_allocated_lock_); | |
82 discardable_memory_limit_ = bytes; | |
83 } | |
84 EnforcePolicy(); | |
85 } | |
86 | |
87 size_t DiscardableMemoryProvider::discardable_memory_limit() const { | |
88 AutoLock lock(bytes_allocated_lock_); | |
89 return discardable_memory_limit_; | |
90 } | |
91 | |
92 void DiscardableMemoryProvider::SetBytesToReclaimUnderModeratePressure( | |
93 size_t bytes) { | |
94 { | |
95 AutoLock lock(bytes_allocated_lock_); | |
96 bytes_to_reclaim_under_moderate_pressure_ = bytes; | |
97 } | |
98 EnforcePolicy(); | |
99 } | |
100 | |
101 size_t DiscardableMemoryProvider:: | |
102 bytes_to_reclaim_under_moderate_pressure() const { | |
103 AutoLock lock(bytes_allocated_lock_); | |
104 return bytes_to_reclaim_under_moderate_pressure_; | |
105 } | |
106 | |
107 void DiscardableMemoryProvider::Register(DiscardableMemory* discardable) { | |
108 DCHECK(allocations_.Peek(discardable) == allocations_.end()); | |
109 { | |
110 AutoLock lock(allocations_lock_); | |
111 allocations_.Put(discardable, true); | |
112 } | |
113 EnforcePolicy(); | |
114 } | |
115 | |
116 void DiscardableMemoryProvider::Unregister(DiscardableMemory* discardable) { | |
117 { | |
118 AutoLock lock(allocations_lock_); | |
119 AllocationMap::iterator it = allocations_.Peek(discardable); | |
120 if (it != allocations_.end()) | |
121 allocations_.Erase(it); | |
122 } | |
123 EnforcePolicy(); | |
124 } | |
125 | |
126 void DiscardableMemoryProvider::DidAllocate(size_t bytes) { | |
127 { | |
128 AutoLock lock(bytes_allocated_lock_); | |
129 bytes_allocated_ += bytes; | |
130 } | |
131 EnforcePolicy(); | |
132 } | |
133 | |
134 void DiscardableMemoryProvider::DidDeallocate(size_t bytes) { | |
135 { | |
136 AutoLock lock(bytes_allocated_lock_); | |
137 DCHECK_LE(bytes, bytes_allocated_); | |
138 bytes_allocated_ -= bytes; | |
139 } | |
140 EnforcePolicy(); | |
141 } | |
142 | |
143 bool DiscardableMemoryProvider::DidAccess(DiscardableMemory* discardable) { | |
144 AutoLock lock(allocations_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 return it != allocations_.end(); | |
149 } | |
150 | |
151 void DiscardableMemoryProvider::PurgeAll() { | |
152 TRACE_EVENT0("base", "DiscardableMemoryProvider::PurgeAll"); | |
153 | |
154 AutoLock lock(allocations_lock_); | |
155 AllocationMap::iterator it = allocations_.begin(); | |
156 for (; it != allocations_.end(); ++it) { | |
157 if (it->first->memory_ && !it->first->is_locked()) { | |
158 it->first->Deallocate(); | |
159 DCHECK(!it->first->memory_); | |
160 } | |
161 } | |
162 } | |
163 | |
164 void DiscardableMemoryProvider::PurgeLRU() { | |
165 size_t limit = 0; | |
166 { | |
167 AutoLock lock(bytes_allocated_lock_); | |
168 if (bytes_to_reclaim_under_moderate_pressure_ == 0) | |
169 return; | |
170 | |
171 if (bytes_to_reclaim_under_moderate_pressure_ < discardable_memory_limit_) | |
172 limit = bytes_allocated_ - bytes_to_reclaim_under_moderate_pressure_; | |
173 } | |
174 | |
175 TRACE_EVENT0("base", "DiscardableMemoryProvider::PurgeLRU"); | |
176 AutoLock lock(allocations_lock_); | |
177 AllocationMap::reverse_iterator it = allocations_.rbegin(); | |
178 for(; it != allocations_.rend(); ++it) { | |
179 if (!it->first->memory_ || it->first->is_locked()) | |
180 continue; | |
181 it->first->Deallocate(); | |
182 DCHECK(!it->first->memory_); | |
183 AutoLock bytes_lock(bytes_allocated_lock_); | |
184 if (bytes_allocated_ <= limit) | |
185 break; | |
186 } | |
187 } | |
188 | |
189 void DiscardableMemoryProvider::EnforcePolicy() { | |
190 { | |
191 AutoLock lock(bytes_allocated_lock_); | |
192 if (enforcing_policy_) | |
193 return; | |
194 } | |
195 | |
196 bool exceeded_bound = false; | |
197 { | |
198 AutoLock lock(bytes_allocated_lock_); | |
199 enforcing_policy_ = true; | |
200 if (discardable_memory_limit_ == 0) { | |
201 enforcing_policy_ = false; | |
202 return; | |
203 } | |
204 exceeded_bound = bytes_allocated_ > discardable_memory_limit_; | |
205 } | |
206 | |
207 if (exceeded_bound) | |
208 PurgeLRU(); | |
209 | |
210 { | |
211 AutoLock lock(bytes_allocated_lock_); | |
212 enforcing_policy_ = false; | |
213 } | |
214 } | |
215 | |
216 } // namespace internal | |
217 } // namespace base | |
OLD | NEW |