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