• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DiscardableMemoryProvider()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 
~DiscardableMemoryProvider()38 DiscardableMemoryProvider::~DiscardableMemoryProvider() {
39   DCHECK(allocations_.empty());
40   DCHECK_EQ(0u, bytes_allocated_);
41 }
42 
NotifyMemoryPressure(MemoryPressureListener::MemoryPressureLevel pressure_level)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 
SetDiscardableMemoryLimit(size_t bytes)57 void DiscardableMemoryProvider::SetDiscardableMemoryLimit(size_t bytes) {
58   AutoLock lock(lock_);
59   discardable_memory_limit_ = bytes;
60   EnforcePolicyWithLockAcquired();
61 }
62 
SetBytesToReclaimUnderModeratePressure(size_t bytes)63 void DiscardableMemoryProvider::SetBytesToReclaimUnderModeratePressure(
64     size_t bytes) {
65   AutoLock lock(lock_);
66   bytes_to_reclaim_under_moderate_pressure_ = bytes;
67 }
68 
Register(const DiscardableMemory * discardable,size_t bytes)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 
Unregister(const DiscardableMemory * discardable)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 
Acquire(const DiscardableMemory * discardable,bool * purged)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 
Release(const DiscardableMemory * discardable,scoped_ptr<uint8,FreeDeleter> memory)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 
PurgeAll()148 void DiscardableMemoryProvider::PurgeAll() {
149   AutoLock lock(lock_);
150   PurgeLRUWithLockAcquiredUntilUsageIsWithin(0);
151 }
152 
IsRegisteredForTest(const DiscardableMemory * discardable) const153 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 
CanBePurgedForTest(const DiscardableMemory * discardable) const160 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 
GetBytesAllocatedForTest() const167 size_t DiscardableMemoryProvider::GetBytesAllocatedForTest() const {
168   AutoLock lock(lock_);
169   return bytes_allocated_;
170 }
171 
Purge()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 
PurgeLRUWithLockAcquiredUntilUsageIsWithin(size_t limit)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 
EnforcePolicyWithLockAcquired()210 void DiscardableMemoryProvider::EnforcePolicyWithLockAcquired() {
211   PurgeLRUWithLockAcquiredUntilUsageIsWithin(discardable_memory_limit_);
212 }
213 
214 }  // namespace internal
215 }  // namespace base
216