OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 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.h" | |
6 | |
7 #include "base/containers/hash_tables.h" | |
8 #include "base/containers/mru_cache.h" | |
9 #include "base/memory/singleton.h" | |
10 #include "base/synchronization/lock.h" | |
11 #include "base/sys_info.h" | |
12 | |
13 #if defined(COMPILER_GCC) | |
14 namespace BASE_HASH_NAMESPACE { | |
15 template <> | |
16 struct hash<base::DiscardableMemory*> { | |
17 size_t operator()(base::DiscardableMemory* ptr) const { | |
18 return hash<size_t>()(reinterpret_cast<size_t>(ptr)); | |
19 } | |
20 }; | |
21 } // namespace BASE_HASH_NAMESPACE | |
22 #endif // COMPILER | |
23 | |
24 namespace base { | |
25 | |
26 namespace { | |
27 | |
28 // These are definitely magical. Is there a nice way to set them? | |
29 static size_t kCacheLimitUpperBound = 32 * 1024 * 1024; | |
30 static size_t kCacheLimitPurgeBound = 4 * 1024 * 1024; | |
31 static int64 kSystemMemoryLowerBound = 256 * 1024 * 1024; | |
32 | |
33 static bool SystemIsUnderMemoryPressure() { | |
34 return SysInfo::AmountOfAvailablePhysicalMemory() < | |
35 kSystemMemoryLowerBound; | |
Avi (use Gerrit)
2013/06/12 14:56:49
Way too magical. Does this function belong in SysI
| |
36 } | |
37 | |
38 typedef HashingMRUCache<DiscardableMemory*, bool> AllocationMap; | |
Avi (use Gerrit)
2013/06/12 14:56:49
bool? If that's just a placeholder, say so.
| |
39 | |
40 } // namespace | |
41 | |
42 // This provider is accessed from multiple threads, so some care needs to be | |
43 // taken to ensure thread safety. In general, the policy has been that | |
44 // allocations are initiated by client code (via Lock or InitializeAndLock), | |
45 // and deallocations are initiated by the provider. Given this policy, the | |
46 // provider assumes that the lock has already been aquired by the provider | |
47 // itself in the Purge/Policy/DidDeallocate methods, but does not make this | |
48 // assumption in the rest of the interface and aquires the lock as appropriate. | |
49 class DiscardableMemoryProvider { | |
Avi (use Gerrit)
2013/06/12 14:56:49
Give the member functions explanatory comments. Me
Avi (use Gerrit)
2013/06/12 14:56:49
DiscardableMemoryProvider desperately needs a unit
| |
50 public: | |
51 DiscardableMemoryProvider() | |
52 : allocations_(AllocationMap::NO_AUTO_EVICT), | |
53 bytes_allocated_(0) { | |
54 } | |
55 | |
56 virtual ~DiscardableMemoryProvider() { | |
57 AutoLock lock(lock_); | |
58 AllocationMap::iterator it = allocations_.begin(); | |
59 for (; it != allocations_.end(); ++it) | |
60 if (it->first->Memory()) | |
61 it->first->Deallocate(); | |
62 } | |
63 | |
64 static DiscardableMemoryProvider* GetInstance() { | |
65 return Singleton<DiscardableMemoryProvider>::get(); | |
66 } | |
67 | |
68 void Register(DiscardableMemory* discardable) { | |
69 Unregister(discardable); | |
Avi (use Gerrit)
2013/06/12 14:56:49
Why the Unregister? Register is only called from D
| |
70 { | |
71 AutoLock lock(lock_); | |
72 allocations_.Put(discardable, true); | |
73 EnforcePolicy(); | |
74 } | |
75 } | |
76 | |
77 void Unregister(DiscardableMemory* discardable) { | |
78 if (discardable->is_locked()) | |
79 discardable->Unlock(); | |
80 { | |
81 AutoLock lock(lock_); | |
82 if (discardable->memory_) | |
83 discardable->Deallocate(); | |
Avi (use Gerrit)
2013/06/12 14:56:49
Eh...
Unregister is only called by ~DiscardableMe
| |
84 AllocationMap::iterator it = allocations_.Peek(discardable); | |
85 if (it != allocations_.end()) { | |
86 allocations_.Erase(it); | |
87 EnforcePolicy(); | |
88 } | |
89 } | |
90 } | |
91 | |
92 void DidAllocate(size_t bytes) { | |
93 AutoLock lock(lock_); | |
94 bytes_allocated_ += bytes; | |
95 EnforcePolicy(); | |
96 } | |
97 | |
98 void DidDeallocate(size_t bytes) { | |
99 lock_.AssertAcquired(); | |
100 DCHECK(bytes <= bytes_allocated_); | |
101 bytes_allocated_ -= bytes; | |
102 EnforcePolicy(); | |
103 } | |
104 | |
105 void DidAccess(DiscardableMemory* discardable) { | |
106 AutoLock lock(lock_); | |
107 allocations_.Get(discardable); | |
108 } | |
109 | |
110 void ForcePurge() { | |
111 AutoLock lock(lock_); | |
112 PurgeAll(); | |
113 } | |
114 | |
115 void PurgeAll() { | |
116 AllocationMap::iterator it = allocations_.begin(); | |
117 for (; it != allocations_.end(); ++it) { | |
118 if (it->first->memory_ && !it->first->is_locked()) { | |
119 it->first->Deallocate(); | |
120 DCHECK(!it->first->memory_); | |
121 } | |
122 } | |
123 } | |
124 | |
125 void PurgeLRU() { | |
126 AllocationMap::reverse_iterator it = allocations_.rbegin(); | |
127 for(; it != allocations_.rend(); ++it) { | |
128 if (!it->first->memory_ || !it->first->is_locked()) | |
129 continue; | |
130 it->first->Deallocate(); | |
131 DCHECK(!it->first->memory_); | |
132 if (bytes_allocated_ < kCacheLimitPurgeBound) | |
cpu_(ooo_6.6-7.5)
2013/06/13 20:16:06
I was expecting freeing N megs, not freeing until
| |
133 break; | |
134 } | |
135 } | |
136 | |
137 void EnforcePolicy() { | |
138 if (SystemIsUnderMemoryPressure()) | |
139 PurgeAll(); | |
cpu_(ooo_6.6-7.5)
2013/06/13 20:16:06
again, surprised on the full purge.
| |
140 else if (bytes_allocated_ > kCacheLimitUpperBound) | |
141 PurgeLRU(); | |
142 } | |
143 | |
144 private: | |
145 AllocationMap allocations_; | |
146 bool visible_; | |
147 size_t bytes_allocated_; | |
148 Lock lock_; | |
Avi (use Gerrit)
2013/06/12 14:56:49
Member variables need comment love too. In particu
| |
149 | |
150 DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryProvider); | |
151 }; | |
152 | |
153 DiscardableMemory::~DiscardableMemory() { | |
154 DiscardableMemoryProvider::GetInstance()->Unregister(this); | |
155 } | |
156 | |
157 bool DiscardableMemory::InitializeAndLock(size_t size) { | |
158 DiscardableMemoryProvider::GetInstance()->Register(this); | |
159 size_ = size; | |
160 return Lock() != DISCARDABLE_MEMORY_FAILED; | |
Avi (use Gerrit)
2013/06/12 14:56:49
LockDiscardableMemoryStatus status = Lock();
DCHEC
| |
161 } | |
162 | |
163 LockDiscardableMemoryStatus DiscardableMemory::Lock() { | |
164 DCHECK(!is_locked_); | |
165 DiscardableMemoryProvider::GetInstance()->DidAccess(this); | |
166 is_locked_ = true; | |
167 | |
168 if (memory_) | |
169 return DISCARDABLE_MEMORY_SUCCESS; | |
170 | |
171 if (Allocate()) | |
172 return DISCARDABLE_MEMORY_PURGED; | |
173 | |
174 return DISCARDABLE_MEMORY_FAILED; | |
175 } | |
176 | |
177 void DiscardableMemory::Unlock() { | |
178 DCHECK(is_locked_); | |
179 DiscardableMemoryProvider::GetInstance()->DidAccess(this); | |
180 is_locked_ = false; | |
181 } | |
182 | |
183 bool DiscardableMemory::Allocate() { | |
184 DCHECK(!memory_); | |
185 memory_ = malloc(size_ * sizeof(char)); | |
Avi (use Gerrit)
2013/06/12 14:56:49
sizeof(char) is defined by the standard to be 1 (C
| |
186 if (memory_) | |
187 DiscardableMemoryProvider::GetInstance()->DidAllocate(size_); | |
188 return memory_; | |
189 } | |
190 | |
191 void DiscardableMemory::Deallocate() { | |
192 DCHECK(memory_); | |
193 free(memory_); | |
194 memory_ = NULL; | |
195 DiscardableMemoryProvider::GetInstance()->DidDeallocate(size_); | |
196 } | |
197 | |
198 | |
199 // static | |
200 bool DiscardableMemory::PurgeForTestingSupported() { | |
201 return true; | |
202 } | |
203 | |
204 // static | |
205 void DiscardableMemory::PurgeForTesting() { | |
206 Purge(); | |
207 } | |
208 | |
209 void DiscardableMemory::Purge() { | |
210 DiscardableMemoryProvider::GetInstance()->ForcePurge(); | |
211 } | |
212 | |
213 } // namespace base | |
OLD | NEW |