Base: Fix a deadlock during shutdown of SequencedWorkerPool.
[chromium-blink-merge.git] / base / memory / discardable_memory_manager.cc
blobcbbdb4770af04494f48290fb7bfae500e5496b47
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/strings/string_number_conversions.h"
13 #include "base/synchronization/lock.h"
14 #include "base/trace_event/trace_event.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 bool DiscardableMemoryManager::ReduceMemoryUsage() {
55 return PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit();
58 void DiscardableMemoryManager::ReduceMemoryUsageUntilWithinLimit(size_t bytes) {
59 AutoLock lock(lock_);
60 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(),
61 bytes);
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));
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;
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_);
82 allocations_.Erase(it);
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;
94 if (!info->bytes)
95 return false;
97 TimeTicks now = Now();
98 size_t bytes_required = info->purgable ? 0u : info->bytes;
100 if (memory_limit_) {
101 size_t limit = 0;
102 if (bytes_required < memory_limit_)
103 limit = memory_limit_ - bytes_required;
105 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(now,
106 limit);
109 // Check for overflow.
110 if (std::numeric_limits<size_t>::max() - bytes_required < bytes_allocated_)
111 return false;
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_);
120 return true;
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;
131 TimeTicks now = Now();
132 allocation->ReleaseLock();
133 info->purgable = true;
134 info->last_usage = now;
136 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
137 now, memory_limit_);
140 void DiscardableMemoryManager::PurgeAll() {
141 AutoLock lock(lock_);
142 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(), 0);
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();
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;
159 size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const {
160 AutoLock lock(lock_);
161 return bytes_allocated_;
164 bool DiscardableMemoryManager::
165 PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit() {
166 AutoLock lock(lock_);
168 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
169 Now() - hard_memory_limit_expiration_time_, soft_memory_limit_);
171 return bytes_allocated_ <= soft_memory_limit_;
174 void DiscardableMemoryManager::
175 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
176 TimeTicks timestamp,
177 size_t limit) {
178 lock_.AssertAcquired();
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;
185 if (bytes_allocated_ <= limit)
186 break;
188 bool purgable = info->purgable && info->last_usage <= timestamp;
189 if (!purgable)
190 continue;
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();
199 if (bytes_allocated_ != bytes_allocated_before_purging)
200 BytesAllocatedChanged(bytes_allocated_);
203 void DiscardableMemoryManager::BytesAllocatedChanged(
204 size_t new_bytes_allocated) const {
205 TRACE_COUNTER_ID1(
206 "base", "DiscardableMemoryUsage", this, new_bytes_allocated);
208 static const char kDiscardableMemoryUsageKey[] = "dm-usage";
209 base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey,
210 Uint64ToString(new_bytes_allocated));
213 TimeTicks DiscardableMemoryManager::Now() const {
214 return TimeTicks::Now();
217 } // namespace internal
218 } // namespace base