Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(115)

Unified Diff: content/common/host_discardable_shared_memory_manager.cc

Issue 2459733002: Move discardable memory to //components from //content (Closed)
Patch Set: Fix build error Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
deleted file mode 100644
index e349fca4171c1a32a1daf1ef22f50913e1d7c89f..0000000000000000000000000000000000000000
--- a/content/common/host_discardable_shared_memory_manager.cc
+++ /dev/null
@@ -1,580 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/common/host_discardable_shared_memory_manager.h"
-
-#include <algorithm>
-#include <utility>
-
-#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"
-#include "base/macros.h"
-#include "base/memory/discardable_memory.h"
-#include "base/memory/memory_coordinator_client_registry.h"
-#include "base/memory/ptr_util.h"
-#include "base/numerics/safe_math.h"
-#include "base/process/memory.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
-#include "base/sys_info.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/trace_event/memory_allocator_dump.h"
-#include "base/trace_event/memory_dump_manager.h"
-#include "base/trace_event/process_memory_dump.h"
-#include "base/trace_event/trace_event.h"
-#include "build/build_config.h"
-#include "content/common/child_process_host_impl.h"
-#include "content/common/discardable_shared_memory_heap.h"
-#include "content/public/common/child_process_host.h"
-
-#if defined(OS_LINUX)
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/metrics/histogram_macros.h"
-#endif
-
-namespace content {
-namespace {
-
-class DiscardableMemoryImpl : public base::DiscardableMemory {
- public:
- DiscardableMemoryImpl(
- std::unique_ptr<base::DiscardableSharedMemory> shared_memory,
- const base::Closure& deleted_callback)
- : shared_memory_(std::move(shared_memory)),
- deleted_callback_(deleted_callback),
- is_locked_(true) {}
-
- ~DiscardableMemoryImpl() override {
- if (is_locked_)
- shared_memory_->Unlock(0, 0);
-
- deleted_callback_.Run();
- }
-
- // Overridden from base::DiscardableMemory:
- bool Lock() override {
- DCHECK(!is_locked_);
-
- if (shared_memory_->Lock(0, 0) != base::DiscardableSharedMemory::SUCCESS)
- return false;
-
- is_locked_ = true;
- return true;
- }
- void Unlock() override {
- DCHECK(is_locked_);
-
- shared_memory_->Unlock(0, 0);
- is_locked_ = false;
- }
- void* data() const override {
- DCHECK(is_locked_);
- return shared_memory_->memory();
- }
-
- base::trace_event::MemoryAllocatorDump* CreateMemoryAllocatorDump(
- const char* name,
- base::trace_event::ProcessMemoryDump* pmd) const override {
- // The memory could have been purged, but we still create a dump with
- // mapped_size. So, the size can be inaccurate.
- base::trace_event::MemoryAllocatorDump* dump =
- pmd->CreateAllocatorDump(name);
- dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
- base::trace_event::MemoryAllocatorDump::kUnitsBytes,
- shared_memory_->mapped_size());
- return dump;
- }
-
- private:
- std::unique_ptr<base::DiscardableSharedMemory> shared_memory_;
- const base::Closure deleted_callback_;
- bool is_locked_;
-
- DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryImpl);
-};
-
-// Returns the default memory limit to use for discardable memory, taking
-// the amount physical memory available and other platform specific constraints
-// into account.
-int64_t GetDefaultMemoryLimit() {
- const int kMegabyte = 1024 * 1024;
-
-#if defined(OS_ANDROID)
- // Limits the number of FDs used to 32, assuming a 4MB allocation size.
- int64_t max_default_memory_limit = 128 * kMegabyte;
-#else
- int64_t max_default_memory_limit = 512 * kMegabyte;
-#endif
-
- // Use 1/8th of discardable memory on low-end devices.
- if (base::SysInfo::IsLowEndDevice())
- max_default_memory_limit /= 8;
-
-#if defined(OS_LINUX)
- base::FilePath shmem_dir;
- if (base::GetShmemTempDir(false, &shmem_dir)) {
- int64_t shmem_dir_amount_of_free_space =
- base::SysInfo::AmountOfFreeDiskSpace(shmem_dir);
- DCHECK_GT(shmem_dir_amount_of_free_space, 0);
- int64_t shmem_dir_amount_of_free_space_mb =
- shmem_dir_amount_of_free_space / kMegabyte;
-
- UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.ShmemDir.AmountOfFreeSpace",
- shmem_dir_amount_of_free_space_mb, 1,
- 4 * 1024, // 4 GB
- 50);
-
- if (shmem_dir_amount_of_free_space_mb < 64) {
- LOG(WARNING) << "Less than 64MB of free space in temporary directory for "
- "shared memory files: "
- << shmem_dir_amount_of_free_space_mb;
- }
-
- // Allow 1/2 of available shmem dir space to be used for discardable memory.
- max_default_memory_limit =
- std::min(max_default_memory_limit, shmem_dir_amount_of_free_space / 2);
- }
-#endif
-
- // Allow 25% of physical memory to be used for discardable memory.
- return std::min(max_default_memory_limit,
- base::SysInfo::AmountOfPhysicalMemory() / 4);
-}
-
-base::LazyInstance<HostDiscardableSharedMemoryManager>
- g_discardable_shared_memory_manager = LAZY_INSTANCE_INITIALIZER;
-
-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(
- std::unique_ptr<base::DiscardableSharedMemory> memory)
- : memory_(std::move(memory)) {}
-
-HostDiscardableSharedMemoryManager::MemorySegment::~MemorySegment() {
-}
-
-HostDiscardableSharedMemoryManager::HostDiscardableSharedMemoryManager()
- : default_memory_limit_(GetDefaultMemoryLimit()),
- memory_limit_(default_memory_limit_),
- bytes_allocated_(0),
- memory_pressure_listener_(new base::MemoryPressureListener(
- base::Bind(&HostDiscardableSharedMemoryManager::OnMemoryPressure,
- base::Unretained(this)))),
- // Current thread might not have a task runner in tests.
- enforce_memory_policy_task_runner_(base::ThreadTaskRunnerHandle::Get()),
- enforce_memory_policy_pending_(false),
- weak_ptr_factory_(this) {
- DCHECK_NE(memory_limit_, 0u);
- enforce_memory_policy_callback_ =
- base::Bind(&HostDiscardableSharedMemoryManager::EnforceMemoryPolicy,
- weak_ptr_factory_.GetWeakPtr());
- base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
- this, "HostDiscardableSharedMemoryManager",
- base::ThreadTaskRunnerHandle::Get());
- base::MemoryCoordinatorClientRegistry::GetInstance()->Register(this);
-}
-
-HostDiscardableSharedMemoryManager::~HostDiscardableSharedMemoryManager() {
- base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
- this);
-}
-
-HostDiscardableSharedMemoryManager*
-HostDiscardableSharedMemoryManager::current() {
- return g_discardable_shared_memory_manager.Pointer();
-}
-
-std::unique_ptr<base::DiscardableMemory>
-HostDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
- size_t size) {
- DCHECK_NE(size, 0u);
-
- 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(current_process_handle,
- ChildProcessHost::kInvalidUniqueID,
- size, new_id, &handle);
- std::unique_ptr<base::DiscardableSharedMemory> memory(
- new base::DiscardableSharedMemory(handle));
- if (!memory->Map(size))
- base::TerminateBecauseOutOfMemory(size);
- // Close file descriptor to avoid running out.
- memory->Close();
- return base::MakeUnique<DiscardableMemoryImpl>(
- std::move(memory),
- base::Bind(
- &HostDiscardableSharedMemoryManager::DeletedDiscardableSharedMemory,
- base::Unretained(this), new_id, ChildProcessHost::kInvalidUniqueID));
-}
-
-bool HostDiscardableSharedMemoryManager::OnMemoryDump(
- const base::trace_event::MemoryDumpArgs& args,
- base::trace_event::ProcessMemoryDump* pmd) {
- if (args.level_of_detail ==
- base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND) {
- base::trace_event::MemoryAllocatorDump* total_dump =
- pmd->CreateAllocatorDump("discardable");
- total_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
- base::trace_event::MemoryAllocatorDump::kUnitsBytes,
- GetBytesAllocated());
- return true;
- }
-
- base::AutoLock lock(lock_);
- for (const auto& process_entry : processes_) {
- const int child_process_id = process_entry.first;
- const MemorySegmentMap& process_segments = process_entry.second;
- for (const auto& segment_entry : process_segments) {
- const int segment_id = segment_entry.first;
- const MemorySegment* segment = segment_entry.second.get();
- if (!segment->memory()->mapped_size())
- continue;
-
- // The "size" will be inherited form the shared global dump.
- std::string dump_name = base::StringPrintf(
- "discardable/process_%x/segment_%d", child_process_id, segment_id);
- base::trace_event::MemoryAllocatorDump* dump =
- pmd->CreateAllocatorDump(dump_name);
-
- dump->AddScalar("virtual_size",
- base::trace_event::MemoryAllocatorDump::kUnitsBytes,
- segment->memory()->mapped_size());
-
- // Host can only tell if whole segment is locked or not.
- dump->AddScalar(
- "locked_size", base::trace_event::MemoryAllocatorDump::kUnitsBytes,
- segment->memory()->IsMemoryLocked() ? segment->memory()->mapped_size()
- : 0u);
-
- // Create the cross-process ownership edge. If the child creates a
- // corresponding dump for the same segment, this will avoid to
- // double-count them in tracing. If, instead, no other process will emit a
- // dump with the same guid, the segment will be accounted to the browser.
- const uint64_t child_tracing_process_id =
- ChildProcessHostImpl::ChildProcessUniqueIdToTracingProcessId(
- child_process_id);
- base::trace_event::MemoryAllocatorDumpGuid shared_segment_guid =
- DiscardableSharedMemoryHeap::GetSegmentGUIDForTracing(
- child_tracing_process_id, segment_id);
- pmd->CreateSharedGlobalAllocatorDump(shared_segment_guid);
- pmd->AddOwnershipEdge(dump->guid(), shared_segment_guid);
-
-#if defined(COUNT_RESIDENT_BYTES_SUPPORTED)
- if (args.level_of_detail ==
- base::trace_event::MemoryDumpLevelOfDetail::DETAILED) {
- size_t resident_size =
- base::trace_event::ProcessMemoryDump::CountResidentBytes(
- segment->memory()->memory(), segment->memory()->mapped_size());
-
- // This is added to the global dump since it has to be attributed to
- // both the allocator dumps involved.
- pmd->GetSharedGlobalAllocatorDump(shared_segment_guid)
- ->AddScalar("resident_size",
- base::trace_event::MemoryAllocatorDump::kUnitsBytes,
- static_cast<uint64_t>(resident_size));
- }
-#endif // defined(COUNT_RESIDENT_BYTES_SUPPORTED)
- }
- }
- return true;
-}
-
-void HostDiscardableSharedMemoryManager::
- AllocateLockedDiscardableSharedMemoryForChild(
- base::ProcessHandle process_handle,
- int child_process_id,
- size_t size,
- DiscardableSharedMemoryId id,
- base::SharedMemoryHandle* shared_memory_handle) {
- AllocateLockedDiscardableSharedMemory(process_handle, child_process_id, size,
- id, shared_memory_handle);
-}
-
-void HostDiscardableSharedMemoryManager::ChildDeletedDiscardableSharedMemory(
- DiscardableSharedMemoryId id,
- int child_process_id) {
- DeletedDiscardableSharedMemory(id, child_process_id);
-}
-
-void HostDiscardableSharedMemoryManager::ProcessRemoved(int child_process_id) {
- base::AutoLock lock(lock_);
-
- ProcessMap::iterator process_it = processes_.find(child_process_id);
- if (process_it == processes_.end())
- return;
-
- size_t bytes_allocated_before_releasing_memory = bytes_allocated_;
-
- 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_);
-}
-
-void HostDiscardableSharedMemoryManager::SetMemoryLimit(size_t limit) {
- base::AutoLock lock(lock_);
-
- memory_limit_ = limit;
- ReduceMemoryUsageUntilWithinMemoryLimit();
-}
-
-void HostDiscardableSharedMemoryManager::EnforceMemoryPolicy() {
- base::AutoLock lock(lock_);
-
- enforce_memory_policy_pending_ = false;
- ReduceMemoryUsageUntilWithinMemoryLimit();
-}
-
-size_t HostDiscardableSharedMemoryManager::GetBytesAllocated() {
- base::AutoLock lock(lock_);
-
- return bytes_allocated_;
-}
-
-void HostDiscardableSharedMemoryManager::OnMemoryStateChange(
- base::MemoryState state) {
- switch (state) {
- case base::MemoryState::NORMAL:
- SetMemoryLimit(default_memory_limit_);
- break;
- case base::MemoryState::THROTTLED:
- SetMemoryLimit(0);
- break;
- case base::MemoryState::SUSPENDED:
- // Note that SUSPENDED never occurs in the main browser process so far.
- // Fall through.
- case base::MemoryState::UNKNOWN:
- NOTREACHED();
- break;
- }
-}
-
-void HostDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
- base::ProcessHandle process_handle,
- int client_process_id,
- size_t size,
- DiscardableSharedMemoryId id,
- base::SharedMemoryHandle* shared_memory_handle) {
- base::AutoLock lock(lock_);
-
- // Make sure |id| is not already in use.
- MemorySegmentMap& process_segments = processes_[client_process_id];
- 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.
- size_t limit = 0;
- // Note: the actual mapped size can be larger than requested and cause
- // |bytes_allocated_| to temporarily be larger than |memory_limit_|. The
- // error is minimized by incrementing |bytes_allocated_| with the actual
- // mapped size rather than |size| below.
- if (size < memory_limit_)
- limit = memory_limit_ - size;
-
- if (bytes_allocated_ > limit)
- ReduceMemoryUsageUntilWithinLimit(limit);
-
- std::unique_ptr<base::DiscardableSharedMemory> memory(
- new base::DiscardableSharedMemory);
- if (!memory->CreateAndMap(size)) {
- *shared_memory_handle = base::SharedMemory::NULLHandle();
- return;
- }
-
- if (!memory->ShareToProcess(process_handle, shared_memory_handle)) {
- LOG(ERROR) << "Cannot share discardable memory segment";
- *shared_memory_handle = base::SharedMemory::NULLHandle();
- return;
- }
-
- // Close file descriptor to avoid running out.
- memory->Close();
-
- base::CheckedNumeric<size_t> checked_bytes_allocated = bytes_allocated_;
- checked_bytes_allocated += memory->mapped_size();
- if (!checked_bytes_allocated.IsValid()) {
- *shared_memory_handle = base::SharedMemory::NULLHandle();
- return;
- }
-
- bytes_allocated_ = checked_bytes_allocated.ValueOrDie();
- BytesAllocatedChanged(bytes_allocated_);
-
- scoped_refptr<MemorySegment> segment(new MemorySegment(std::move(memory)));
- process_segments[id] = segment.get();
- segments_.push_back(segment.get());
- std::push_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
-
- if (bytes_allocated_ > memory_limit_)
- ScheduleEnforceMemoryPolicy();
-}
-
-void HostDiscardableSharedMemoryManager::DeletedDiscardableSharedMemory(
- DiscardableSharedMemoryId id,
- int client_process_id) {
- base::AutoLock lock(lock_);
-
- MemorySegmentMap& process_segments = processes_[client_process_id];
-
- 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_);
-
- switch (memory_pressure_level) {
- case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
- break;
- case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
- // Purge memory until usage is within half of |memory_limit_|.
- ReduceMemoryUsageUntilWithinLimit(memory_limit_ / 2);
- break;
- case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
- // Purge everything possible when pressure is critical.
- ReduceMemoryUsageUntilWithinLimit(0);
- break;
- }
-}
-
-void
-HostDiscardableSharedMemoryManager::ReduceMemoryUsageUntilWithinMemoryLimit() {
- lock_.AssertAcquired();
-
- if (bytes_allocated_ <= memory_limit_)
- return;
-
- ReduceMemoryUsageUntilWithinLimit(memory_limit_);
- if (bytes_allocated_ > memory_limit_)
- ScheduleEnforceMemoryPolicy();
-}
-
-void HostDiscardableSharedMemoryManager::ReduceMemoryUsageUntilWithinLimit(
- size_t limit) {
- TRACE_EVENT1("renderer_host",
- "HostDiscardableSharedMemoryManager::"
- "ReduceMemoryUsageUntilWithinLimit",
- "bytes_allocated",
- bytes_allocated_);
-
- // Usage time of currently locked segments are updated to this time and
- // we stop eviction attempts as soon as we come across a segment that we've
- // previously tried to evict but was locked.
- base::Time current_time = Now();
-
- lock_.AssertAcquired();
- size_t bytes_allocated_before_purging = bytes_allocated_;
- while (!segments_.empty()) {
- if (bytes_allocated_ <= limit)
- break;
-
- // Stop eviction attempts when the LRU segment is currently in use.
- if (segments_.front()->memory()->last_known_usage() >= current_time)
- break;
-
- std::pop_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
- scoped_refptr<MemorySegment> segment = segments_.back();
- segments_.pop_back();
-
- // Simply drop the reference and continue if memory has already been
- // unmapped. This happens when a memory segment has been deleted by
- // the client.
- if (!segment->memory()->mapped_size())
- continue;
-
- // Attempt to purge LRU segment. When successful, released the memory.
- if (segment->memory()->Purge(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.get());
- std::push_heap(segments_.begin(), segments_.end(), CompareMemoryUsageTime);
- }
-
- if (bytes_allocated_ != bytes_allocated_before_purging)
- 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->Unmap();
- memory->Close();
-}
-
-void HostDiscardableSharedMemoryManager::BytesAllocatedChanged(
- size_t new_bytes_allocated) const {
- static const char kTotalDiscardableMemoryAllocatedKey[] =
- "total-discardable-memory-allocated";
- base::debug::SetCrashKeyValue(kTotalDiscardableMemoryAllocatedKey,
- base::Uint64ToString(new_bytes_allocated));
-}
-
-base::Time HostDiscardableSharedMemoryManager::Now() const {
- return base::Time::Now();
-}
-
-void HostDiscardableSharedMemoryManager::ScheduleEnforceMemoryPolicy() {
- lock_.AssertAcquired();
-
- if (enforce_memory_policy_pending_)
- return;
-
- enforce_memory_policy_pending_ = true;
- DCHECK(enforce_memory_policy_task_runner_);
- enforce_memory_policy_task_runner_->PostDelayedTask(
- FROM_HERE, enforce_memory_policy_callback_,
- base::TimeDelta::FromMilliseconds(kEnforceMemoryPolicyDelayMs));
-}
-
-} // namespace content

Powered by Google App Engine
This is Rietveld 408576698