| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/memory/discardable_memory_manager.h" | 5 #include "base/memory/discardable_memory_manager.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | |
| 8 #include "base/containers/hash_tables.h" | 7 #include "base/containers/hash_tables.h" |
| 9 #include "base/containers/mru_cache.h" | 8 #include "base/containers/mru_cache.h" |
| 10 #include "base/debug/trace_event.h" | 9 #include "base/debug/trace_event.h" |
| 11 #include "base/synchronization/lock.h" | 10 #include "base/synchronization/lock.h" |
| 12 #include "base/sys_info.h" | |
| 13 | 11 |
| 14 namespace base { | 12 namespace base { |
| 15 namespace internal { | 13 namespace internal { |
| 16 | 14 |
| 17 namespace { | 15 DiscardableMemoryManager::AllocationInfo::AllocationInfo(size_t bytes) |
| 16 : bytes(bytes), locked(false) {} |
| 18 | 17 |
| 19 // This is admittedly pretty magical. It's approximately enough memory for four | 18 DiscardableMemoryManager::AllocationInfo::~AllocationInfo() {} |
| 20 // 2560x1600 images. | |
| 21 static const size_t kDefaultDiscardableMemoryLimit = 64 * 1024 * 1024; | |
| 22 static const size_t kDefaultBytesToKeepUnderModeratePressure = | |
| 23 kDefaultDiscardableMemoryLimit / 4; | |
| 24 | 19 |
| 25 } // namespace | 20 DiscardableMemoryManager::DiscardableMemoryManager( |
| 26 | 21 DiscardableMemoryAllocation::Factory* allocation_factory, |
| 27 DiscardableMemoryManager::DiscardableMemoryManager() | 22 size_t discardable_memory_limit) |
| 28 : allocations_(AllocationMap::NO_AUTO_EVICT), | 23 : allocation_factory_(allocation_factory), |
| 24 next_allocation_id_(0), |
| 25 allocations_(AllocationMap::NO_AUTO_EVICT), |
| 29 bytes_allocated_(0), | 26 bytes_allocated_(0), |
| 30 discardable_memory_limit_(kDefaultDiscardableMemoryLimit), | 27 discardable_memory_limit_(discardable_memory_limit) {} |
| 31 bytes_to_keep_under_moderate_pressure_( | |
| 32 kDefaultBytesToKeepUnderModeratePressure) { | |
| 33 } | |
| 34 | 28 |
| 35 DiscardableMemoryManager::~DiscardableMemoryManager() { | 29 DiscardableMemoryManager::~DiscardableMemoryManager() { |
| 36 DCHECK(allocations_.empty()); | 30 DCHECK(allocations_.empty()); |
| 37 DCHECK_EQ(0u, bytes_allocated_); | 31 DCHECK_EQ(0u, bytes_allocated_); |
| 38 } | 32 } |
| 39 | 33 |
| 40 void DiscardableMemoryManager::RegisterMemoryPressureListener() { | |
| 41 AutoLock lock(lock_); | |
| 42 DCHECK(base::MessageLoop::current()); | |
| 43 DCHECK(!memory_pressure_listener_); | |
| 44 memory_pressure_listener_.reset( | |
| 45 new MemoryPressureListener( | |
| 46 base::Bind(&DiscardableMemoryManager::OnMemoryPressure, | |
| 47 Unretained(this)))); | |
| 48 } | |
| 49 | |
| 50 void DiscardableMemoryManager::UnregisterMemoryPressureListener() { | |
| 51 AutoLock lock(lock_); | |
| 52 DCHECK(memory_pressure_listener_); | |
| 53 memory_pressure_listener_.reset(); | |
| 54 } | |
| 55 | |
| 56 void DiscardableMemoryManager::SetDiscardableMemoryLimit(size_t bytes) { | 34 void DiscardableMemoryManager::SetDiscardableMemoryLimit(size_t bytes) { |
| 57 AutoLock lock(lock_); | 35 AutoLock lock(lock_); |
| 58 discardable_memory_limit_ = bytes; | 36 discardable_memory_limit_ = bytes; |
| 59 EnforcePolicyWithLockAcquired(); | 37 EnforcePolicyWithLockAcquired(); |
| 60 } | 38 } |
| 61 | 39 |
| 62 void DiscardableMemoryManager::SetBytesToKeepUnderModeratePressure( | 40 DiscardableMemoryManager::AllocationId DiscardableMemoryManager::Register( |
| 63 size_t bytes) { | 41 size_t bytes) { |
| 64 AutoLock lock(lock_); | 42 AutoLock lock(lock_); |
| 65 bytes_to_keep_under_moderate_pressure_ = bytes; | 43 AllocationId allocation_id = next_allocation_id_++; |
| 66 } | 44 if (next_allocation_id_ == INT_MAX) |
| 67 | 45 next_allocation_id_ = 1; |
| 68 void DiscardableMemoryManager::Register( | |
| 69 const DiscardableMemory* discardable, size_t bytes) { | |
| 70 AutoLock lock(lock_); | |
| 71 // A registered memory listener is currently required. This DCHECK can be | 46 // A registered memory listener is currently required. This DCHECK can be |
| 72 // moved or removed if we decide that it's useful to relax this condition. | 47 // moved or removed if we decide that it's useful to relax this condition. |
| 73 // TODO(reveman): Enable this DCHECK when skia and blink are able to | 48 // TODO(reveman): Enable this DCHECK when skia and blink are able to |
| 74 // register memory pressure listeners. crbug.com/333907 | 49 // register memory pressure listeners. crbug.com/333907 |
| 75 // DCHECK(memory_pressure_listener_); | 50 // DCHECK(memory_pressure_listener_); |
| 76 DCHECK(allocations_.Peek(discardable) == allocations_.end()); | 51 DCHECK(allocations_.Peek(allocation_id) == allocations_.end()); |
| 77 allocations_.Put(discardable, Allocation(bytes)); | 52 allocations_.Put(allocation_id, AllocationInfo(bytes)); |
| 53 return allocation_id; |
| 78 } | 54 } |
| 79 | 55 |
| 80 void DiscardableMemoryManager::Unregister( | 56 void DiscardableMemoryManager::Unregister(AllocationId allocation_id) { |
| 81 const DiscardableMemory* discardable) { | |
| 82 AutoLock lock(lock_); | 57 AutoLock lock(lock_); |
| 83 AllocationMap::iterator it = allocations_.Peek(discardable); | 58 AllocationMap::iterator it = allocations_.Peek(allocation_id); |
| 84 if (it == allocations_.end()) | 59 if (it == allocations_.end()) |
| 85 return; | 60 return; |
| 86 | 61 |
| 87 if (it->second.memory) { | 62 if (it->second.allocation.get()) { |
| 88 size_t bytes = it->second.bytes; | 63 size_t bytes = it->second.bytes; |
| 89 DCHECK_LE(bytes, bytes_allocated_); | 64 DCHECK_LE(bytes, bytes_allocated_); |
| 90 bytes_allocated_ -= bytes; | 65 bytes_allocated_ -= bytes; |
| 91 free(it->second.memory); | 66 it->second.allocation.reset(); |
| 92 } | 67 } |
| 93 allocations_.Erase(it); | 68 allocations_.Erase(it); |
| 94 } | 69 } |
| 95 | 70 |
| 96 scoped_ptr<uint8, FreeDeleter> DiscardableMemoryManager::Acquire( | 71 void* DiscardableMemoryManager::Lock(AllocationId allocation_id, bool* purged) { |
| 97 const DiscardableMemory* discardable, | |
| 98 bool* purged) { | |
| 99 AutoLock lock(lock_); | 72 AutoLock lock(lock_); |
| 100 // NB: |allocations_| is an MRU cache, and use of |Get| here updates that | 73 // NB: |allocations_| is an MRU cache, and use of |Get| here updates that |
| 101 // cache. | 74 // cache. |
| 102 AllocationMap::iterator it = allocations_.Get(discardable); | 75 AllocationMap::iterator it = allocations_.Get(allocation_id); |
| 103 CHECK(it != allocations_.end()); | 76 CHECK(it != allocations_.end()); |
| 104 | 77 |
| 105 if (it->second.memory) { | 78 if (it->second.allocation.get()) { |
| 106 scoped_ptr<uint8, FreeDeleter> memory(it->second.memory); | 79 DiscardableMemoryAllocation* allocation = it->second.allocation.get(); |
| 107 it->second.memory = NULL; | 80 |
| 108 *purged = false; | 81 DCHECK(!it->second.locked); |
| 109 return memory.Pass(); | 82 it->second.locked = true; |
| 83 *purged = !allocation->Lock(); |
| 84 return allocation->Memory(); |
| 110 } | 85 } |
| 111 | 86 |
| 112 size_t bytes = it->second.bytes; | 87 size_t bytes = it->second.bytes; |
| 113 if (!bytes) | 88 if (!bytes) |
| 114 return scoped_ptr<uint8, FreeDeleter>(); | 89 return NULL; |
| 115 | 90 |
| 116 if (discardable_memory_limit_) { | 91 if (discardable_memory_limit_) { |
| 117 size_t limit = 0; | 92 size_t limit = 0; |
| 118 if (bytes < discardable_memory_limit_) | 93 if (bytes < discardable_memory_limit_) |
| 119 limit = discardable_memory_limit_ - bytes; | 94 limit = discardable_memory_limit_ - bytes; |
| 120 | 95 |
| 121 PurgeLRUWithLockAcquiredUntilUsageIsWithin(limit); | 96 PurgeLRUWithLockAcquiredUntilUsageIsWithin(limit); |
| 122 } | 97 } |
| 123 | 98 |
| 124 // Check for overflow. | 99 // Check for overflow. |
| 125 if (std::numeric_limits<size_t>::max() - bytes < bytes_allocated_) | 100 if (std::numeric_limits<size_t>::max() - bytes < bytes_allocated_) |
| 126 return scoped_ptr<uint8, FreeDeleter>(); | 101 return NULL; |
| 127 | 102 |
| 128 scoped_ptr<uint8, FreeDeleter> memory(static_cast<uint8*>(malloc(bytes))); | 103 linked_ptr<DiscardableMemoryAllocation> allocation( |
| 129 if (!memory) | 104 allocation_factory_->CreateLockedAllocation(bytes).release()); |
| 130 return scoped_ptr<uint8, FreeDeleter>(); | 105 if (!allocation.get()) |
| 106 return NULL; |
| 131 | 107 |
| 108 it->second.allocation = allocation; |
| 109 it->second.locked = true; |
| 132 bytes_allocated_ += bytes; | 110 bytes_allocated_ += bytes; |
| 133 *purged = true; | 111 *purged = true; |
| 134 return memory.Pass(); | 112 return allocation->Memory(); |
| 135 } | 113 } |
| 136 | 114 |
| 137 void DiscardableMemoryManager::Release( | 115 void DiscardableMemoryManager::Unlock(AllocationId allocation_id) { |
| 138 const DiscardableMemory* discardable, | |
| 139 scoped_ptr<uint8, FreeDeleter> memory) { | |
| 140 AutoLock lock(lock_); | 116 AutoLock lock(lock_); |
| 141 // NB: |allocations_| is an MRU cache, and use of |Get| here updates that | 117 // NB: |allocations_| is an MRU cache, and use of |Get| here updates that |
| 142 // cache. | 118 // cache. |
| 143 AllocationMap::iterator it = allocations_.Get(discardable); | 119 AllocationMap::iterator it = allocations_.Get(allocation_id); |
| 144 CHECK(it != allocations_.end()); | 120 CHECK(it != allocations_.end()); |
| 145 | 121 |
| 146 DCHECK(!it->second.memory); | 122 DCHECK(it->second.allocation.get()); |
| 147 it->second.memory = memory.release(); | 123 DCHECK(it->second.locked); |
| 148 | 124 it->second.locked = false; |
| 125 it->second.allocation->Unlock(); |
| 149 EnforcePolicyWithLockAcquired(); | 126 EnforcePolicyWithLockAcquired(); |
| 150 } | 127 } |
| 151 | 128 |
| 152 void DiscardableMemoryManager::PurgeAll() { | 129 void DiscardableMemoryManager::PurgeUntilUsageIsWithin(size_t limit) { |
| 153 AutoLock lock(lock_); | 130 AutoLock lock(lock_); |
| 154 PurgeLRUWithLockAcquiredUntilUsageIsWithin(0); | 131 PurgeLRUWithLockAcquiredUntilUsageIsWithin(limit); |
| 155 } | 132 } |
| 156 | 133 |
| 157 bool DiscardableMemoryManager::IsRegisteredForTest( | 134 bool DiscardableMemoryManager::IsRegisteredForTest( |
| 158 const DiscardableMemory* discardable) const { | 135 AllocationId allocation_id) const { |
| 159 AutoLock lock(lock_); | 136 AutoLock lock(lock_); |
| 160 AllocationMap::const_iterator it = allocations_.Peek(discardable); | 137 AllocationMap::const_iterator it = allocations_.Peek(allocation_id); |
| 161 return it != allocations_.end(); | 138 return it != allocations_.end(); |
| 162 } | 139 } |
| 163 | 140 |
| 164 bool DiscardableMemoryManager::CanBePurgedForTest( | 141 bool DiscardableMemoryManager::CanBePurgedForTest( |
| 165 const DiscardableMemory* discardable) const { | 142 AllocationId allocation_id) const { |
| 166 AutoLock lock(lock_); | 143 AutoLock lock(lock_); |
| 167 AllocationMap::const_iterator it = allocations_.Peek(discardable); | 144 AllocationMap::const_iterator it = allocations_.Peek(allocation_id); |
| 168 return it != allocations_.end() && it->second.memory; | 145 return it != allocations_.end() && !it->second.locked; |
| 169 } | 146 } |
| 170 | 147 |
| 171 size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const { | 148 size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const { |
| 172 AutoLock lock(lock_); | 149 AutoLock lock(lock_); |
| 173 return bytes_allocated_; | 150 return bytes_allocated_; |
| 174 } | 151 } |
| 175 | 152 |
| 176 void DiscardableMemoryManager::OnMemoryPressure( | |
| 177 MemoryPressureListener::MemoryPressureLevel pressure_level) { | |
| 178 switch (pressure_level) { | |
| 179 case MemoryPressureListener::MEMORY_PRESSURE_MODERATE: | |
| 180 Purge(); | |
| 181 return; | |
| 182 case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL: | |
| 183 PurgeAll(); | |
| 184 return; | |
| 185 } | |
| 186 | |
| 187 NOTREACHED(); | |
| 188 } | |
| 189 | |
| 190 void DiscardableMemoryManager::Purge() { | |
| 191 AutoLock lock(lock_); | |
| 192 | |
| 193 PurgeLRUWithLockAcquiredUntilUsageIsWithin( | |
| 194 bytes_to_keep_under_moderate_pressure_); | |
| 195 } | |
| 196 | |
| 197 void DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin( | 153 void DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin( |
| 198 size_t limit) { | 154 size_t limit) { |
| 199 TRACE_EVENT1( | 155 TRACE_EVENT1( |
| 200 "base", | 156 "base", |
| 201 "DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin", | 157 "DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin", |
| 202 "limit", limit); | 158 "limit", limit); |
| 203 | 159 |
| 204 lock_.AssertAcquired(); | 160 lock_.AssertAcquired(); |
| 205 | 161 |
| 206 for (AllocationMap::reverse_iterator it = allocations_.rbegin(); | 162 for (AllocationMap::reverse_iterator it = allocations_.rbegin(); |
| 207 it != allocations_.rend(); | 163 it != allocations_.rend(); |
| 208 ++it) { | 164 ++it) { |
| 209 if (bytes_allocated_ <= limit) | 165 if (bytes_allocated_ <= limit) |
| 210 break; | 166 break; |
| 211 if (!it->second.memory) | 167 if (!it->second.allocation.get()) |
| 168 continue; |
| 169 if (it->second.locked) |
| 212 continue; | 170 continue; |
| 213 | 171 |
| 214 size_t bytes = it->second.bytes; | 172 size_t bytes = it->second.bytes; |
| 215 DCHECK_LE(bytes, bytes_allocated_); | 173 DCHECK_LE(bytes, bytes_allocated_); |
| 216 bytes_allocated_ -= bytes; | 174 bytes_allocated_ -= bytes; |
| 217 free(it->second.memory); | 175 it->second.allocation.reset(); |
| 218 it->second.memory = NULL; | |
| 219 } | 176 } |
| 220 } | 177 } |
| 221 | 178 |
| 222 void DiscardableMemoryManager::EnforcePolicyWithLockAcquired() { | 179 void DiscardableMemoryManager::EnforcePolicyWithLockAcquired() { |
| 223 PurgeLRUWithLockAcquiredUntilUsageIsWithin(discardable_memory_limit_); | 180 PurgeLRUWithLockAcquiredUntilUsageIsWithin(discardable_memory_limit_); |
| 224 } | 181 } |
| 225 | 182 |
| 226 } // namespace internal | 183 } // namespace internal |
| 227 } // namespace base | 184 } // namespace base |
| OLD | NEW |