Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / base / memory / discardable_memory_manager.cc
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/debug/trace_event.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/synchronization/lock.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 void DiscardableMemoryManager::ReleaseFreeMemory() {
55   TRACE_EVENT0("base", "DiscardableMemoryManager::ReleaseFreeMemory");
56
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;
62
63     if (!info->purgable)
64       continue;
65
66     // Skip if memory is still resident, otherwise purge and adjust
67     // |bytes_allocated_|.
68     if (allocation->IsMemoryResident())
69       continue;
70
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();
76   }
77
78   if (bytes_allocated_ != bytes_allocated_before_releasing_memory)
79     BytesAllocatedChanged(bytes_allocated_);
80 }
81
82 bool DiscardableMemoryManager::ReduceMemoryUsage() {
83   return PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit();
84 }
85
86 void DiscardableMemoryManager::ReduceMemoryUsageUntilWithinLimit(size_t bytes) {
87   AutoLock lock(lock_);
88   PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(),
89                                                                       bytes);
90 }
91
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));
96 }
97
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;
103
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_);
109   }
110   allocations_.Erase(it);
111 }
112
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;
121
122   if (!info->bytes)
123     return false;
124
125   TimeTicks now = Now();
126   size_t bytes_required = info->purgable ? 0u : info->bytes;
127
128   if (memory_limit_) {
129     size_t limit = 0;
130     if (bytes_required < memory_limit_)
131       limit = memory_limit_ - bytes_required;
132
133     PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(now,
134                                                                         limit);
135   }
136
137   // Check for overflow.
138   if (std::numeric_limits<size_t>::max() - bytes_required < bytes_allocated_)
139     return false;
140
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_);
147   }
148   return true;
149 }
150
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;
158
159   TimeTicks now = Now();
160   allocation->ReleaseLock();
161   info->purgable = true;
162   info->last_usage = now;
163
164   PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
165       now, memory_limit_);
166 }
167
168 void DiscardableMemoryManager::PurgeAll() {
169   AutoLock lock(lock_);
170   PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(), 0);
171 }
172
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();
178 }
179
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;
185 }
186
187 size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const {
188   AutoLock lock(lock_);
189   return bytes_allocated_;
190 }
191
192 bool DiscardableMemoryManager::
193     PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit() {
194   AutoLock lock(lock_);
195
196   PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
197       Now() - hard_memory_limit_expiration_time_, soft_memory_limit_);
198
199   return bytes_allocated_ <= soft_memory_limit_;
200 }
201
202 void DiscardableMemoryManager::
203     PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
204         TimeTicks timestamp,
205         size_t limit) {
206   lock_.AssertAcquired();
207
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;
212
213     if (bytes_allocated_ <= limit)
214       break;
215
216     bool purgable = info->purgable && info->last_usage <= timestamp;
217     if (!purgable)
218       continue;
219
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();
225   }
226
227   if (bytes_allocated_ != bytes_allocated_before_purging)
228     BytesAllocatedChanged(bytes_allocated_);
229 }
230
231 void DiscardableMemoryManager::BytesAllocatedChanged(
232     size_t new_bytes_allocated) const {
233   TRACE_COUNTER_ID1(
234       "base", "DiscardableMemoryUsage", this, new_bytes_allocated);
235
236   static const char kDiscardableMemoryUsageKey[] = "dm-usage";
237   base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey,
238                                 Uint64ToString(new_bytes_allocated));
239 }
240
241 TimeTicks DiscardableMemoryManager::Now() const {
242   return TimeTicks::Now();
243 }
244
245 }  // namespace internal
246 }  // namespace base