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/synchronization/lock.h" 12 #include "base/sys_info.h" 13 14 namespace base { 15 namespace internal { 16 17 namespace { 18 19 // This is admittedly pretty magical. It's approximately enough memory for two 20 // 2560x1600 images. 21 static const size_t kDefaultDiscardableMemoryLimit = 32 * 1024 * 1024; 22 static const size_t kDefaultBytesToReclaimUnderModeratePressure = 23 kDefaultDiscardableMemoryLimit / 2; 24 25 } // namespace 26 27 DiscardableMemoryProvider::DiscardableMemoryProvider() 28 : allocations_(AllocationMap::NO_AUTO_EVICT), 29 bytes_allocated_(0), 30 discardable_memory_limit_(kDefaultDiscardableMemoryLimit), 31 bytes_to_reclaim_under_moderate_pressure_( 32 kDefaultBytesToReclaimUnderModeratePressure), 33 memory_pressure_listener_( 34 base::Bind(&DiscardableMemoryProvider::NotifyMemoryPressure, 35 Unretained(this))) { 36 } 37 38 DiscardableMemoryProvider::~DiscardableMemoryProvider() { 39 DCHECK(allocations_.empty()); 40 DCHECK_EQ(0u, bytes_allocated_); 41 } 42 43 void DiscardableMemoryProvider::NotifyMemoryPressure( 44 MemoryPressureListener::MemoryPressureLevel pressure_level) { 45 switch (pressure_level) { 46 case MemoryPressureListener::MEMORY_PRESSURE_MODERATE: 47 Purge(); 48 return; 49 case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL: 50 PurgeAll(); 51 return; 52 } 53 54 NOTREACHED(); 55 } 56 57 void DiscardableMemoryProvider::SetDiscardableMemoryLimit(size_t bytes) { 58 AutoLock lock(lock_); 59 discardable_memory_limit_ = bytes; 60 EnforcePolicyWithLockAcquired(); 61 } 62 63 void DiscardableMemoryProvider::SetBytesToReclaimUnderModeratePressure( 64 size_t bytes) { 65 AutoLock lock(lock_); 66 bytes_to_reclaim_under_moderate_pressure_ = bytes; 67 } 68 69 void DiscardableMemoryProvider::Register( 70 const DiscardableMemory* discardable, size_t bytes) { 71 AutoLock lock(lock_); 72 DCHECK(allocations_.Peek(discardable) == allocations_.end()); 73 allocations_.Put(discardable, Allocation(bytes)); 74 } 75 76 void DiscardableMemoryProvider::Unregister( 77 const DiscardableMemory* discardable) { 78 AutoLock lock(lock_); 79 AllocationMap::iterator it = allocations_.Peek(discardable); 80 if (it == allocations_.end()) 81 return; 82 83 if (it->second.memory) { 84 size_t bytes = it->second.bytes; 85 DCHECK_LE(bytes, bytes_allocated_); 86 bytes_allocated_ -= bytes; 87 free(it->second.memory); 88 } 89 allocations_.Erase(it); 90 } 91 92 scoped_ptr<uint8, FreeDeleter> DiscardableMemoryProvider::Acquire( 93 const DiscardableMemory* discardable, 94 bool* purged) { 95 AutoLock lock(lock_); 96 // NB: |allocations_| is an MRU cache, and use of |Get| here updates that 97 // cache. 98 AllocationMap::iterator it = allocations_.Get(discardable); 99 CHECK(it != allocations_.end()); 100 101 if (it->second.memory) { 102 scoped_ptr<uint8, FreeDeleter> memory(it->second.memory); 103 it->second.memory = NULL; 104 *purged = false; 105 return memory.Pass(); 106 } 107 108 size_t bytes = it->second.bytes; 109 if (!bytes) 110 return scoped_ptr<uint8, FreeDeleter>(); 111 112 if (discardable_memory_limit_) { 113 size_t limit = 0; 114 if (bytes < discardable_memory_limit_) 115 limit = discardable_memory_limit_ - bytes; 116 117 PurgeLRUWithLockAcquiredUntilUsageIsWithin(limit); 118 } 119 120 // Check for overflow. 121 if (std::numeric_limits<size_t>::max() - bytes < bytes_allocated_) 122 return scoped_ptr<uint8, FreeDeleter>(); 123 124 scoped_ptr<uint8, FreeDeleter> memory(static_cast<uint8*>(malloc(bytes))); 125 if (!memory) 126 return scoped_ptr<uint8, FreeDeleter>(); 127 128 bytes_allocated_ += bytes; 129 *purged = true; 130 return memory.Pass(); 131 } 132 133 void DiscardableMemoryProvider::Release( 134 const DiscardableMemory* discardable, 135 scoped_ptr<uint8, FreeDeleter> memory) { 136 AutoLock lock(lock_); 137 // NB: |allocations_| is an MRU cache, and use of |Get| here updates that 138 // cache. 139 AllocationMap::iterator it = allocations_.Get(discardable); 140 CHECK(it != allocations_.end()); 141 142 DCHECK(!it->second.memory); 143 it->second.memory = memory.release(); 144 145 EnforcePolicyWithLockAcquired(); 146 } 147 148 void DiscardableMemoryProvider::PurgeAll() { 149 AutoLock lock(lock_); 150 PurgeLRUWithLockAcquiredUntilUsageIsWithin(0); 151 } 152 153 bool DiscardableMemoryProvider::IsRegisteredForTest( 154 const DiscardableMemory* discardable) const { 155 AutoLock lock(lock_); 156 AllocationMap::const_iterator it = allocations_.Peek(discardable); 157 return it != allocations_.end(); 158 } 159 160 bool DiscardableMemoryProvider::CanBePurgedForTest( 161 const DiscardableMemory* discardable) const { 162 AutoLock lock(lock_); 163 AllocationMap::const_iterator it = allocations_.Peek(discardable); 164 return it != allocations_.end() && it->second.memory; 165 } 166 167 size_t DiscardableMemoryProvider::GetBytesAllocatedForTest() const { 168 AutoLock lock(lock_); 169 return bytes_allocated_; 170 } 171 172 void DiscardableMemoryProvider::Purge() { 173 AutoLock lock(lock_); 174 175 if (bytes_to_reclaim_under_moderate_pressure_ == 0) 176 return; 177 178 size_t limit = 0; 179 if (bytes_to_reclaim_under_moderate_pressure_ < bytes_allocated_) 180 limit = bytes_allocated_ - bytes_to_reclaim_under_moderate_pressure_; 181 182 PurgeLRUWithLockAcquiredUntilUsageIsWithin(limit); 183 } 184 185 void DiscardableMemoryProvider::PurgeLRUWithLockAcquiredUntilUsageIsWithin( 186 size_t limit) { 187 TRACE_EVENT1( 188 "base", 189 "DiscardableMemoryProvider::PurgeLRUWithLockAcquiredUntilUsageIsWithin", 190 "limit", limit); 191 192 lock_.AssertAcquired(); 193 194 for (AllocationMap::reverse_iterator it = allocations_.rbegin(); 195 it != allocations_.rend(); 196 ++it) { 197 if (bytes_allocated_ <= limit) 198 break; 199 if (!it->second.memory) 200 continue; 201 202 size_t bytes = it->second.bytes; 203 DCHECK_LE(bytes, bytes_allocated_); 204 bytes_allocated_ -= bytes; 205 free(it->second.memory); 206 it->second.memory = NULL; 207 } 208 } 209 210 void DiscardableMemoryProvider::EnforcePolicyWithLockAcquired() { 211 PurgeLRUWithLockAcquiredUntilUsageIsWithin(discardable_memory_limit_); 212 } 213 214 } // namespace internal 215 } // namespace base 216