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/memory/discardable_memory.h" | |
11 #include "base/synchronization/lock.h" | |
12 #include "base/sys_info.h" | |
13 | |
14 namespace base { | |
15 | |
16 namespace { | |
17 | |
18 // If this is given a valid value via SetInstanceForTest, this pointer will be | |
19 // returned by GetInstance rather than the usual singleton. | |
20 static base::DiscardableMemoryProvider* s_provider_for_test = NULL; | |
21 | |
22 // This is admittedly pretty magical. It's approximately enough memory for two | |
23 // 2560x1600 images. | |
willchan no longer on Chromium
2013/08/27 03:23:15
One thing I wonder is if we'll have a good way of
Ian Vollick
2013/09/24 17:15:25
Consumers of the DiscardableMemory already need to
| |
24 static const size_t kDefaultDiscardableMemoryLimit = 32 * 1024 * 1024; | |
25 static const size_t kDefaultBytesToReclaimUnderModeratePressure = | |
26 kDefaultDiscardableMemoryLimit / 2; | |
27 | |
28 } // namespace | |
29 | |
30 DiscardableMemoryProvider::DiscardableMemoryProvider() | |
31 : allocations_(AllocationMap::NO_AUTO_EVICT), | |
32 bytes_allocated_(0), | |
33 discardable_memory_limit_(kDefaultDiscardableMemoryLimit), | |
34 bytes_to_reclaim_under_moderate_pressure_( | |
35 kDefaultBytesToReclaimUnderModeratePressure), | |
36 enforcing_policy_(false), | |
37 memory_pressure_listener_( | |
38 base::Bind(&DiscardableMemoryProvider::NotifyMemoryPressure)) { | |
39 } | |
40 | |
41 DiscardableMemoryProvider::~DiscardableMemoryProvider() { | |
42 AutoLock lock(allocations_lock_); | |
43 AllocationMap::iterator it = allocations_.begin(); | |
44 for (; it != allocations_.end(); ++it) | |
45 if (it->first->memory_) | |
46 it->first->Deallocate(); | |
47 } | |
48 | |
49 // static | |
50 DiscardableMemoryProvider* DiscardableMemoryProvider::GetInstance() { | |
51 if (s_provider_for_test) | |
52 return s_provider_for_test; | |
53 return Singleton<DiscardableMemoryProvider>::get(); | |
54 } | |
55 | |
56 // static | |
57 void DiscardableMemoryProvider::SetInstanceForTest( | |
58 DiscardableMemoryProvider* provider) { | |
59 s_provider_for_test = provider; | |
60 } | |
61 | |
62 // static | |
63 void DiscardableMemoryProvider::NotifyMemoryPressure( | |
64 MemoryPressureListener::MemoryPressureLevel pressure_level) { | |
65 switch (pressure_level) { | |
66 case MemoryPressureListener::MEMORY_PRESSURE_MODERATE: | |
67 DiscardableMemoryProvider::GetInstance()->PurgeLRU(); | |
68 break; | |
69 case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL: | |
70 DiscardableMemoryProvider::GetInstance()->PurgeAll(); | |
71 break; | |
72 default: | |
73 NOTREACHED(); | |
74 } | |
75 } | |
76 | |
77 void DiscardableMemoryProvider::SetDiscardableMemoryLimit(size_t bytes) { | |
78 { | |
79 AutoLock lock(bytes_allocated_lock_); | |
80 discardable_memory_limit_ = bytes; | |
81 } | |
82 EnforcePolicy(); | |
83 } | |
84 | |
85 size_t DiscardableMemoryProvider::discardable_memory_limit() const { | |
86 AutoLock lock(bytes_allocated_lock_); | |
87 return discardable_memory_limit_; | |
88 } | |
89 | |
90 void DiscardableMemoryProvider::SetBytesToReclaimUnderModeratePressure( | |
91 size_t bytes) { | |
92 { | |
93 AutoLock lock(bytes_allocated_lock_); | |
94 bytes_to_reclaim_under_moderate_pressure_ = bytes; | |
95 } | |
96 EnforcePolicy(); | |
97 } | |
98 | |
99 size_t DiscardableMemoryProvider:: | |
100 bytes_to_reclaim_under_moderate_pressure() const { | |
101 AutoLock lock(bytes_allocated_lock_); | |
102 return bytes_to_reclaim_under_moderate_pressure_; | |
103 } | |
104 | |
105 void DiscardableMemoryProvider::Register(DiscardableMemory* discardable) { | |
106 DCHECK(allocations_.Peek(discardable) == allocations_.end()); | |
107 { | |
108 AutoLock lock(allocations_lock_); | |
109 allocations_.Put(discardable, true); | |
110 } | |
111 EnforcePolicy(); | |
112 } | |
113 | |
114 void DiscardableMemoryProvider::Unregister(DiscardableMemory* discardable) { | |
115 { | |
116 AutoLock lock(allocations_lock_); | |
117 AllocationMap::iterator it = allocations_.Peek(discardable); | |
118 if (it != allocations_.end()) | |
119 allocations_.Erase(it); | |
120 } | |
121 EnforcePolicy(); | |
122 } | |
123 | |
124 void DiscardableMemoryProvider::DidAllocate(size_t bytes) { | |
125 { | |
126 AutoLock lock(bytes_allocated_lock_); | |
127 bytes_allocated_ += bytes; | |
128 } | |
129 EnforcePolicy(); | |
130 } | |
131 | |
132 void DiscardableMemoryProvider::DidDeallocate(size_t bytes) { | |
133 { | |
134 AutoLock lock(bytes_allocated_lock_); | |
135 DCHECK(bytes <= bytes_allocated_); | |
willchan no longer on Chromium
2013/08/27 03:23:15
DCHECK_LE?
Ian Vollick
2013/09/24 17:15:25
Done.
| |
136 bytes_allocated_ -= bytes; | |
137 } | |
138 EnforcePolicy(); | |
139 } | |
140 | |
141 bool DiscardableMemoryProvider::DidAccess(DiscardableMemory* discardable) { | |
142 AutoLock lock(allocations_lock_); | |
143 AllocationMap::iterator it = allocations_.Get(discardable); | |
willchan no longer on Chromium
2013/08/27 03:23:15
This is non-obvious to readers who don't realize t
Ian Vollick
2013/09/24 17:15:25
Done.
| |
144 return it != allocations_.end(); | |
145 } | |
146 | |
147 void DiscardableMemoryProvider::PurgeAll() { | |
148 AutoLock lock(allocations_lock_); | |
149 AllocationMap::iterator it = allocations_.begin(); | |
150 for (; it != allocations_.end(); ++it) { | |
151 if (it->first->memory_ && !it->first->is_locked()) { | |
152 it->first->Deallocate(); | |
153 DCHECK(!it->first->memory_); | |
154 } | |
155 } | |
156 } | |
157 | |
158 void DiscardableMemoryProvider::PurgeLRU() { | |
159 size_t limit = 0; | |
160 { | |
161 AutoLock lock(bytes_allocated_lock_); | |
162 if (bytes_to_reclaim_under_moderate_pressure_ == 0) | |
163 return; | |
164 | |
165 if (bytes_to_reclaim_under_moderate_pressure_ < discardable_memory_limit_) | |
166 limit = bytes_allocated_ - bytes_to_reclaim_under_moderate_pressure_; | |
167 } | |
168 | |
169 AutoLock lock(allocations_lock_); | |
170 AllocationMap::reverse_iterator it = allocations_.rbegin(); | |
171 for(; it != allocations_.rend(); ++it) { | |
172 if (!it->first->memory_ || it->first->is_locked()) | |
173 continue; | |
174 it->first->Deallocate(); | |
175 DCHECK(!it->first->memory_); | |
176 AutoLock bytes_lock(bytes_allocated_lock_); | |
177 if (bytes_allocated_ <= limit) | |
178 break; | |
179 } | |
180 } | |
181 | |
182 void DiscardableMemoryProvider::EnforcePolicy() { | |
183 { | |
184 AutoLock lock(bytes_allocated_lock_); | |
185 if (enforcing_policy_) | |
186 return; | |
187 } | |
188 | |
189 bool exceeded_bound = false; | |
190 { | |
191 AutoLock lock(bytes_allocated_lock_); | |
192 enforcing_policy_ = true; | |
193 if (discardable_memory_limit_ == 0) { | |
194 enforcing_policy_ = false; | |
195 return; | |
196 } | |
197 exceeded_bound = bytes_allocated_ > discardable_memory_limit_; | |
198 } | |
199 | |
200 if (exceeded_bound) | |
201 PurgeLRU(); | |
202 | |
203 { | |
204 AutoLock lock(bytes_allocated_lock_); | |
205 enforcing_policy_ = false; | |
206 } | |
207 } | |
208 | |
209 } // namespace base | |
OLD | NEW |