Upstream version 8.37.180.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/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
18 DiscardableMemoryManager::DiscardableMemoryManager(
19     size_t memory_limit,
20     size_t bytes_to_keep_under_moderate_pressure)
21     : allocations_(AllocationMap::NO_AUTO_EVICT),
22       bytes_allocated_(0),
23       memory_limit_(memory_limit),
24       bytes_to_keep_under_moderate_pressure_(
25           bytes_to_keep_under_moderate_pressure) {
26   BytesAllocatedChanged();
27 }
28
29 DiscardableMemoryManager::~DiscardableMemoryManager() {
30   DCHECK(allocations_.empty());
31   DCHECK_EQ(0u, bytes_allocated_);
32 }
33
34 void DiscardableMemoryManager::RegisterMemoryPressureListener() {
35   AutoLock lock(lock_);
36   DCHECK(base::MessageLoop::current());
37   DCHECK(!memory_pressure_listener_);
38   memory_pressure_listener_.reset(new MemoryPressureListener(base::Bind(
39       &DiscardableMemoryManager::OnMemoryPressure, Unretained(this))));
40 }
41
42 void DiscardableMemoryManager::UnregisterMemoryPressureListener() {
43   AutoLock lock(lock_);
44   DCHECK(memory_pressure_listener_);
45   memory_pressure_listener_.reset();
46 }
47
48 void DiscardableMemoryManager::SetMemoryLimit(size_t bytes) {
49   AutoLock lock(lock_);
50   memory_limit_ = bytes;
51   EnforcePolicyWithLockAcquired();
52 }
53
54 void DiscardableMemoryManager::SetBytesToKeepUnderModeratePressure(
55     size_t bytes) {
56   AutoLock lock(lock_);
57   bytes_to_keep_under_moderate_pressure_ = bytes;
58 }
59
60 void DiscardableMemoryManager::Register(Allocation* allocation, size_t bytes) {
61   AutoLock lock(lock_);
62   // A registered memory listener is currently required. This DCHECK can be
63   // moved or removed if we decide that it's useful to relax this condition.
64   // TODO(reveman): Enable this DCHECK when skia and blink are able to
65   // register memory pressure listeners. crbug.com/333907
66   // DCHECK(memory_pressure_listener_);
67   DCHECK(allocations_.Peek(allocation) == allocations_.end());
68   allocations_.Put(allocation, AllocationInfo(bytes));
69 }
70
71 void DiscardableMemoryManager::Unregister(Allocation* allocation) {
72   AutoLock lock(lock_);
73   AllocationMap::iterator it = allocations_.Peek(allocation);
74   DCHECK(it != allocations_.end());
75   const AllocationInfo& info = it->second;
76
77   if (info.purgable) {
78     size_t bytes_purgable = info.bytes;
79     DCHECK_LE(bytes_purgable, bytes_allocated_);
80     bytes_allocated_ -= bytes_purgable;
81     BytesAllocatedChanged();
82   }
83   allocations_.Erase(it);
84 }
85
86 bool DiscardableMemoryManager::AcquireLock(Allocation* allocation,
87                                            bool* purged) {
88   AutoLock lock(lock_);
89   // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
90   // cache.
91   AllocationMap::iterator it = allocations_.Get(allocation);
92   DCHECK(it != allocations_.end());
93   AllocationInfo* info = &it->second;
94
95   if (!info->bytes)
96     return false;
97
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     PurgeLRUWithLockAcquiredUntilUsageIsWithin(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   if (bytes_required) {
115     bytes_allocated_ += bytes_required;
116     BytesAllocatedChanged();
117   }
118   return true;
119 }
120
121 void DiscardableMemoryManager::ReleaseLock(Allocation* allocation) {
122   AutoLock lock(lock_);
123   // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
124   // cache.
125   AllocationMap::iterator it = allocations_.Get(allocation);
126   DCHECK(it != allocations_.end());
127   AllocationInfo* info = &it->second;
128
129   allocation->ReleaseLock();
130   info->purgable = true;
131   EnforcePolicyWithLockAcquired();
132 }
133
134 void DiscardableMemoryManager::PurgeAll() {
135   AutoLock lock(lock_);
136   PurgeLRUWithLockAcquiredUntilUsageIsWithin(0);
137 }
138
139 bool DiscardableMemoryManager::IsRegisteredForTest(
140     Allocation* allocation) const {
141   AutoLock lock(lock_);
142   AllocationMap::const_iterator it = allocations_.Peek(allocation);
143   return it != allocations_.end();
144 }
145
146 bool DiscardableMemoryManager::CanBePurgedForTest(
147     Allocation* allocation) const {
148   AutoLock lock(lock_);
149   AllocationMap::const_iterator it = allocations_.Peek(allocation);
150   return it != allocations_.end() && it->second.purgable;
151 }
152
153 size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const {
154   AutoLock lock(lock_);
155   return bytes_allocated_;
156 }
157
158 void DiscardableMemoryManager::OnMemoryPressure(
159     MemoryPressureListener::MemoryPressureLevel pressure_level) {
160   switch (pressure_level) {
161     case MemoryPressureListener::MEMORY_PRESSURE_MODERATE:
162       Purge();
163       return;
164     case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL:
165       PurgeAll();
166       return;
167   }
168
169   NOTREACHED();
170 }
171
172 void DiscardableMemoryManager::Purge() {
173   AutoLock lock(lock_);
174
175   PurgeLRUWithLockAcquiredUntilUsageIsWithin(
176       bytes_to_keep_under_moderate_pressure_);
177 }
178
179 void DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin(
180     size_t limit) {
181   TRACE_EVENT1(
182       "base",
183       "DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin",
184       "limit",
185       limit);
186
187   lock_.AssertAcquired();
188
189   size_t bytes_allocated_before_purging = bytes_allocated_;
190   for (AllocationMap::reverse_iterator it = allocations_.rbegin();
191        it != allocations_.rend();
192        ++it) {
193     Allocation* allocation = it->first;
194     AllocationInfo* info = &it->second;
195
196     if (bytes_allocated_ <= limit)
197       break;
198     if (!info->purgable)
199       continue;
200
201     size_t bytes_purgable = info->bytes;
202     DCHECK_LE(bytes_purgable, bytes_allocated_);
203     bytes_allocated_ -= bytes_purgable;
204     info->purgable = false;
205     allocation->Purge();
206   }
207
208   if (bytes_allocated_ != bytes_allocated_before_purging)
209     BytesAllocatedChanged();
210 }
211
212 void DiscardableMemoryManager::EnforcePolicyWithLockAcquired() {
213   PurgeLRUWithLockAcquiredUntilUsageIsWithin(memory_limit_);
214 }
215
216 void DiscardableMemoryManager::BytesAllocatedChanged() const {
217   TRACE_COUNTER_ID1("base", "DiscardableMemoryUsage", this, bytes_allocated_);
218
219   static const char kDiscardableMemoryUsageKey[] = "dm-usage";
220   base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey,
221                                 Uint64ToString(bytes_allocated_));
222 }
223
224 }  // namespace internal
225 }  // namespace base