• 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,TimeDelta hard_memory_limit_expiration_time)18 DiscardableMemoryManager::DiscardableMemoryManager(
19     size_t memory_limit,
20     size_t soft_memory_limit,
21     TimeDelta hard_memory_limit_expiration_time)
22     : allocations_(AllocationMap::NO_AUTO_EVICT),
23       bytes_allocated_(0u),
24       memory_limit_(memory_limit),
25       soft_memory_limit_(soft_memory_limit),
26       hard_memory_limit_expiration_time_(hard_memory_limit_expiration_time) {
27   BytesAllocatedChanged(bytes_allocated_);
28 }
29 
~DiscardableMemoryManager()30 DiscardableMemoryManager::~DiscardableMemoryManager() {
31   DCHECK(allocations_.empty());
32   DCHECK_EQ(0u, bytes_allocated_);
33 }
34 
SetMemoryLimit(size_t bytes)35 void DiscardableMemoryManager::SetMemoryLimit(size_t bytes) {
36   AutoLock lock(lock_);
37   memory_limit_ = bytes;
38   PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
39       Now(), memory_limit_);
40 }
41 
SetSoftMemoryLimit(size_t bytes)42 void DiscardableMemoryManager::SetSoftMemoryLimit(size_t bytes) {
43   AutoLock lock(lock_);
44   soft_memory_limit_ = bytes;
45 }
46 
SetHardMemoryLimitExpirationTime(TimeDelta hard_memory_limit_expiration_time)47 void DiscardableMemoryManager::SetHardMemoryLimitExpirationTime(
48     TimeDelta hard_memory_limit_expiration_time) {
49   AutoLock lock(lock_);
50   hard_memory_limit_expiration_time_ = hard_memory_limit_expiration_time;
51 }
52 
ReduceMemoryUsage()53 bool DiscardableMemoryManager::ReduceMemoryUsage() {
54   return PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit();
55 }
56 
ReduceMemoryUsageUntilWithinLimit(size_t bytes)57 void DiscardableMemoryManager::ReduceMemoryUsageUntilWithinLimit(size_t bytes) {
58   AutoLock lock(lock_);
59   PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(),
60                                                                       bytes);
61 }
62 
Register(Allocation * allocation,size_t bytes)63 void DiscardableMemoryManager::Register(Allocation* allocation, size_t bytes) {
64   AutoLock lock(lock_);
65   DCHECK(allocations_.Peek(allocation) == allocations_.end());
66   allocations_.Put(allocation, AllocationInfo(bytes));
67 }
68 
Unregister(Allocation * allocation)69 void DiscardableMemoryManager::Unregister(Allocation* allocation) {
70   AutoLock lock(lock_);
71   AllocationMap::iterator it = allocations_.Peek(allocation);
72   DCHECK(it != allocations_.end());
73   const AllocationInfo& info = it->second;
74 
75   if (info.purgable) {
76     size_t bytes_purgable = info.bytes;
77     DCHECK_LE(bytes_purgable, bytes_allocated_);
78     bytes_allocated_ -= bytes_purgable;
79     BytesAllocatedChanged(bytes_allocated_);
80   }
81   allocations_.Erase(it);
82 }
83 
AcquireLock(Allocation * allocation,bool * purged)84 bool DiscardableMemoryManager::AcquireLock(Allocation* allocation,
85                                            bool* purged) {
86   AutoLock lock(lock_);
87   // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
88   // cache.
89   AllocationMap::iterator it = allocations_.Get(allocation);
90   DCHECK(it != allocations_.end());
91   AllocationInfo* info = &it->second;
92 
93   if (!info->bytes)
94     return false;
95 
96   TimeTicks now = Now();
97   size_t bytes_required = info->purgable ? 0u : info->bytes;
98 
99   if (memory_limit_) {
100     size_t limit = 0;
101     if (bytes_required < memory_limit_)
102       limit = memory_limit_ - bytes_required;
103 
104     PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(now,
105                                                                         limit);
106   }
107 
108   // Check for overflow.
109   if (std::numeric_limits<size_t>::max() - bytes_required < bytes_allocated_)
110     return false;
111 
112   *purged = !allocation->AllocateAndAcquireLock();
113   info->purgable = false;
114   info->last_usage = now;
115   if (bytes_required) {
116     bytes_allocated_ += bytes_required;
117     BytesAllocatedChanged(bytes_allocated_);
118   }
119   return true;
120 }
121 
ReleaseLock(Allocation * allocation)122 void DiscardableMemoryManager::ReleaseLock(Allocation* allocation) {
123   AutoLock lock(lock_);
124   // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
125   // cache.
126   AllocationMap::iterator it = allocations_.Get(allocation);
127   DCHECK(it != allocations_.end());
128   AllocationInfo* info = &it->second;
129 
130   TimeTicks now = Now();
131   allocation->ReleaseLock();
132   info->purgable = true;
133   info->last_usage = now;
134 
135   PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
136       now, memory_limit_);
137 }
138 
PurgeAll()139 void DiscardableMemoryManager::PurgeAll() {
140   AutoLock lock(lock_);
141   PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(), 0);
142 }
143 
IsRegisteredForTest(Allocation * allocation) const144 bool DiscardableMemoryManager::IsRegisteredForTest(
145     Allocation* allocation) const {
146   AutoLock lock(lock_);
147   AllocationMap::const_iterator it = allocations_.Peek(allocation);
148   return it != allocations_.end();
149 }
150 
CanBePurgedForTest(Allocation * allocation) const151 bool DiscardableMemoryManager::CanBePurgedForTest(
152     Allocation* allocation) const {
153   AutoLock lock(lock_);
154   AllocationMap::const_iterator it = allocations_.Peek(allocation);
155   return it != allocations_.end() && it->second.purgable;
156 }
157 
GetBytesAllocatedForTest() const158 size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const {
159   AutoLock lock(lock_);
160   return bytes_allocated_;
161 }
162 
163 bool DiscardableMemoryManager::
PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit()164     PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit() {
165   AutoLock lock(lock_);
166 
167   PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
168       Now() - hard_memory_limit_expiration_time_, soft_memory_limit_);
169 
170   return bytes_allocated_ <= soft_memory_limit_;
171 }
172 
173 void DiscardableMemoryManager::
PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(TimeTicks timestamp,size_t limit)174     PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
175         TimeTicks timestamp,
176         size_t limit) {
177   lock_.AssertAcquired();
178 
179   size_t bytes_allocated_before_purging = bytes_allocated_;
180   for (AllocationMap::reverse_iterator it = allocations_.rbegin();
181        it != allocations_.rend();
182        ++it) {
183     Allocation* allocation = it->first;
184     AllocationInfo* info = &it->second;
185 
186     if (bytes_allocated_ <= limit)
187       break;
188 
189     bool purgable = info->purgable && info->last_usage <= timestamp;
190     if (!purgable)
191       continue;
192 
193     size_t bytes_purgable = info->bytes;
194     DCHECK_LE(bytes_purgable, bytes_allocated_);
195     bytes_allocated_ -= bytes_purgable;
196     info->purgable = false;
197     allocation->Purge();
198   }
199 
200   if (bytes_allocated_ != bytes_allocated_before_purging)
201     BytesAllocatedChanged(bytes_allocated_);
202 }
203 
BytesAllocatedChanged(size_t new_bytes_allocated) const204 void DiscardableMemoryManager::BytesAllocatedChanged(
205     size_t new_bytes_allocated) const {
206   TRACE_COUNTER_ID1(
207       "base", "DiscardableMemoryUsage", this, new_bytes_allocated);
208 
209   static const char kDiscardableMemoryUsageKey[] = "dm-usage";
210   base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey,
211                                 Uint64ToString(new_bytes_allocated));
212 }
213 
Now() const214 TimeTicks DiscardableMemoryManager::Now() const {
215   return TimeTicks::Now();
216 }
217 
218 }  // namespace internal
219 }  // namespace base
220