| 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 |