| Index: content/common/host_discardable_shared_memory_manager.cc
|
| diff --git a/content/common/host_discardable_shared_memory_manager.cc b/content/common/host_discardable_shared_memory_manager.cc
|
| index 3d2be9d22c1e32bcad935e4cc8694b60e1282df7..ab74ef0b156ada35e31d3d9cc247fbe9e103f230 100644
|
| --- a/content/common/host_discardable_shared_memory_manager.cc
|
| +++ b/content/common/host_discardable_shared_memory_manager.cc
|
| @@ -6,6 +6,8 @@
|
|
|
| #include <algorithm>
|
|
|
| +#include "base/atomic_sequence_num.h"
|
| +#include "base/bind.h"
|
| #include "base/callback.h"
|
| #include "base/debug/crash_logging.h"
|
| #include "base/lazy_instance.h"
|
| @@ -21,13 +23,17 @@ namespace {
|
| class DiscardableMemoryShmemChunkImpl
|
| : public base::DiscardableMemoryShmemChunk {
|
| public:
|
| - explicit DiscardableMemoryShmemChunkImpl(
|
| - scoped_ptr<base::DiscardableSharedMemory> shared_memory)
|
| - : shared_memory_(shared_memory.Pass()), is_locked_(true) {}
|
| + DiscardableMemoryShmemChunkImpl(
|
| + scoped_ptr<base::DiscardableSharedMemory> shared_memory,
|
| + const base::Closure& deleted_callback)
|
| + : shared_memory_(shared_memory.Pass()),
|
| + deleted_callback_(deleted_callback),
|
| + is_locked_(true) {}
|
| ~DiscardableMemoryShmemChunkImpl() override {
|
| if (is_locked_)
|
| shared_memory_->Unlock(0, 0);
|
| - shared_memory_->Purge(base::Time::Now());
|
| +
|
| + deleted_callback_.Run();
|
| }
|
|
|
| // Overridden from base::DiscardableMemoryShmemChunk:
|
| @@ -53,6 +59,7 @@ class DiscardableMemoryShmemChunkImpl
|
|
|
| private:
|
| scoped_ptr<base::DiscardableSharedMemory> shared_memory_;
|
| + const base::Closure deleted_callback_;
|
| bool is_locked_;
|
|
|
| DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryShmemChunkImpl);
|
| @@ -65,12 +72,14 @@ const int64_t kMaxDefaultMemoryLimit = 512 * 1024 * 1024;
|
|
|
| const int kEnforceMemoryPolicyDelayMs = 1000;
|
|
|
| +// Global atomic to generate unique discardable shared memory IDs.
|
| +base::StaticAtomicSequenceNumber g_next_discardable_shared_memory_id;
|
| +
|
| } // namespace
|
|
|
| HostDiscardableSharedMemoryManager::MemorySegment::MemorySegment(
|
| - linked_ptr<base::DiscardableSharedMemory> memory,
|
| - base::ProcessHandle process_handle)
|
| - : memory(memory), process_handle(process_handle) {
|
| + scoped_ptr<base::DiscardableSharedMemory> memory)
|
| + : memory_(memory.Pass()) {
|
| }
|
|
|
| HostDiscardableSharedMemoryManager::MemorySegment::~MemorySegment() {
|
| @@ -101,51 +110,58 @@ HostDiscardableSharedMemoryManager::current() {
|
| scoped_ptr<base::DiscardableMemoryShmemChunk>
|
| HostDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
|
| size_t size) {
|
| + DiscardableSharedMemoryId new_id =
|
| + g_next_discardable_shared_memory_id.GetNext();
|
| + base::ProcessHandle current_process_handle = base::GetCurrentProcessHandle();
|
| +
|
| // Note: Use DiscardableSharedMemoryHeap for in-process allocation
|
| // of discardable memory if the cost of each allocation is too high.
|
| base::SharedMemoryHandle handle;
|
| - AllocateLockedDiscardableSharedMemory(base::GetCurrentProcessHandle(), size,
|
| + AllocateLockedDiscardableSharedMemory(current_process_handle, size, new_id,
|
| &handle);
|
| CHECK(base::SharedMemory::IsHandleValid(handle));
|
| scoped_ptr<base::DiscardableSharedMemory> memory(
|
| new base::DiscardableSharedMemory(handle));
|
| CHECK(memory->Map(size));
|
| - return make_scoped_ptr(new DiscardableMemoryShmemChunkImpl(memory.Pass()));
|
| + return make_scoped_ptr(new DiscardableMemoryShmemChunkImpl(
|
| + memory.Pass(),
|
| + base::Bind(
|
| + &HostDiscardableSharedMemoryManager::DeletedDiscardableSharedMemory,
|
| + base::Unretained(this), new_id, current_process_handle)));
|
| }
|
|
|
| void HostDiscardableSharedMemoryManager::
|
| AllocateLockedDiscardableSharedMemoryForChild(
|
| base::ProcessHandle process_handle,
|
| size_t size,
|
| + DiscardableSharedMemoryId id,
|
| base::SharedMemoryHandle* shared_memory_handle) {
|
| - AllocateLockedDiscardableSharedMemory(process_handle, size,
|
| + AllocateLockedDiscardableSharedMemory(process_handle, size, id,
|
| shared_memory_handle);
|
| }
|
|
|
| +void HostDiscardableSharedMemoryManager::ChildDeletedDiscardableSharedMemory(
|
| + DiscardableSharedMemoryId id,
|
| + base::ProcessHandle process_handle) {
|
| + DeletedDiscardableSharedMemory(id, process_handle);
|
| +}
|
| +
|
| void HostDiscardableSharedMemoryManager::ProcessRemoved(
|
| base::ProcessHandle process_handle) {
|
| base::AutoLock lock(lock_);
|
|
|
| - size_t bytes_allocated_before_purging = bytes_allocated_;
|
| - for (auto& segment : segments_) {
|
| - // Skip segments that belong to a different process.
|
| - if (segment.process_handle != process_handle)
|
| - continue;
|
| + ProcessMap::iterator process_it = processes_.find(process_handle);
|
| + if (process_it == processes_.end())
|
| + return;
|
|
|
| - size_t size = segment.memory->mapped_size();
|
| - DCHECK_GE(bytes_allocated_, size);
|
| -
|
| - // This will unmap the memory segment and drop our reference. The result
|
| - // is that the memory will be released to the OS if the child process is
|
| - // no longer referencing it.
|
| - // Note: We intentionally leave the segment in the vector to avoid
|
| - // reconstructing the heap. The element will be removed from the heap
|
| - // when its last usage time is older than all other segments.
|
| - segment.memory->Close();
|
| - bytes_allocated_ -= size;
|
| - }
|
| + size_t bytes_allocated_before_releasing_memory = bytes_allocated_;
|
|
|
| - if (bytes_allocated_ != bytes_allocated_before_purging)
|
| + for (auto& segment_it : process_it->second)
|
| + ReleaseMemory(segment_it.second->memory());
|
| +
|
| + processes_.erase(process_it);
|
| +
|
| + if (bytes_allocated_ != bytes_allocated_before_releasing_memory)
|
| BytesAllocatedChanged(bytes_allocated_);
|
| }
|
|
|
| @@ -163,9 +179,16 @@ void HostDiscardableSharedMemoryManager::EnforceMemoryPolicy() {
|
| ReduceMemoryUsageUntilWithinMemoryLimit();
|
| }
|
|
|
| +size_t HostDiscardableSharedMemoryManager::GetBytesAllocated() {
|
| + base::AutoLock lock(lock_);
|
| +
|
| + return bytes_allocated_;
|
| +}
|
| +
|
| void HostDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
|
| base::ProcessHandle process_handle,
|
| size_t size,
|
| + DiscardableSharedMemoryId id,
|
| base::SharedMemoryHandle* shared_memory_handle) {
|
| // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405
|
| // is fixed.
|
| @@ -174,6 +197,14 @@ void HostDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
|
| "466405 AllocateLockedDiscardableSharedMemory::Start"));
|
| base::AutoLock lock(lock_);
|
|
|
| + // Make sure |id| is not already in use.
|
| + MemorySegmentMap& process_segments = processes_[process_handle];
|
| + if (process_segments.find(id) != process_segments.end()) {
|
| + LOG(ERROR) << "Invalid discardable shared memory ID";
|
| + *shared_memory_handle = base::SharedMemory::NULLHandle();
|
| + return;
|
| + }
|
| +
|
| // Memory usage must be reduced to prevent the addition of |size| from
|
| // taking usage above the limit. Usage should be reduced to 0 in cases
|
| // where |size| is greater than the limit.
|
| @@ -198,7 +229,7 @@ void HostDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
|
| tracked_objects::ScopedTracker tracking_profile3(
|
| FROM_HERE_WITH_EXPLICIT_FUNCTION(
|
| "466405 AllocateLockedDiscardableSharedMemory::NewMemory"));
|
| - linked_ptr<base::DiscardableSharedMemory> memory(
|
| + scoped_ptr<base::DiscardableSharedMemory> memory(
|
| new base::DiscardableSharedMemory);
|
| if (!memory->CreateAndMap(size)) {
|
| *shared_memory_handle = base::SharedMemory::NULLHandle();
|
| @@ -232,7 +263,9 @@ void HostDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
|
| bytes_allocated_ = checked_bytes_allocated.ValueOrDie();
|
| BytesAllocatedChanged(bytes_allocated_);
|
|
|
| - segments_.push_back(MemorySegment(memory, process_handle));
|
| + scoped_refptr<MemorySegment> segment(new MemorySegment(memory.Pass()));
|
| + process_segments[id] = segment.get();
|
| + segments_.push_back(segment.get());
|
| std::push_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
|
|
|
| // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466405
|
| @@ -246,6 +279,29 @@ void HostDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
|
| ScheduleEnforceMemoryPolicy();
|
| }
|
|
|
| +void HostDiscardableSharedMemoryManager::DeletedDiscardableSharedMemory(
|
| + DiscardableSharedMemoryId id,
|
| + base::ProcessHandle process_handle) {
|
| + base::AutoLock lock(lock_);
|
| +
|
| + MemorySegmentMap& process_segments = processes_[process_handle];
|
| +
|
| + MemorySegmentMap::iterator segment_it = process_segments.find(id);
|
| + if (segment_it == process_segments.end()) {
|
| + LOG(ERROR) << "Invalid discardable shared memory ID";
|
| + return;
|
| + }
|
| +
|
| + size_t bytes_allocated_before_releasing_memory = bytes_allocated_;
|
| +
|
| + ReleaseMemory(segment_it->second->memory());
|
| +
|
| + process_segments.erase(segment_it);
|
| +
|
| + if (bytes_allocated_ != bytes_allocated_before_releasing_memory)
|
| + BytesAllocatedChanged(bytes_allocated_);
|
| +}
|
| +
|
| void HostDiscardableSharedMemoryManager::OnMemoryPressure(
|
| base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
|
| base::AutoLock lock(lock_);
|
| @@ -266,6 +322,8 @@ void HostDiscardableSharedMemoryManager::OnMemoryPressure(
|
|
|
| void
|
| HostDiscardableSharedMemoryManager::ReduceMemoryUsageUntilWithinMemoryLimit() {
|
| + lock_.AssertAcquired();
|
| +
|
| if (bytes_allocated_ <= memory_limit_)
|
| return;
|
|
|
| @@ -294,11 +352,11 @@ void HostDiscardableSharedMemoryManager::ReduceMemoryUsageUntilWithinLimit(
|
| break;
|
|
|
| // Stop eviction attempts when the LRU segment is currently in use.
|
| - if (segments_.front().memory->last_known_usage() >= current_time)
|
| + if (segments_.front()->memory()->last_known_usage() >= current_time)
|
| break;
|
|
|
| std::pop_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
|
| - MemorySegment segment = segments_.back();
|
| + scoped_refptr<MemorySegment> segment = segments_.back();
|
| segments_.pop_back();
|
|
|
| // Attempt to purge and truncate LRU segment. When successful, as much
|
| @@ -306,16 +364,14 @@ void HostDiscardableSharedMemoryManager::ReduceMemoryUsageUntilWithinLimit(
|
| // released depends on the platform. The child process should perform
|
| // periodic cleanup to ensure that all memory is release within a
|
| // reasonable amount of time.
|
| - if (segment.memory->PurgeAndTruncate(current_time)) {
|
| - size_t size = segment.memory->mapped_size();
|
| - DCHECK_GE(bytes_allocated_, size);
|
| - bytes_allocated_ -= size;
|
| + if (segment->memory()->PurgeAndTruncate(current_time)) {
|
| + ReleaseMemory(segment->memory());
|
| continue;
|
| }
|
|
|
| // Add memory segment (with updated usage timestamp) back on heap after
|
| // failed attempt to purge it.
|
| - segments_.push_back(segment);
|
| + segments_.push_back(segment.get());
|
| std::push_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
|
| }
|
|
|
| @@ -323,6 +379,23 @@ void HostDiscardableSharedMemoryManager::ReduceMemoryUsageUntilWithinLimit(
|
| BytesAllocatedChanged(bytes_allocated_);
|
| }
|
|
|
| +void HostDiscardableSharedMemoryManager::ReleaseMemory(
|
| + base::DiscardableSharedMemory* memory) {
|
| + lock_.AssertAcquired();
|
| +
|
| + size_t size = memory->mapped_size();
|
| + DCHECK_GE(bytes_allocated_, size);
|
| + bytes_allocated_ -= size;
|
| +
|
| + // This will unmap the memory segment and drop our reference. The result
|
| + // is that the memory will be released to the OS if the child process is
|
| + // no longer referencing it.
|
| + // Note: We intentionally leave the segment in the |segments| vector to
|
| + // avoid reconstructing the heap. The element will be removed from the heap
|
| + // when its last usage time is older than all other segments.
|
| + memory->Close();
|
| +}
|
| +
|
| void HostDiscardableSharedMemoryManager::BytesAllocatedChanged(
|
| size_t new_bytes_allocated) const {
|
| TRACE_COUNTER_ID1("renderer_host", "TotalDiscardableMemoryUsage", this,
|
| @@ -338,6 +411,8 @@ base::Time HostDiscardableSharedMemoryManager::Now() const {
|
| }
|
|
|
| void HostDiscardableSharedMemoryManager::ScheduleEnforceMemoryPolicy() {
|
| + lock_.AssertAcquired();
|
| +
|
| if (enforce_memory_policy_pending_)
|
| return;
|
|
|
|
|