Add UMA to identify where a hotword trigger comes from.
[chromium-blink-merge.git] / base / memory / discardable_memory_manager.cc
blobfaf583b485fded9a42c6ccfc3be43a6b110121c8
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.
5 #include "base/memory/discardable_memory_manager.h"
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/debug/trace_event.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/synchronization/lock.h"
16 namespace base {
17 namespace internal {
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_);
31 DiscardableMemoryManager::~DiscardableMemoryManager() {
32 DCHECK(allocations_.empty());
33 DCHECK_EQ(0u, bytes_allocated_);
36 void DiscardableMemoryManager::SetMemoryLimit(size_t bytes) {
37 AutoLock lock(lock_);
38 memory_limit_ = bytes;
39 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
40 Now(), memory_limit_);
43 void DiscardableMemoryManager::SetSoftMemoryLimit(size_t bytes) {
44 AutoLock lock(lock_);
45 soft_memory_limit_ = bytes;
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;
54 void DiscardableMemoryManager::ReleaseFreeMemory() {
55 TRACE_EVENT0("base", "DiscardableMemoryManager::ReleaseFreeMemory");
57 AutoLock lock(lock_);
58 size_t bytes_allocated_before_releasing_memory = bytes_allocated_;
59 for (auto& entry : allocations_) {
60 Allocation* allocation = entry.first;
61 AllocationInfo* info = &entry.second;
63 if (!info->purgable)
64 continue;
66 // Skip if memory is still resident, otherwise purge and adjust
67 // |bytes_allocated_|.
68 if (allocation->IsMemoryResident())
69 continue;
71 size_t bytes_purgable = info->bytes;
72 DCHECK_LE(bytes_purgable, bytes_allocated_);
73 bytes_allocated_ -= bytes_purgable;
74 info->purgable = false;
75 allocation->Purge();
78 if (bytes_allocated_ != bytes_allocated_before_releasing_memory)
79 BytesAllocatedChanged(bytes_allocated_);
82 bool DiscardableMemoryManager::ReduceMemoryUsage() {
83 return PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit();
86 void DiscardableMemoryManager::ReduceMemoryUsageUntilWithinLimit(size_t bytes) {
87 AutoLock lock(lock_);
88 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(),
89 bytes);
92 void DiscardableMemoryManager::Register(Allocation* allocation, size_t bytes) {
93 AutoLock lock(lock_);
94 DCHECK(allocations_.Peek(allocation) == allocations_.end());
95 allocations_.Put(allocation, AllocationInfo(bytes));
98 void DiscardableMemoryManager::Unregister(Allocation* allocation) {
99 AutoLock lock(lock_);
100 AllocationMap::iterator it = allocations_.Peek(allocation);
101 DCHECK(it != allocations_.end());
102 const AllocationInfo& info = it->second;
104 if (info.purgable) {
105 size_t bytes_purgable = info.bytes;
106 DCHECK_LE(bytes_purgable, bytes_allocated_);
107 bytes_allocated_ -= bytes_purgable;
108 BytesAllocatedChanged(bytes_allocated_);
110 allocations_.Erase(it);
113 bool DiscardableMemoryManager::AcquireLock(Allocation* allocation,
114 bool* purged) {
115 AutoLock lock(lock_);
116 // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
117 // cache.
118 AllocationMap::iterator it = allocations_.Get(allocation);
119 DCHECK(it != allocations_.end());
120 AllocationInfo* info = &it->second;
122 if (!info->bytes)
123 return false;
125 TimeTicks now = Now();
126 size_t bytes_required = info->purgable ? 0u : info->bytes;
128 if (memory_limit_) {
129 size_t limit = 0;
130 if (bytes_required < memory_limit_)
131 limit = memory_limit_ - bytes_required;
133 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(now,
134 limit);
137 // Check for overflow.
138 if (std::numeric_limits<size_t>::max() - bytes_required < bytes_allocated_)
139 return false;
141 *purged = !allocation->AllocateAndAcquireLock();
142 info->purgable = false;
143 info->last_usage = now;
144 if (bytes_required) {
145 bytes_allocated_ += bytes_required;
146 BytesAllocatedChanged(bytes_allocated_);
148 return true;
151 void DiscardableMemoryManager::ReleaseLock(Allocation* allocation) {
152 AutoLock lock(lock_);
153 // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
154 // cache.
155 AllocationMap::iterator it = allocations_.Get(allocation);
156 DCHECK(it != allocations_.end());
157 AllocationInfo* info = &it->second;
159 TimeTicks now = Now();
160 allocation->ReleaseLock();
161 info->purgable = true;
162 info->last_usage = now;
164 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
165 now, memory_limit_);
168 void DiscardableMemoryManager::PurgeAll() {
169 AutoLock lock(lock_);
170 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(), 0);
173 bool DiscardableMemoryManager::IsRegisteredForTest(
174 Allocation* allocation) const {
175 AutoLock lock(lock_);
176 AllocationMap::const_iterator it = allocations_.Peek(allocation);
177 return it != allocations_.end();
180 bool DiscardableMemoryManager::CanBePurgedForTest(
181 Allocation* allocation) const {
182 AutoLock lock(lock_);
183 AllocationMap::const_iterator it = allocations_.Peek(allocation);
184 return it != allocations_.end() && it->second.purgable;
187 size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const {
188 AutoLock lock(lock_);
189 return bytes_allocated_;
192 bool DiscardableMemoryManager::
193 PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit() {
194 AutoLock lock(lock_);
196 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
197 Now() - hard_memory_limit_expiration_time_, soft_memory_limit_);
199 return bytes_allocated_ <= soft_memory_limit_;
202 void DiscardableMemoryManager::
203 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
204 TimeTicks timestamp,
205 size_t limit) {
206 lock_.AssertAcquired();
208 size_t bytes_allocated_before_purging = bytes_allocated_;
209 for (auto& entry : base::Reversed(allocations_)) {
210 Allocation* allocation = entry.first;
211 AllocationInfo* info = &entry.second;
213 if (bytes_allocated_ <= limit)
214 break;
216 bool purgable = info->purgable && info->last_usage <= timestamp;
217 if (!purgable)
218 continue;
220 size_t bytes_purgable = info->bytes;
221 DCHECK_LE(bytes_purgable, bytes_allocated_);
222 bytes_allocated_ -= bytes_purgable;
223 info->purgable = false;
224 allocation->Purge();
227 if (bytes_allocated_ != bytes_allocated_before_purging)
228 BytesAllocatedChanged(bytes_allocated_);
231 void DiscardableMemoryManager::BytesAllocatedChanged(
232 size_t new_bytes_allocated) const {
233 TRACE_COUNTER_ID1(
234 "base", "DiscardableMemoryUsage", this, new_bytes_allocated);
236 static const char kDiscardableMemoryUsageKey[] = "dm-usage";
237 base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey,
238 Uint64ToString(new_bytes_allocated));
241 TimeTicks DiscardableMemoryManager::Now() const {
242 return TimeTicks::Now();
245 } // namespace internal
246 } // namespace base