123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656 |
- // 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 "components/discardable_memory/client/client_discardable_shared_memory_manager.h"
- #include <algorithm>
- #include <utility>
- #include "base/atomic_sequence_num.h"
- #include "base/bind.h"
- #include "base/format_macros.h"
- #include "base/memory/discardable_memory.h"
- #include "base/memory/discardable_shared_memory.h"
- #include "base/memory/page_size.h"
- #include "base/metrics/histogram_functions.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/process/memory.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/synchronization/waitable_event.h"
- #include "base/system/sys_info.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "base/time/time.h"
- #include "base/trace_event/memory_dump_manager.h"
- #include "base/trace_event/trace_event.h"
- #include "build/build_config.h"
- #include "components/crash/core/common/crash_key.h"
- namespace discardable_memory {
- namespace {
- const base::Feature kShorterPeriodicPurge{"ShorterPeriodicPurge",
- base::FEATURE_DISABLED_BY_DEFAULT};
- // Global atomic to generate unique discardable shared memory IDs.
- base::AtomicSequenceNumber g_next_discardable_shared_memory_id;
- size_t GetDefaultAllocationSize() {
- const size_t kOneMegabyteInBytes = 1024 * 1024;
- // There is a trade-off between round-trip cost to the browser process and
- // memory usage overhead. 4MB is measured as the ideal size according to the
- // usage statistics. For low-end devices, we care about lowering the memory
- // usage and 1MB is good for the most basic cases.
- [[maybe_unused]] const size_t kDefaultAllocationSize =
- 4 * kOneMegabyteInBytes;
- [[maybe_unused]] const size_t kDefaultLowEndDeviceAllocationSize =
- kOneMegabyteInBytes;
- #if defined(ARCH_CPU_32_BITS) && !BUILDFLAG(IS_ANDROID)
- // On 32 bit architectures, use a smaller chunk, as address space
- // fragmentation may make a 4MiB allocation impossible to fulfill in the
- // browser process. See crbug.com/983348 for details.
- //
- // Not on Android, since on this platform total number of file descriptors is
- // also a concern.
- return kDefaultLowEndDeviceAllocationSize;
- #elif BUILDFLAG(IS_FUCHSIA)
- // Low end Fuchsia devices may be very constrained, so use smaller allocations
- // to save memory. See https://fxbug.dev/55760.
- return base::SysInfo::IsLowEndDevice() ? kDefaultLowEndDeviceAllocationSize
- : kDefaultAllocationSize;
- #else
- return kDefaultAllocationSize;
- #endif
- }
- void InitManagerMojoOnIO(
- mojo::Remote<mojom::DiscardableSharedMemoryManager>* manager_mojo,
- mojo::PendingRemote<mojom::DiscardableSharedMemoryManager> remote) {
- manager_mojo->Bind(std::move(remote));
- }
- void DeletedDiscardableSharedMemoryOnIO(
- mojo::Remote<mojom::DiscardableSharedMemoryManager>* manager_mojo,
- int32_t id) {
- (*manager_mojo)->DeletedDiscardableSharedMemory(id);
- }
- } // namespace
- constexpr base::TimeDelta
- ClientDiscardableSharedMemoryManager::kMinAgeForScheduledPurge;
- constexpr base::TimeDelta
- ClientDiscardableSharedMemoryManager::kScheduledPurgeInterval;
- ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::
- DiscardableMemoryImpl(
- ClientDiscardableSharedMemoryManager* manager,
- std::unique_ptr<DiscardableSharedMemoryHeap::Span> span)
- : manager_(manager), span_(std::move(span)) {
- DCHECK_NE(manager, nullptr);
- }
- ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::
- ~DiscardableMemoryImpl() {
- base::AutoLock lock(manager_->lock_);
- if (!span_) {
- DCHECK(!is_locked());
- return;
- }
- manager_->UnlockAndReleaseMemory(this, std::move(span_));
- }
- bool ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::Lock() {
- base::AutoLock lock(manager_->lock_);
- DCHECK(!is_locked());
- if (span_ && manager_->LockSpan(span_.get()))
- last_locked_ = base::TimeTicks();
- bool locked = is_locked();
- UMA_HISTOGRAM_BOOLEAN("Memory.Discardable.LockingSuccess", locked);
- return locked;
- }
- void ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::Unlock() {
- base::AutoLock lock(manager_->lock_);
- DCHECK(is_locked());
- DCHECK(span_);
- manager_->UnlockSpan(span_.get());
- last_locked_ = base::TimeTicks::Now();
- }
- std::unique_ptr<DiscardableSharedMemoryHeap::Span>
- ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::Purge(
- base::TimeTicks min_ticks) {
- DCHECK(span_);
- if (is_locked())
- return nullptr;
- if (last_locked_ > min_ticks)
- return nullptr;
- return std::move(span_);
- }
- void* ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::data()
- const {
- #if DCHECK_IS_ON()
- {
- base::AutoLock lock(manager_->lock_);
- DCHECK(is_locked());
- }
- #endif
- return reinterpret_cast<void*>(span_->start() * base::GetPageSize());
- }
- bool ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::is_locked()
- const {
- return last_locked_.is_null();
- }
- void ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::
- DiscardForTesting() {
- #if DCHECK_IS_ON()
- {
- base::AutoLock lock(manager_->lock_);
- DCHECK(!is_locked());
- }
- #endif
- span_->shared_memory()->Purge(base::Time::Now());
- }
- base::trace_event::MemoryAllocatorDump* ClientDiscardableSharedMemoryManager::
- DiscardableMemoryImpl::CreateMemoryAllocatorDump(
- const char* name,
- base::trace_event::ProcessMemoryDump* pmd) const {
- base::AutoLock lock(manager_->lock_);
- return manager_->CreateMemoryAllocatorDump(span_.get(), name, pmd);
- }
- ClientDiscardableSharedMemoryManager::ClientDiscardableSharedMemoryManager(
- mojo::PendingRemote<mojom::DiscardableSharedMemoryManager> manager,
- scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
- : ClientDiscardableSharedMemoryManager(io_task_runner) {
- manager_mojo_ =
- std::make_unique<mojo::Remote<mojom::DiscardableSharedMemoryManager>>();
- io_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&InitManagerMojoOnIO, manager_mojo_.get(),
- std::move(manager)));
- }
- ClientDiscardableSharedMemoryManager::ClientDiscardableSharedMemoryManager(
- scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
- : RefCountedDeleteOnSequence<ClientDiscardableSharedMemoryManager>(
- base::ThreadTaskRunnerHandle::Get()),
- task_runner_(base::ThreadTaskRunnerHandle::Get()),
- heap_(std::make_unique<DiscardableSharedMemoryHeap>()),
- io_task_runner_(std::move(io_task_runner)),
- manager_mojo_(nullptr) {
- base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
- this, "ClientDiscardableSharedMemoryManager",
- base::ThreadTaskRunnerHandle::Get());
- }
- ClientDiscardableSharedMemoryManager::~ClientDiscardableSharedMemoryManager() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
- this);
- // Any memory allocated by a ClientDiscardableSharedMemoryManager must not be
- // touched after it is destroyed, or it will cause a use-after-free. This
- // check ensures that we stop before that can happen, instead of continuing
- // with dangling pointers.
- CHECK_EQ(heap_->GetSize(), heap_->GetFreelistSize());
- if (heap_->GetSize())
- MemoryUsageChanged(0, 0);
- // Releasing the |heap_| before posting a task for deleting |manager_mojo_|.
- // It is because releasing |heap_| will invoke DeletedDiscardableSharedMemory
- // which needs |manager_mojo_|.
- heap_.reset();
- // Delete the |manager_mojo_| on IO thread, so any pending tasks on IO thread
- // will be executed before the |manager_mojo_| is deleted.
- bool posted = io_task_runner_->DeleteSoon(FROM_HERE, manager_mojo_.release());
- if (!posted)
- manager_mojo_.reset();
- }
- void ClientDiscardableSharedMemoryManager::OnForegrounded() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- foregrounded_ = true;
- }
- void ClientDiscardableSharedMemoryManager::OnBackgrounded() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- foregrounded_ = false;
- }
- std::unique_ptr<base::DiscardableMemory>
- ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
- size_t size) {
- base::AutoLock lock(lock_);
- if (!is_purge_scheduled_) {
- task_runner_->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&ClientDiscardableSharedMemoryManager::ScheduledPurge,
- this),
- kScheduledPurgeInterval);
- is_purge_scheduled_ = true;
- }
- DCHECK_NE(size, 0u);
- auto size_in_kb = static_cast<base::HistogramBase::Sample>(size / 1024);
- UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.DiscardableAllocationSize",
- size_in_kb, // In KiB
- 1,
- 4 * 1024 * 1024, // 4 GiB
- 50);
- // Round up to multiple of page size.
- size_t pages =
- std::max((size + base::GetPageSize() - 1) / base::GetPageSize(),
- static_cast<size_t>(1));
- static const size_t allocation_size = GetDefaultAllocationSize();
- DCHECK_EQ(allocation_size % base::GetPageSize(), 0u);
- // Default allocation size in pages.
- size_t allocation_pages = allocation_size / base::GetPageSize();
- size_t slack = 0;
- // When searching the free lists, allow a slack between required size and
- // free span size that is less or equal to |allocation_size|. This is to
- // avoid segments larger then |allocation_size| unless they are a perfect
- // fit. The result is that large allocations can be reused without reducing
- // the ability to discard memory.
- if (pages < allocation_pages)
- slack = allocation_pages - pages;
- size_t heap_size_prior_to_releasing_purged_memory = heap_->GetSize();
- for (;;) {
- // Search free lists for suitable span.
- std::unique_ptr<DiscardableSharedMemoryHeap::Span> free_span =
- heap_->SearchFreeLists(pages, slack);
- if (!free_span)
- break;
- // Attempt to lock |free_span|. Delete span and search free lists again
- // if locking failed.
- if (free_span->shared_memory()->Lock(
- free_span->start() * base::GetPageSize() -
- reinterpret_cast<size_t>(free_span->shared_memory()->memory()),
- free_span->length() * base::GetPageSize()) ==
- base::DiscardableSharedMemory::FAILED) {
- DCHECK(!free_span->shared_memory()->IsMemoryResident());
- // We have to release purged memory before |free_span| can be destroyed.
- heap_->ReleasePurgedMemory();
- DCHECK(!free_span->shared_memory());
- continue;
- }
- free_span->set_is_locked(true);
- if (pages >= allocation_pages) {
- UMA_HISTOGRAM_BOOLEAN("Memory.Discardable.LargeAllocationFromFreelist",
- true);
- }
- // Memory usage is guaranteed to have changed after having removed
- // at least one span from the free lists.
- MemoryUsageChanged(heap_->GetSize(), heap_->GetFreelistSize());
- // Memory in this span is no longer held in the freelist, so we don't want
- // to count it towards the total of dirty freelist memory.
- heap_->dirty_freed_memory_page_count_ -= free_span->MarkAsClean();
- auto discardable_memory =
- std::make_unique<DiscardableMemoryImpl>(this, std::move(free_span));
- allocated_memory_.insert(discardable_memory.get());
- return std::move(discardable_memory);
- }
- // Release purged memory to free up the address space before we attempt to
- // allocate more memory.
- heap_->ReleasePurgedMemory();
- // Make sure crash keys are up to date in case allocation fails.
- if (heap_->GetSize() != heap_size_prior_to_releasing_purged_memory)
- MemoryUsageChanged(heap_->GetSize(), heap_->GetFreelistSize());
- size_t pages_to_allocate =
- std::max(allocation_size / base::GetPageSize(), pages);
- size_t allocation_size_in_bytes = pages_to_allocate * base::GetPageSize();
- int32_t new_id = g_next_discardable_shared_memory_id.GetNext();
- if (bytes_allocated_limit_for_testing_ &&
- heap_->GetSize() >= bytes_allocated_limit_for_testing_) {
- return nullptr;
- }
- // Ask parent process to allocate a new discardable shared memory segment.
- std::unique_ptr<base::DiscardableSharedMemory> shared_memory =
- AllocateLockedDiscardableSharedMemory(allocation_size_in_bytes, new_id);
- if (!shared_memory)
- return nullptr;
- // Create span for allocated memory.
- // Spans are managed by |heap_| (the member of
- // the ClientDiscardableSharedMemoryManager), so it is safe to use
- // base::Unretained(this) here.
- std::unique_ptr<DiscardableSharedMemoryHeap::Span> new_span(heap_->Grow(
- std::move(shared_memory), allocation_size_in_bytes, new_id,
- base::BindOnce(
- &ClientDiscardableSharedMemoryManager::DeletedDiscardableSharedMemory,
- base::Unretained(this), new_id)));
- new_span->set_is_locked(true);
- // Unlock and insert any left over memory into free lists.
- if (pages < pages_to_allocate) {
- std::unique_ptr<DiscardableSharedMemoryHeap::Span> leftover =
- heap_->Split(new_span.get(), pages);
- leftover->shared_memory()->Unlock(
- leftover->start() * base::GetPageSize() -
- reinterpret_cast<size_t>(leftover->shared_memory()->memory()),
- leftover->length() * base::GetPageSize());
- leftover->set_is_locked(false);
- heap_->MergeIntoFreeListsClean(std::move(leftover));
- }
- if (pages >= allocation_pages) {
- UMA_HISTOGRAM_BOOLEAN("Memory.Discardable.LargeAllocationFromFreelist",
- false);
- }
- MemoryUsageChanged(heap_->GetSize(), heap_->GetFreelistSize());
- auto discardable_memory =
- std::make_unique<DiscardableMemoryImpl>(this, std::move(new_span));
- allocated_memory_.insert(discardable_memory.get());
- return std::move(discardable_memory);
- }
- bool ClientDiscardableSharedMemoryManager::OnMemoryDump(
- const base::trace_event::MemoryDumpArgs& args,
- base::trace_event::ProcessMemoryDump* pmd) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- base::AutoLock lock(lock_);
- if (foregrounded_) {
- const size_t total_size = heap_->GetSize() / 1024; // in KiB
- const size_t freelist_size = heap_->GetFreelistSize() / 1024; // in KiB
- base::UmaHistogramCounts1M("Memory.Discardable.FreelistSize.Foreground",
- freelist_size);
- base::UmaHistogramCounts1M("Memory.Discardable.VirtualSize.Foreground",
- total_size);
- base::UmaHistogramCounts1M("Memory.Discardable.Size.Foreground",
- total_size - freelist_size);
- }
- base::UmaHistogramCounts1M(
- "Memory.Discardable.FreelistSize.Dirty",
- heap_->dirty_freed_memory_page_count_ * base::GetPageSize() / 1024);
- return heap_->OnMemoryDump(args, pmd);
- }
- size_t ClientDiscardableSharedMemoryManager::GetBytesAllocated() const {
- base::AutoLock lock(lock_);
- return GetBytesAllocatedLocked();
- }
- size_t ClientDiscardableSharedMemoryManager::GetBytesAllocatedLocked() const {
- return heap_->GetSize() - heap_->GetFreelistSize();
- }
- void ClientDiscardableSharedMemoryManager::BackgroundPurge() {
- PurgeUnlockedMemory(base::TimeDelta());
- }
- void ClientDiscardableSharedMemoryManager::ScheduledPurge() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- // From local testing and UMA, memory usually accumulates slowly in renderers,
- // and can sit idle for hours. We purge only the old memory, as this should
- // recover the memory without adverse latency effects.
- // TODO(crbug.com/1123679): Determine if |kMinAgeForScheduledPurge| and the
- // constant from |ScheduledPurge| need to be tuned.
- if (base::FeatureList::IsEnabled(kShorterPeriodicPurge)) {
- PurgeUnlockedMemory(
- ClientDiscardableSharedMemoryManager::kMinAgeForScheduledPurge / 2);
- } else {
- PurgeUnlockedMemory(
- ClientDiscardableSharedMemoryManager::kMinAgeForScheduledPurge);
- }
- bool should_schedule = false;
- {
- base::AutoLock lock(lock_);
- should_schedule = GetBytesAllocatedLocked() != 0;
- is_purge_scheduled_ = should_schedule;
- }
- if (should_schedule) {
- task_runner_->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&ClientDiscardableSharedMemoryManager::ScheduledPurge,
- this),
- kScheduledPurgeInterval);
- }
- }
- void ClientDiscardableSharedMemoryManager::PurgeUnlockedMemory(
- base::TimeDelta min_age) {
- {
- base::AutoLock lock(lock_);
- auto now = base::TimeTicks::Now();
- // Iterate this way in order to avoid invalidating the iterator while
- // removing elements from |allocated_memory_| as we iterate over it.
- for (auto it = allocated_memory_.begin(); it != allocated_memory_.end();
- /* nop */) {
- auto prev = it++;
- DiscardableMemoryImpl* mem = *prev;
- // This assert is only required because the static checker can't figure
- // out that |mem->manager_->lock_| is the same as |this->lock_|, as
- // verified by the DCHECK.
- DCHECK_EQ(&lock_, &mem->manager_->lock_);
- mem->manager_->lock_.AssertAcquired();
- auto span = mem->Purge(now - min_age);
- if (span) {
- allocated_memory_.erase(prev);
- ReleaseSpan(std::move(span));
- }
- }
- }
- ReleaseFreeMemory();
- }
- void ClientDiscardableSharedMemoryManager::ReleaseFreeMemory() {
- TRACE_EVENT0("blink",
- "ClientDiscardableSharedMemoryManager::ReleaseFreeMemory()");
- base::AutoLock lock(lock_);
- size_t heap_size_prior_to_releasing_memory = heap_->GetSize();
- // Release both purged and free memory.
- heap_->ReleasePurgedMemory();
- heap_->ReleaseFreeMemory();
- if (heap_->GetSize() != heap_size_prior_to_releasing_memory)
- MemoryUsageChanged(heap_->GetSize(), heap_->GetFreelistSize());
- }
- bool ClientDiscardableSharedMemoryManager::LockSpan(
- DiscardableSharedMemoryHeap::Span* span) {
- if (!span->shared_memory())
- return false;
- size_t offset = span->start() * base::GetPageSize() -
- reinterpret_cast<size_t>(span->shared_memory()->memory());
- size_t length = span->length() * base::GetPageSize();
- switch (span->shared_memory()->Lock(offset, length)) {
- case base::DiscardableSharedMemory::SUCCESS:
- span->set_is_locked(true);
- return true;
- case base::DiscardableSharedMemory::PURGED:
- span->shared_memory()->Unlock(offset, length);
- span->set_is_locked(false);
- return false;
- case base::DiscardableSharedMemory::FAILED:
- return false;
- }
- NOTREACHED();
- return false;
- }
- void ClientDiscardableSharedMemoryManager::UnlockSpan(
- DiscardableSharedMemoryHeap::Span* span) {
- DCHECK(span->shared_memory());
- size_t offset = span->start() * base::GetPageSize() -
- reinterpret_cast<size_t>(span->shared_memory()->memory());
- size_t length = span->length() * base::GetPageSize();
- span->set_is_locked(false);
- return span->shared_memory()->Unlock(offset, length);
- }
- void ClientDiscardableSharedMemoryManager::UnlockAndReleaseMemory(
- DiscardableMemoryImpl* memory,
- std::unique_ptr<DiscardableSharedMemoryHeap::Span> span) {
- memory->manager_->lock_.AssertAcquired();
- // lock_.AssertAcquired();
- if (memory->is_locked()) {
- UnlockSpan(span.get());
- }
- DCHECK(span);
- auto removed = allocated_memory_.erase(memory);
- DCHECK_EQ(removed, 1u);
- ReleaseSpan(std::move(span));
- }
- void ClientDiscardableSharedMemoryManager::ReleaseSpan(
- std::unique_ptr<DiscardableSharedMemoryHeap::Span> span) {
- DCHECK(span);
- // Delete span instead of merging it into free lists if memory is gone.
- if (!span->shared_memory())
- return;
- heap_->MergeIntoFreeLists(std::move(span));
- // Bytes of free memory changed.
- MemoryUsageChanged(heap_->GetSize(), heap_->GetFreelistSize());
- }
- base::trace_event::MemoryAllocatorDump*
- ClientDiscardableSharedMemoryManager::CreateMemoryAllocatorDump(
- DiscardableSharedMemoryHeap::Span* span,
- const char* name,
- base::trace_event::ProcessMemoryDump* pmd) const {
- return heap_->CreateMemoryAllocatorDump(span, name, pmd);
- }
- std::unique_ptr<base::DiscardableSharedMemory>
- ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
- size_t size,
- int32_t id) {
- TRACE_EVENT2("renderer",
- "ClientDiscardableSharedMemoryManager::"
- "AllocateLockedDiscardableSharedMemory",
- "size", size, "id", id);
- static crash_reporter::CrashKeyString<24>
- discardable_memory_ipc_requested_size(
- "discardable-memory-ipc-requested-size");
- static crash_reporter::CrashKeyString<24> discardable_memory_ipc_error_cause(
- "discardable-memory-ipc-error-cause");
- base::UnsafeSharedMemoryRegion region;
- base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
- base::WaitableEvent::InitialState::NOT_SIGNALED);
- base::ScopedClosureRunner event_signal_runner(
- base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(&event)));
- io_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&ClientDiscardableSharedMemoryManager::AllocateOnIO,
- base::Unretained(this), size, id, ®ion,
- std::move(event_signal_runner)));
- // Waiting until IPC has finished on the IO thread.
- event.Wait();
- // This is likely address space exhaustion in the the browser process. We
- // don't want to crash the browser process for that, which is why the check
- // is here, and not there.
- if (!region.IsValid()) {
- discardable_memory_ipc_error_cause.Set("browser side");
- discardable_memory_ipc_requested_size.Set(base::NumberToString(size));
- return nullptr;
- }
- auto memory =
- std::make_unique<base::DiscardableSharedMemory>(std::move(region));
- if (!memory->Map(size)) {
- discardable_memory_ipc_error_cause.Set("client side");
- discardable_memory_ipc_requested_size.Set(base::NumberToString(size));
- return nullptr;
- }
- discardable_memory_ipc_error_cause.Clear();
- discardable_memory_ipc_requested_size.Clear();
- return memory;
- }
- void ClientDiscardableSharedMemoryManager::AllocateOnIO(
- size_t size,
- int32_t id,
- base::UnsafeSharedMemoryRegion* region,
- base::ScopedClosureRunner closure_runner) {
- (*manager_mojo_)
- ->AllocateLockedDiscardableSharedMemory(
- static_cast<uint32_t>(size), id,
- base::BindOnce(
- &ClientDiscardableSharedMemoryManager::AllocateCompletedOnIO,
- base::Unretained(this), region, std::move(closure_runner)));
- }
- void ClientDiscardableSharedMemoryManager::AllocateCompletedOnIO(
- base::UnsafeSharedMemoryRegion* region,
- base::ScopedClosureRunner closure_runner,
- base::UnsafeSharedMemoryRegion ret_region) {
- *region = std::move(ret_region);
- }
- void ClientDiscardableSharedMemoryManager::DeletedDiscardableSharedMemory(
- int32_t id) {
- io_task_runner_->PostTask(FROM_HERE,
- base::BindOnce(&DeletedDiscardableSharedMemoryOnIO,
- manager_mojo_.get(), id));
- }
- void ClientDiscardableSharedMemoryManager::MemoryUsageChanged(
- size_t new_bytes_total,
- size_t new_bytes_free) const {
- static crash_reporter::CrashKeyString<24> discardable_memory_allocated(
- "discardable-memory-allocated");
- discardable_memory_allocated.Set(base::NumberToString(new_bytes_total));
- static crash_reporter::CrashKeyString<24> discardable_memory_free(
- "discardable-memory-free");
- discardable_memory_free.Set(base::NumberToString(new_bytes_free));
- }
- } // namespace discardable_memory
|