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