Chromium Code Reviews| 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 |