• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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_manager.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/crash_logging.h"
11 #include "base/debug/trace_event.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/synchronization/lock.h"
14 
15 namespace base {
16 namespace internal {
17 
DiscardableMemoryManager(size_t memory_limit,size_t soft_memory_limit,size_t bytes_to_keep_under_moderate_pressure,TimeDelta hard_memory_limit_expiration_time)18 DiscardableMemoryManager::DiscardableMemoryManager(
19     size_t memory_limit,
20     size_t soft_memory_limit,
21     size_t bytes_to_keep_under_moderate_pressure,
22     TimeDelta hard_memory_limit_expiration_time)
23     : allocations_(AllocationMap::NO_AUTO_EVICT),
24       bytes_allocated_(0u),
25       memory_limit_(memory_limit),
26       soft_memory_limit_(soft_memory_limit),
27       bytes_to_keep_under_moderate_pressure_(
28           bytes_to_keep_under_moderate_pressure),
29       hard_memory_limit_expiration_time_(hard_memory_limit_expiration_time) {
30   BytesAllocatedChanged(bytes_allocated_);
31 }
32 
~DiscardableMemoryManager()33 DiscardableMemoryManager::~DiscardableMemoryManager() {
34   DCHECK(allocations_.empty());
35   DCHECK_EQ(0u, bytes_allocated_);
36 }
37 
RegisterMemoryPressureListener()38 void DiscardableMemoryManager::RegisterMemoryPressureListener() {
39   AutoLock lock(lock_);
40   DCHECK(base::MessageLoop::current());
41   DCHECK(!memory_pressure_listener_);
42   memory_pressure_listener_.reset(new MemoryPressureListener(base::Bind(
43       &DiscardableMemoryManager::OnMemoryPressure, Unretained(this))));
44 }
45 
UnregisterMemoryPressureListener()46 void DiscardableMemoryManager::UnregisterMemoryPressureListener() {
47   AutoLock lock(lock_);
48   DCHECK(memory_pressure_listener_);
49   memory_pressure_listener_.reset();
50 }
51 
SetMemoryLimit(size_t bytes)52 void DiscardableMemoryManager::SetMemoryLimit(size_t bytes) {
53   AutoLock lock(lock_);
54   memory_limit_ = bytes;
55   PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
56       Now(), memory_limit_);
57 }
58 
SetSoftMemoryLimit(size_t bytes)59 void DiscardableMemoryManager::SetSoftMemoryLimit(size_t bytes) {
60   AutoLock lock(lock_);
61   soft_memory_limit_ = bytes;
62 }
63 
SetBytesToKeepUnderModeratePressure(size_t bytes)64 void DiscardableMemoryManager::SetBytesToKeepUnderModeratePressure(
65     size_t bytes) {
66   AutoLock lock(lock_);
67   bytes_to_keep_under_moderate_pressure_ = bytes;
68 }
69 
SetHardMemoryLimitExpirationTime(TimeDelta hard_memory_limit_expiration_time)70 void DiscardableMemoryManager::SetHardMemoryLimitExpirationTime(
71     TimeDelta hard_memory_limit_expiration_time) {
72   AutoLock lock(lock_);
73   hard_memory_limit_expiration_time_ = hard_memory_limit_expiration_time;
74 }
75 
ReduceMemoryUsage()76 bool DiscardableMemoryManager::ReduceMemoryUsage() {
77   return PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit();
78 }
79 
Register(Allocation * allocation,size_t bytes)80 void DiscardableMemoryManager::Register(Allocation* allocation, size_t bytes) {
81   AutoLock lock(lock_);
82   // A registered memory listener is currently required. This DCHECK can be
83   // moved or removed if we decide that it's useful to relax this condition.
84   // TODO(reveman): Enable this DCHECK when skia and blink are able to
85   // register memory pressure listeners. crbug.com/333907
86   // DCHECK(memory_pressure_listener_);
87   DCHECK(allocations_.Peek(allocation) == allocations_.end());
88   allocations_.Put(allocation, AllocationInfo(bytes));
89 }
90 
Unregister(Allocation * allocation)91 void DiscardableMemoryManager::Unregister(Allocation* allocation) {
92   AutoLock lock(lock_);
93   AllocationMap::iterator it = allocations_.Peek(allocation);
94   DCHECK(it != allocations_.end());
95   const AllocationInfo& info = it->second;
96 
97   if (info.purgable) {
98     size_t bytes_purgable = info.bytes;
99     DCHECK_LE(bytes_purgable, bytes_allocated_);
100     bytes_allocated_ -= bytes_purgable;
101     BytesAllocatedChanged(bytes_allocated_);
102   }
103   allocations_.Erase(it);
104 }
105 
AcquireLock(Allocation * allocation,bool * purged)106 bool DiscardableMemoryManager::AcquireLock(Allocation* allocation,
107                                            bool* purged) {
108   AutoLock lock(lock_);
109   // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
110   // cache.
111   AllocationMap::iterator it = allocations_.Get(allocation);
112   DCHECK(it != allocations_.end());
113   AllocationInfo* info = &it->second;
114 
115   if (!info->bytes)
116     return false;
117 
118   TimeTicks now = Now();
119   size_t bytes_required = info->purgable ? 0u : info->bytes;
120 
121   if (memory_limit_) {
122     size_t limit = 0;
123     if (bytes_required < memory_limit_)
124       limit = memory_limit_ - bytes_required;
125 
126     PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(now,
127                                                                         limit);
128   }
129 
130   // Check for overflow.
131   if (std::numeric_limits<size_t>::max() - bytes_required < bytes_allocated_)
132     return false;
133 
134   *purged = !allocation->AllocateAndAcquireLock();
135   info->purgable = false;
136   info->last_usage = now;
137   if (bytes_required) {
138     bytes_allocated_ += bytes_required;
139     BytesAllocatedChanged(bytes_allocated_);
140   }
141   return true;
142 }
143 
ReleaseLock(Allocation * allocation)144 void DiscardableMemoryManager::ReleaseLock(Allocation* allocation) {
145   AutoLock lock(lock_);
146   // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
147   // cache.
148   AllocationMap::iterator it = allocations_.Get(allocation);
149   DCHECK(it != allocations_.end());
150   AllocationInfo* info = &it->second;
151 
152   TimeTicks now = Now();
153   allocation->ReleaseLock();
154   info->purgable = true;
155   info->last_usage = now;
156 
157   PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
158       now, memory_limit_);
159 }
160 
PurgeAll()161 void DiscardableMemoryManager::PurgeAll() {
162   AutoLock lock(lock_);
163   PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(), 0);
164 }
165 
IsRegisteredForTest(Allocation * allocation) const166 bool DiscardableMemoryManager::IsRegisteredForTest(
167     Allocation* allocation) const {
168   AutoLock lock(lock_);
169   AllocationMap::const_iterator it = allocations_.Peek(allocation);
170   return it != allocations_.end();
171 }
172 
CanBePurgedForTest(Allocation * allocation) const173 bool DiscardableMemoryManager::CanBePurgedForTest(
174     Allocation* allocation) const {
175   AutoLock lock(lock_);
176   AllocationMap::const_iterator it = allocations_.Peek(allocation);
177   return it != allocations_.end() && it->second.purgable;
178 }
179 
GetBytesAllocatedForTest() const180 size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const {
181   AutoLock lock(lock_);
182   return bytes_allocated_;
183 }
184 
OnMemoryPressure(MemoryPressureListener::MemoryPressureLevel pressure_level)185 void DiscardableMemoryManager::OnMemoryPressure(
186     MemoryPressureListener::MemoryPressureLevel pressure_level) {
187   switch (pressure_level) {
188     case MemoryPressureListener::MEMORY_PRESSURE_MODERATE:
189       PurgeUntilWithinBytesToKeepUnderModeratePressure();
190       return;
191     case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL:
192       PurgeAll();
193       return;
194   }
195 
196   NOTREACHED();
197 }
198 
199 void
PurgeUntilWithinBytesToKeepUnderModeratePressure()200 DiscardableMemoryManager::PurgeUntilWithinBytesToKeepUnderModeratePressure() {
201   AutoLock lock(lock_);
202 
203   PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
204       Now(), bytes_to_keep_under_moderate_pressure_);
205 }
206 
207 bool DiscardableMemoryManager::
PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit()208     PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit() {
209   AutoLock lock(lock_);
210 
211   PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
212       Now() - hard_memory_limit_expiration_time_, soft_memory_limit_);
213 
214   return bytes_allocated_ <= soft_memory_limit_;
215 }
216 
217 void DiscardableMemoryManager::
PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(TimeTicks timestamp,size_t limit)218     PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
219         TimeTicks timestamp,
220         size_t limit) {
221   lock_.AssertAcquired();
222 
223   size_t bytes_allocated_before_purging = bytes_allocated_;
224   for (AllocationMap::reverse_iterator it = allocations_.rbegin();
225        it != allocations_.rend();
226        ++it) {
227     Allocation* allocation = it->first;
228     AllocationInfo* info = &it->second;
229 
230     if (bytes_allocated_ <= limit)
231       break;
232 
233     bool purgable = info->purgable && info->last_usage <= timestamp;
234     if (!purgable)
235       continue;
236 
237     size_t bytes_purgable = info->bytes;
238     DCHECK_LE(bytes_purgable, bytes_allocated_);
239     bytes_allocated_ -= bytes_purgable;
240     info->purgable = false;
241     allocation->Purge();
242   }
243 
244   if (bytes_allocated_ != bytes_allocated_before_purging)
245     BytesAllocatedChanged(bytes_allocated_);
246 }
247 
BytesAllocatedChanged(size_t new_bytes_allocated) const248 void DiscardableMemoryManager::BytesAllocatedChanged(
249     size_t new_bytes_allocated) const {
250   TRACE_COUNTER_ID1(
251       "base", "DiscardableMemoryUsage", this, new_bytes_allocated);
252 
253   static const char kDiscardableMemoryUsageKey[] = "dm-usage";
254   base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey,
255                                 Uint64ToString(new_bytes_allocated));
256 }
257 
Now() const258 TimeTicks DiscardableMemoryManager::Now() const {
259   return TimeTicks::Now();
260 }
261 
262 }  // namespace internal
263 }  // namespace base
264