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 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 |