OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/common/host_discardable_shared_memory_manager.h" | 5 #include "content/common/host_discardable_shared_memory_manager.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/callback.h" | 9 #include "base/callback.h" |
10 #include "base/debug/crash_logging.h" | 10 #include "base/debug/crash_logging.h" |
11 #include "base/lazy_instance.h" | 11 #include "base/lazy_instance.h" |
12 #include "base/numerics/safe_math.h" | 12 #include "base/numerics/safe_math.h" |
13 #include "base/profiler/scoped_tracker.h" | 13 #include "base/profiler/scoped_tracker.h" |
14 #include "base/strings/string_number_conversions.h" | 14 #include "base/strings/string_number_conversions.h" |
15 #include "base/sys_info.h" | 15 #include "base/sys_info.h" |
16 #include "base/trace_event/trace_event.h" | 16 #include "base/trace_event/trace_event.h" |
17 | 17 |
18 namespace content { | 18 namespace content { |
19 namespace { | 19 namespace { |
20 | 20 |
21 class DiscardableMemoryShmemChunkImpl | |
22 : public base::DiscardableMemoryShmemChunk { | |
23 public: | |
24 explicit DiscardableMemoryShmemChunkImpl( | |
25 scoped_ptr<base::DiscardableSharedMemory> shared_memory) | |
26 : shared_memory_(shared_memory.Pass()), is_locked_(true) {} | |
27 ~DiscardableMemoryShmemChunkImpl() override { | |
28 if (is_locked_) | |
29 shared_memory_->Unlock(0, 0); | |
30 shared_memory_->Purge(base::Time::Now()); | |
31 } | |
32 | |
33 // Overridden from base::DiscardableMemoryShmemChunk: | |
34 bool Lock() override { | |
35 DCHECK(!is_locked_); | |
36 | |
37 if (shared_memory_->Lock(0, 0) != base::DiscardableSharedMemory::SUCCESS) | |
38 return false; | |
39 | |
40 is_locked_ = true; | |
41 return true; | |
42 } | |
43 void Unlock() override { | |
44 DCHECK(is_locked_); | |
45 | |
46 shared_memory_->Unlock(0, 0); | |
47 is_locked_ = false; | |
48 } | |
49 void* Memory() const override { | |
50 DCHECK(is_locked_); | |
51 return shared_memory_->memory(); | |
52 } | |
53 | |
54 private: | |
55 scoped_ptr<base::DiscardableSharedMemory> shared_memory_; | |
56 bool is_locked_; | |
57 | |
58 DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryShmemChunkImpl); | |
59 }; | |
60 | |
61 base::LazyInstance<HostDiscardableSharedMemoryManager> | 21 base::LazyInstance<HostDiscardableSharedMemoryManager> |
62 g_discardable_shared_memory_manager = LAZY_INSTANCE_INITIALIZER; | 22 g_discardable_shared_memory_manager = LAZY_INSTANCE_INITIALIZER; |
63 | 23 |
64 const int64_t kMaxDefaultMemoryLimit = 512 * 1024 * 1024; | 24 const int64_t kMaxDefaultMemoryLimit = 512 * 1024 * 1024; |
65 | 25 |
66 const int kEnforceMemoryPolicyDelayMs = 1000; | 26 const int kEnforceMemoryPolicyDelayMs = 1000; |
67 | 27 |
68 } // namespace | 28 } // namespace |
69 | 29 |
70 HostDiscardableSharedMemoryManager::MemorySegment::MemorySegment( | 30 HostDiscardableSharedMemoryManager::MemorySegment::MemorySegment( |
(...skipping 23 matching lines...) Expand all Loading... |
94 } | 54 } |
95 | 55 |
96 HostDiscardableSharedMemoryManager* | 56 HostDiscardableSharedMemoryManager* |
97 HostDiscardableSharedMemoryManager::current() { | 57 HostDiscardableSharedMemoryManager::current() { |
98 return g_discardable_shared_memory_manager.Pointer(); | 58 return g_discardable_shared_memory_manager.Pointer(); |
99 } | 59 } |
100 | 60 |
101 scoped_ptr<base::DiscardableMemoryShmemChunk> | 61 scoped_ptr<base::DiscardableMemoryShmemChunk> |
102 HostDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory( | 62 HostDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory( |
103 size_t size) { | 63 size_t size) { |
104 // Note: Use DiscardableSharedMemoryHeap for in-process allocation | 64 // TODO(reveman): Need to implement this for discardable memory support in |
105 // of discardable memory if the cost of each allocation is too high. | 65 // the browser process. |
106 base::SharedMemoryHandle handle; | 66 NOTIMPLEMENTED(); |
107 AllocateLockedDiscardableSharedMemory(base::GetCurrentProcessHandle(), size, | 67 return nullptr; |
108 &handle); | |
109 CHECK(base::SharedMemory::IsHandleValid(handle)); | |
110 scoped_ptr<base::DiscardableSharedMemory> memory( | |
111 new base::DiscardableSharedMemory(handle)); | |
112 CHECK(memory->Map(size)); | |
113 return make_scoped_ptr(new DiscardableMemoryShmemChunkImpl(memory.Pass())); | |
114 } | 68 } |
115 | 69 |
116 void HostDiscardableSharedMemoryManager:: | 70 void HostDiscardableSharedMemoryManager:: |
117 AllocateLockedDiscardableSharedMemoryForChild( | 71 AllocateLockedDiscardableSharedMemoryForChild( |
118 base::ProcessHandle process_handle, | 72 base::ProcessHandle process_handle, |
119 size_t size, | 73 size_t size, |
120 base::SharedMemoryHandle* shared_memory_handle) { | 74 base::SharedMemoryHandle* shared_memory_handle) { |
121 AllocateLockedDiscardableSharedMemory(process_handle, size, | |
122 shared_memory_handle); | |
123 } | |
124 | |
125 void HostDiscardableSharedMemoryManager::ProcessRemoved( | |
126 base::ProcessHandle process_handle) { | |
127 base::AutoLock lock(lock_); | |
128 | |
129 size_t bytes_allocated_before_purging = bytes_allocated_; | |
130 for (auto& segment : segments_) { | |
131 // Skip segments that belong to a different process. | |
132 if (segment.process_handle != process_handle) | |
133 continue; | |
134 | |
135 size_t size = segment.memory->mapped_size(); | |
136 DCHECK_GE(bytes_allocated_, size); | |
137 | |
138 // This will unmap the memory segment and drop our reference. The result | |
139 // is that the memory will be released to the OS if the child process is | |
140 // no longer referencing it. | |
141 // Note: We intentionally leave the segment in the vector to avoid | |
142 // reconstructing the heap. The element will be removed from the heap | |
143 // when its last usage time is older than all other segments. | |
144 segment.memory->Close(); | |
145 bytes_allocated_ -= size; | |
146 } | |
147 | |
148 if (bytes_allocated_ != bytes_allocated_before_purging) | |
149 BytesAllocatedChanged(bytes_allocated_); | |
150 } | |
151 | |
152 void HostDiscardableSharedMemoryManager::SetMemoryLimit(size_t limit) { | |
153 base::AutoLock lock(lock_); | |
154 | |
155 memory_limit_ = limit; | |
156 ReduceMemoryUsageUntilWithinMemoryLimit(); | |
157 } | |
158 | |
159 void HostDiscardableSharedMemoryManager::EnforceMemoryPolicy() { | |
160 base::AutoLock lock(lock_); | |
161 | |
162 enforce_memory_policy_pending_ = false; | |
163 ReduceMemoryUsageUntilWithinMemoryLimit(); | |
164 } | |
165 | |
166 void HostDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory( | |
167 base::ProcessHandle process_handle, | |
168 size_t size, | |
169 base::SharedMemoryHandle* shared_memory_handle) { | |
170 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 | 75 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 |
171 // is fixed. | 76 // is fixed. |
172 tracked_objects::ScopedTracker tracking_profile1( | 77 tracked_objects::ScopedTracker tracking_profile1( |
173 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 78 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
174 "466405 AllocateLockedDiscardableSharedMemory::Start")); | 79 "466405 AllocateLockedDiscardableSharedMemoryForChild::Start")); |
175 base::AutoLock lock(lock_); | 80 base::AutoLock lock(lock_); |
176 | 81 |
177 // Memory usage must be reduced to prevent the addition of |size| from | 82 // Memory usage must be reduced to prevent the addition of |size| from |
178 // taking usage above the limit. Usage should be reduced to 0 in cases | 83 // taking usage above the limit. Usage should be reduced to 0 in cases |
179 // where |size| is greater than the limit. | 84 // where |size| is greater than the limit. |
180 size_t limit = 0; | 85 size_t limit = 0; |
181 // Note: the actual mapped size can be larger than requested and cause | 86 // Note: the actual mapped size can be larger than requested and cause |
182 // |bytes_allocated_| to temporarily be larger than |memory_limit_|. The | 87 // |bytes_allocated_| to temporarily be larger than |memory_limit_|. The |
183 // error is minimized by incrementing |bytes_allocated_| with the actual | 88 // error is minimized by incrementing |bytes_allocated_| with the actual |
184 // mapped size rather than |size| below. | 89 // mapped size rather than |size| below. |
185 if (size < memory_limit_) | 90 if (size < memory_limit_) |
186 limit = memory_limit_ - size; | 91 limit = memory_limit_ - size; |
187 | 92 |
188 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 | 93 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 |
189 // is fixed. | 94 // is fixed. |
190 tracked_objects::ScopedTracker tracking_profile2( | 95 tracked_objects::ScopedTracker tracking_profile2( |
191 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 96 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
192 "466405 AllocateLockedDiscardableSharedMemory::ReduceMemoryUsage")); | 97 "466405 " |
| 98 "AllocateLockedDiscardableSharedMemoryForChild::ReduceMemoryUsage")); |
193 if (bytes_allocated_ > limit) | 99 if (bytes_allocated_ > limit) |
194 ReduceMemoryUsageUntilWithinLimit(limit); | 100 ReduceMemoryUsageUntilWithinLimit(limit); |
195 | 101 |
196 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 | 102 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 |
197 // is fixed. | 103 // is fixed. |
198 tracked_objects::ScopedTracker tracking_profile3( | 104 tracked_objects::ScopedTracker tracking_profile3( |
199 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 105 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
200 "466405 AllocateLockedDiscardableSharedMemory::NewMemory")); | 106 "466405 AllocateLockedDiscardableSharedMemoryForChild::NewMemory")); |
201 linked_ptr<base::DiscardableSharedMemory> memory( | 107 linked_ptr<base::DiscardableSharedMemory> memory( |
202 new base::DiscardableSharedMemory); | 108 new base::DiscardableSharedMemory); |
203 if (!memory->CreateAndMap(size)) { | 109 if (!memory->CreateAndMap(size)) { |
204 *shared_memory_handle = base::SharedMemory::NULLHandle(); | 110 *shared_memory_handle = base::SharedMemory::NULLHandle(); |
205 return; | 111 return; |
206 } | 112 } |
207 | 113 |
208 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 | 114 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 |
209 // is fixed. | 115 // is fixed. |
210 tracked_objects::ScopedTracker tracking_profile4( | 116 tracked_objects::ScopedTracker tracking_profile4( |
211 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 117 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
212 "466405 AllocateLockedDiscardableSharedMemory::ShareToProcess")); | 118 "466405 " |
| 119 "AllocateLockedDiscardableSharedMemoryForChild::ShareToProcess")); |
213 if (!memory->ShareToProcess(process_handle, shared_memory_handle)) { | 120 if (!memory->ShareToProcess(process_handle, shared_memory_handle)) { |
214 LOG(ERROR) << "Cannot share discardable memory segment"; | 121 LOG(ERROR) << "Cannot share discardable memory segment"; |
215 *shared_memory_handle = base::SharedMemory::NULLHandle(); | 122 *shared_memory_handle = base::SharedMemory::NULLHandle(); |
216 return; | 123 return; |
217 } | 124 } |
218 | 125 |
219 base::CheckedNumeric<size_t> checked_bytes_allocated = bytes_allocated_; | 126 base::CheckedNumeric<size_t> checked_bytes_allocated = bytes_allocated_; |
220 checked_bytes_allocated += memory->mapped_size(); | 127 checked_bytes_allocated += memory->mapped_size(); |
221 if (!checked_bytes_allocated.IsValid()) { | 128 if (!checked_bytes_allocated.IsValid()) { |
222 *shared_memory_handle = base::SharedMemory::NULLHandle(); | 129 *shared_memory_handle = base::SharedMemory::NULLHandle(); |
223 return; | 130 return; |
224 } | 131 } |
225 | 132 |
226 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 | 133 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 |
227 // is fixed. | 134 // is fixed. |
228 tracked_objects::ScopedTracker tracking_profile5( | 135 tracked_objects::ScopedTracker tracking_profile5( |
229 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 136 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
230 "466405 " | 137 "466405 " |
231 "AllocateLockedDiscardableSharedMemory::BytesAllocatedChanged")); | 138 "AllocateLockedDiscardableSharedMemoryForChild::" |
| 139 "BytesAllocatedChanged")); |
232 bytes_allocated_ = checked_bytes_allocated.ValueOrDie(); | 140 bytes_allocated_ = checked_bytes_allocated.ValueOrDie(); |
233 BytesAllocatedChanged(bytes_allocated_); | 141 BytesAllocatedChanged(bytes_allocated_); |
234 | 142 |
235 segments_.push_back(MemorySegment(memory, process_handle)); | 143 segments_.push_back(MemorySegment(memory, process_handle)); |
236 std::push_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime); | 144 std::push_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime); |
237 | 145 |
238 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 | 146 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 |
239 // is fixed. | 147 // is fixed. |
240 tracked_objects::ScopedTracker tracking_profile6( | 148 tracked_objects::ScopedTracker tracking_profile6( |
241 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 149 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
242 "466405 " | 150 "466405 " |
243 "AllocateLockedDiscardableSharedMemory::" | 151 "AllocateLockedDiscardableSharedMemoryForChild::" |
244 "ScheduleEnforceMemoryPolicy")); | 152 "ScheduleEnforceMemoryPolicy")); |
245 if (bytes_allocated_ > memory_limit_) | 153 if (bytes_allocated_ > memory_limit_) |
246 ScheduleEnforceMemoryPolicy(); | 154 ScheduleEnforceMemoryPolicy(); |
247 } | 155 } |
248 | 156 |
| 157 void HostDiscardableSharedMemoryManager::ProcessRemoved( |
| 158 base::ProcessHandle process_handle) { |
| 159 base::AutoLock lock(lock_); |
| 160 |
| 161 size_t bytes_allocated_before_purging = bytes_allocated_; |
| 162 for (auto& segment : segments_) { |
| 163 // Skip segments that belong to a different process. |
| 164 if (segment.process_handle != process_handle) |
| 165 continue; |
| 166 |
| 167 size_t size = segment.memory->mapped_size(); |
| 168 DCHECK_GE(bytes_allocated_, size); |
| 169 |
| 170 // This will unmap the memory segment and drop our reference. The result |
| 171 // is that the memory will be released to the OS if the child process is |
| 172 // no longer referencing it. |
| 173 // Note: We intentionally leave the segment in the vector to avoid |
| 174 // reconstructing the heap. The element will be removed from the heap |
| 175 // when its last usage time is older than all other segments. |
| 176 segment.memory->Close(); |
| 177 bytes_allocated_ -= size; |
| 178 } |
| 179 |
| 180 if (bytes_allocated_ != bytes_allocated_before_purging) |
| 181 BytesAllocatedChanged(bytes_allocated_); |
| 182 } |
| 183 |
| 184 void HostDiscardableSharedMemoryManager::SetMemoryLimit(size_t limit) { |
| 185 base::AutoLock lock(lock_); |
| 186 |
| 187 memory_limit_ = limit; |
| 188 ReduceMemoryUsageUntilWithinMemoryLimit(); |
| 189 } |
| 190 |
| 191 void HostDiscardableSharedMemoryManager::EnforceMemoryPolicy() { |
| 192 base::AutoLock lock(lock_); |
| 193 |
| 194 enforce_memory_policy_pending_ = false; |
| 195 ReduceMemoryUsageUntilWithinMemoryLimit(); |
| 196 } |
| 197 |
249 void HostDiscardableSharedMemoryManager::OnMemoryPressure( | 198 void HostDiscardableSharedMemoryManager::OnMemoryPressure( |
250 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { | 199 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { |
251 base::AutoLock lock(lock_); | 200 base::AutoLock lock(lock_); |
252 | 201 |
253 switch (memory_pressure_level) { | 202 switch (memory_pressure_level) { |
254 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: | 203 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: |
255 break; | 204 break; |
256 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: | 205 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: |
257 // Purge memory until usage is within half of |memory_limit_|. | 206 // Purge memory until usage is within half of |memory_limit_|. |
258 ReduceMemoryUsageUntilWithinLimit(memory_limit_ / 2); | 207 ReduceMemoryUsageUntilWithinLimit(memory_limit_ / 2); |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
343 | 292 |
344 enforce_memory_policy_pending_ = true; | 293 enforce_memory_policy_pending_ = true; |
345 base::MessageLoop::current()->PostDelayedTask( | 294 base::MessageLoop::current()->PostDelayedTask( |
346 FROM_HERE, | 295 FROM_HERE, |
347 base::Bind(&HostDiscardableSharedMemoryManager::EnforceMemoryPolicy, | 296 base::Bind(&HostDiscardableSharedMemoryManager::EnforceMemoryPolicy, |
348 weak_ptr_factory_.GetWeakPtr()), | 297 weak_ptr_factory_.GetWeakPtr()), |
349 base::TimeDelta::FromMilliseconds(kEnforceMemoryPolicyDelayMs)); | 298 base::TimeDelta::FromMilliseconds(kEnforceMemoryPolicyDelayMs)); |
350 } | 299 } |
351 | 300 |
352 } // namespace content | 301 } // namespace content |
OLD | NEW |