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