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 |
21 base::LazyInstance<HostDiscardableSharedMemoryManager> | 61 base::LazyInstance<HostDiscardableSharedMemoryManager> |
22 g_discardable_shared_memory_manager = LAZY_INSTANCE_INITIALIZER; | 62 g_discardable_shared_memory_manager = LAZY_INSTANCE_INITIALIZER; |
23 | 63 |
24 const int64_t kMaxDefaultMemoryLimit = 512 * 1024 * 1024; | 64 const int64_t kMaxDefaultMemoryLimit = 512 * 1024 * 1024; |
25 | 65 |
26 const int kEnforceMemoryPolicyDelayMs = 1000; | 66 const int kEnforceMemoryPolicyDelayMs = 1000; |
27 | 67 |
28 } // namespace | 68 } // namespace |
29 | 69 |
30 HostDiscardableSharedMemoryManager::MemorySegment::MemorySegment( | 70 HostDiscardableSharedMemoryManager::MemorySegment::MemorySegment( |
(...skipping 23 matching lines...) Expand all Loading... |
54 } | 94 } |
55 | 95 |
56 HostDiscardableSharedMemoryManager* | 96 HostDiscardableSharedMemoryManager* |
57 HostDiscardableSharedMemoryManager::current() { | 97 HostDiscardableSharedMemoryManager::current() { |
58 return g_discardable_shared_memory_manager.Pointer(); | 98 return g_discardable_shared_memory_manager.Pointer(); |
59 } | 99 } |
60 | 100 |
61 scoped_ptr<base::DiscardableMemoryShmemChunk> | 101 scoped_ptr<base::DiscardableMemoryShmemChunk> |
62 HostDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory( | 102 HostDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory( |
63 size_t size) { | 103 size_t size) { |
64 // TODO(reveman): Need to implement this for discardable memory support in | 104 // Note: Use DiscardableSharedMemoryHeap for in-process allocation |
65 // the browser process. | 105 // of discardable memory if the cost of each allocation is too high. |
66 NOTIMPLEMENTED(); | 106 base::SharedMemoryHandle handle; |
67 return nullptr; | 107 AllocateLockedDiscardableSharedMemory(base::GetCurrentProcessHandle(), size, |
| 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())); |
68 } | 114 } |
69 | 115 |
70 void HostDiscardableSharedMemoryManager:: | 116 void HostDiscardableSharedMemoryManager:: |
71 AllocateLockedDiscardableSharedMemoryForChild( | 117 AllocateLockedDiscardableSharedMemoryForChild( |
72 base::ProcessHandle process_handle, | 118 base::ProcessHandle process_handle, |
73 size_t size, | 119 size_t size, |
74 base::SharedMemoryHandle* shared_memory_handle) { | 120 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) { |
75 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 | 170 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 |
76 // is fixed. | 171 // is fixed. |
77 tracked_objects::ScopedTracker tracking_profile1( | 172 tracked_objects::ScopedTracker tracking_profile1( |
78 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 173 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
79 "466405 AllocateLockedDiscardableSharedMemoryForChild::Start")); | 174 "466405 AllocateLockedDiscardableSharedMemory::Start")); |
80 base::AutoLock lock(lock_); | 175 base::AutoLock lock(lock_); |
81 | 176 |
82 // Memory usage must be reduced to prevent the addition of |size| from | 177 // Memory usage must be reduced to prevent the addition of |size| from |
83 // taking usage above the limit. Usage should be reduced to 0 in cases | 178 // taking usage above the limit. Usage should be reduced to 0 in cases |
84 // where |size| is greater than the limit. | 179 // where |size| is greater than the limit. |
85 size_t limit = 0; | 180 size_t limit = 0; |
86 // Note: the actual mapped size can be larger than requested and cause | 181 // Note: the actual mapped size can be larger than requested and cause |
87 // |bytes_allocated_| to temporarily be larger than |memory_limit_|. The | 182 // |bytes_allocated_| to temporarily be larger than |memory_limit_|. The |
88 // error is minimized by incrementing |bytes_allocated_| with the actual | 183 // error is minimized by incrementing |bytes_allocated_| with the actual |
89 // mapped size rather than |size| below. | 184 // mapped size rather than |size| below. |
90 if (size < memory_limit_) | 185 if (size < memory_limit_) |
91 limit = memory_limit_ - size; | 186 limit = memory_limit_ - size; |
92 | 187 |
93 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 | 188 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 |
94 // is fixed. | 189 // is fixed. |
95 tracked_objects::ScopedTracker tracking_profile2( | 190 tracked_objects::ScopedTracker tracking_profile2( |
96 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 191 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
97 "466405 " | 192 "466405 AllocateLockedDiscardableSharedMemory::ReduceMemoryUsage")); |
98 "AllocateLockedDiscardableSharedMemoryForChild::ReduceMemoryUsage")); | |
99 if (bytes_allocated_ > limit) | 193 if (bytes_allocated_ > limit) |
100 ReduceMemoryUsageUntilWithinLimit(limit); | 194 ReduceMemoryUsageUntilWithinLimit(limit); |
101 | 195 |
102 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 | 196 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 |
103 // is fixed. | 197 // is fixed. |
104 tracked_objects::ScopedTracker tracking_profile3( | 198 tracked_objects::ScopedTracker tracking_profile3( |
105 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 199 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
106 "466405 AllocateLockedDiscardableSharedMemoryForChild::NewMemory")); | 200 "466405 AllocateLockedDiscardableSharedMemory::NewMemory")); |
107 linked_ptr<base::DiscardableSharedMemory> memory( | 201 linked_ptr<base::DiscardableSharedMemory> memory( |
108 new base::DiscardableSharedMemory); | 202 new base::DiscardableSharedMemory); |
109 if (!memory->CreateAndMap(size)) { | 203 if (!memory->CreateAndMap(size)) { |
110 *shared_memory_handle = base::SharedMemory::NULLHandle(); | 204 *shared_memory_handle = base::SharedMemory::NULLHandle(); |
111 return; | 205 return; |
112 } | 206 } |
113 | 207 |
114 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 | 208 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 |
115 // is fixed. | 209 // is fixed. |
116 tracked_objects::ScopedTracker tracking_profile4( | 210 tracked_objects::ScopedTracker tracking_profile4( |
117 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 211 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
118 "466405 " | 212 "466405 AllocateLockedDiscardableSharedMemory::ShareToProcess")); |
119 "AllocateLockedDiscardableSharedMemoryForChild::ShareToProcess")); | |
120 if (!memory->ShareToProcess(process_handle, shared_memory_handle)) { | 213 if (!memory->ShareToProcess(process_handle, shared_memory_handle)) { |
121 LOG(ERROR) << "Cannot share discardable memory segment"; | 214 LOG(ERROR) << "Cannot share discardable memory segment"; |
122 *shared_memory_handle = base::SharedMemory::NULLHandle(); | 215 *shared_memory_handle = base::SharedMemory::NULLHandle(); |
123 return; | 216 return; |
124 } | 217 } |
125 | 218 |
126 base::CheckedNumeric<size_t> checked_bytes_allocated = bytes_allocated_; | 219 base::CheckedNumeric<size_t> checked_bytes_allocated = bytes_allocated_; |
127 checked_bytes_allocated += memory->mapped_size(); | 220 checked_bytes_allocated += memory->mapped_size(); |
128 if (!checked_bytes_allocated.IsValid()) { | 221 if (!checked_bytes_allocated.IsValid()) { |
129 *shared_memory_handle = base::SharedMemory::NULLHandle(); | 222 *shared_memory_handle = base::SharedMemory::NULLHandle(); |
130 return; | 223 return; |
131 } | 224 } |
132 | 225 |
133 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 | 226 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 |
134 // is fixed. | 227 // is fixed. |
135 tracked_objects::ScopedTracker tracking_profile5( | 228 tracked_objects::ScopedTracker tracking_profile5( |
136 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 229 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
137 "466405 " | 230 "466405 " |
138 "AllocateLockedDiscardableSharedMemoryForChild::" | 231 "AllocateLockedDiscardableSharedMemory::BytesAllocatedChanged")); |
139 "BytesAllocatedChanged")); | |
140 bytes_allocated_ = checked_bytes_allocated.ValueOrDie(); | 232 bytes_allocated_ = checked_bytes_allocated.ValueOrDie(); |
141 BytesAllocatedChanged(bytes_allocated_); | 233 BytesAllocatedChanged(bytes_allocated_); |
142 | 234 |
143 segments_.push_back(MemorySegment(memory, process_handle)); | 235 segments_.push_back(MemorySegment(memory, process_handle)); |
144 std::push_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime); | 236 std::push_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime); |
145 | 237 |
146 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 | 238 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405 |
147 // is fixed. | 239 // is fixed. |
148 tracked_objects::ScopedTracker tracking_profile6( | 240 tracked_objects::ScopedTracker tracking_profile6( |
149 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 241 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
150 "466405 " | 242 "466405 " |
151 "AllocateLockedDiscardableSharedMemoryForChild::" | 243 "AllocateLockedDiscardableSharedMemory::" |
152 "ScheduleEnforceMemoryPolicy")); | 244 "ScheduleEnforceMemoryPolicy")); |
153 if (bytes_allocated_ > memory_limit_) | 245 if (bytes_allocated_ > memory_limit_) |
154 ScheduleEnforceMemoryPolicy(); | 246 ScheduleEnforceMemoryPolicy(); |
155 } | 247 } |
156 | 248 |
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 | |
198 void HostDiscardableSharedMemoryManager::OnMemoryPressure( | 249 void HostDiscardableSharedMemoryManager::OnMemoryPressure( |
199 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { | 250 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { |
200 base::AutoLock lock(lock_); | 251 base::AutoLock lock(lock_); |
201 | 252 |
202 switch (memory_pressure_level) { | 253 switch (memory_pressure_level) { |
203 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: | 254 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: |
204 break; | 255 break; |
205 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: | 256 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: |
206 // Purge memory until usage is within half of |memory_limit_|. | 257 // Purge memory until usage is within half of |memory_limit_|. |
207 ReduceMemoryUsageUntilWithinLimit(memory_limit_ / 2); | 258 ReduceMemoryUsageUntilWithinLimit(memory_limit_ / 2); |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
292 | 343 |
293 enforce_memory_policy_pending_ = true; | 344 enforce_memory_policy_pending_ = true; |
294 base::MessageLoop::current()->PostDelayedTask( | 345 base::MessageLoop::current()->PostDelayedTask( |
295 FROM_HERE, | 346 FROM_HERE, |
296 base::Bind(&HostDiscardableSharedMemoryManager::EnforceMemoryPolicy, | 347 base::Bind(&HostDiscardableSharedMemoryManager::EnforceMemoryPolicy, |
297 weak_ptr_factory_.GetWeakPtr()), | 348 weak_ptr_factory_.GetWeakPtr()), |
298 base::TimeDelta::FromMilliseconds(kEnforceMemoryPolicyDelayMs)); | 349 base::TimeDelta::FromMilliseconds(kEnforceMemoryPolicyDelayMs)); |
299 } | 350 } |
300 | 351 |
301 } // namespace content | 352 } // namespace content |
OLD | NEW |