Commit 0ceffaa2 authored by Thomas Anderson's avatar Thomas Anderson Committed by Commit Bot

Revert "Proactively purging discardable memory on Desktop"

This reverts commit 3558d80a.

Reason for revert: Suspected cause of build failure on android-official:

https://ci.chromium.org/p/chromium/builders/ci/android-official/1056

[13692/25621] SOLINK ./libaudio_unittests__library.so
FAILED: libaudio_unittests__library.so libaudio_unittests__library.so.TOC lib.unstripped/libaudio_unittests__library.so lib.unstripped/libaudio_unittests__library.so.map.gz
/b/s/w/ir/cipd_bin_packages/cpython/bin/python "../../build/toolchain/gcc_solink_wrapper.py" --reade...(too long)
ld.lld: error: undefined hidden symbol: base::(anonymous namespace)::g_discardable_allocator (.llvm.16724700302094337840)
>>> referenced by run_all_unittests.cc:0 (../../media/test/run_all_unittests.cc:0)
>>>               thinlto-cache/llvmcache-3F27182A7CE8A475288094E8A454CB68C4740658:(TestSuiteNoAtExit::Initialize())

Original change's description:
> Proactively purging discardable memory on Desktop
> 
> This CL adds a way for ClientDiscardableSharedMemoryManager to discard
> unlocked discardable memory locally. To achieve this, it keeps track
> of all locked and unlocked, unpurged instances. These are then purged
> all at once when ReleaseFreeMemory() is called.
> 
> This is guarded by a new feature which is disabled by default, and as
> such there is no behaviour change by default.
> 
> Bug: 1109209
> Change-Id: I7755d714ff354e831557627fda401751384392b8
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2321400
> Commit-Queue: Thiabaud Engelbrecht <thiabaud@google.com>
> Reviewed-by: Peng Huang <penghuang@chromium.org>
> Reviewed-by: Benoit L <lizeb@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#798630}

TBR=penghuang@chromium.org,lizeb@chromium.org,thiabaud@google.com

Change-Id: I39ddd002be3753d1339501c74df866d898ad53a4
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 1109209
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2360121Reviewed-by: default avatarThomas Anderson <thomasanderson@chromium.org>
Commit-Queue: Thomas Anderson <thomasanderson@chromium.org>
Cr-Commit-Position: refs/heads/master@{#798796}
parent 9f9ebab3
...@@ -236,7 +236,6 @@ test("components_unittests") { ...@@ -236,7 +236,6 @@ test("components_unittests") {
"//components/content_settings/browser:unit_tests", "//components/content_settings/browser:unit_tests",
"//components/contextual_search/core:unit_tests", "//components/contextual_search/core:unit_tests",
"//components/data_use_measurement/core:unit_tests", "//components/data_use_measurement/core:unit_tests",
"//components/discardable_memory/client:unit_tests",
"//components/discardable_memory/common:unit_tests", "//components/discardable_memory/common:unit_tests",
"//components/discardable_memory/service:unit_tests", "//components/discardable_memory/service:unit_tests",
"//components/dom_distiller/content/browser:unit_tests", "//components/dom_distiller/content/browser:unit_tests",
......
...@@ -21,15 +21,3 @@ component("client") { ...@@ -21,15 +21,3 @@ component("client") {
"//components/discardable_memory/public/mojom", "//components/discardable_memory/public/mojom",
] ]
} }
source_set("unit_tests") {
testonly = true
sources = [ "client_discardable_shared_memory_manager_unittest.cc" ]
deps = [
":client",
"//base",
"//testing/gtest",
]
}
...@@ -4,12 +4,14 @@ ...@@ -4,12 +4,14 @@
#include "components/discardable_memory/client/client_discardable_shared_memory_manager.h" #include "components/discardable_memory/client/client_discardable_shared_memory_manager.h"
#include <inttypes.h>
#include <algorithm> #include <algorithm>
#include <memory>
#include <utility> #include <utility>
#include "base/atomic_sequence_num.h" #include "base/atomic_sequence_num.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/feature_list.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/discardable_memory.h" #include "base/memory/discardable_memory.h"
#include "base/memory/discardable_shared_memory.h" #include "base/memory/discardable_shared_memory.h"
...@@ -32,13 +34,6 @@ namespace { ...@@ -32,13 +34,6 @@ namespace {
// Global atomic to generate unique discardable shared memory IDs. // Global atomic to generate unique discardable shared memory IDs.
base::AtomicSequenceNumber g_next_discardable_shared_memory_id; base::AtomicSequenceNumber g_next_discardable_shared_memory_id;
// This controls whether unlocked memory is released when |ReleaseFreeMemory| is
// called. Enabling this causes |ReleaseFreeMemory| to release all
// unlocked memory instances, as well as release all free memory (as opposed to
// merely releasing all free memory).
const base::Feature kPurgeUnlockedMemory{"PurgeUnlockedMemory",
base::FEATURE_DISABLED_BY_DEFAULT};
size_t GetDefaultAllocationSize() { size_t GetDefaultAllocationSize() {
const size_t kOneMegabyteInBytes = 1024 * 1024; const size_t kOneMegabyteInBytes = 1024 * 1024;
...@@ -67,108 +62,80 @@ size_t GetDefaultAllocationSize() { ...@@ -67,108 +62,80 @@ size_t GetDefaultAllocationSize() {
#endif #endif
} }
void InitManagerMojoOnIO( class DiscardableMemoryImpl : public base::DiscardableMemory {
mojo::Remote<mojom::DiscardableSharedMemoryManager>* manager_mojo, public:
mojo::PendingRemote<mojom::DiscardableSharedMemoryManager> remote) { DiscardableMemoryImpl(ClientDiscardableSharedMemoryManager* manager,
manager_mojo->Bind(std::move(remote)); std::unique_ptr<DiscardableSharedMemoryHeap::Span> span)
} : manager_(manager), span_(std::move(span)), is_locked_(true) {}
void DeletedDiscardableSharedMemoryOnIO( ~DiscardableMemoryImpl() override {
mojo::Remote<mojom::DiscardableSharedMemoryManager>* manager_mojo, if (is_locked_)
int32_t id) { manager_->UnlockSpan(span_.get());
(*manager_mojo)->DeletedDiscardableSharedMemory(id);
}
} // namespace manager_->ReleaseSpan(std::move(span_));
ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::
DiscardableMemoryImpl(
ClientDiscardableSharedMemoryManager* manager,
std::unique_ptr<DiscardableSharedMemoryHeap::Span> span)
: manager_(manager), span_(std::move(span)), is_locked_(true) {
DCHECK_NE(manager, nullptr);
}
ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::
~DiscardableMemoryImpl() {
base::AutoLock lock(manager_->GetLock());
if (!span_) {
DCHECK(!is_locked_);
return;
} }
if (is_locked_)
manager_->UnlockSpan(span_.get());
manager_->ReleaseMemory(this, std::move(span_)); // Overridden from base::DiscardableMemory:
} bool Lock() override {
DCHECK(!is_locked_);
bool ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::Lock() { if (!manager_->LockSpan(span_.get()))
base::AutoLock lock(manager_->GetLock()); return false;
DCHECK(!is_locked_);
if (!span_) is_locked_ = true;
return false; return true;
}
void Unlock() override {
DCHECK(is_locked_);
if (!manager_->LockSpan(span_.get())) manager_->UnlockSpan(span_.get());
return false; is_locked_ = false;
}
void* data() const override {
DCHECK(is_locked_);
return reinterpret_cast<void*>(span_->start() * base::GetPageSize());
}
is_locked_ = true; void DiscardForTesting() override {
return true; DCHECK(!is_locked_);
} span_->shared_memory()->Purge(base::Time::Now());
}
void ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::Unlock() { base::trace_event::MemoryAllocatorDump* CreateMemoryAllocatorDump(
base::AutoLock lock(manager_->GetLock()); const char* name,
DCHECK(is_locked_); base::trace_event::ProcessMemoryDump* pmd) const override {
DCHECK(span_); return manager_->CreateMemoryAllocatorDump(span_.get(), name, pmd);
}
manager_->UnlockSpan(span_.get()); private:
is_locked_ = false; ClientDiscardableSharedMemoryManager* const manager_;
} std::unique_ptr<DiscardableSharedMemoryHeap::Span> span_;
bool is_locked_;
std::unique_ptr<DiscardableSharedMemoryHeap::Span> DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryImpl);
ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::Purge() { };
DCHECK(span_);
if (is_locked_)
return nullptr;
return std::move(span_);
}
void* ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::data() void InitManagerMojoOnIO(
const { mojo::Remote<mojom::DiscardableSharedMemoryManager>* manager_mojo,
#if DCHECK_IS_ON() mojo::PendingRemote<mojom::DiscardableSharedMemoryManager> remote) {
{ manager_mojo->Bind(std::move(remote));
base::AutoLock lock(manager_->GetLock());
DCHECK(is_locked_);
}
#endif
return reinterpret_cast<void*>(span_->start() * base::GetPageSize());
} }
void ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl:: void DeletedDiscardableSharedMemoryOnIO(
DiscardForTesting() { mojo::Remote<mojom::DiscardableSharedMemoryManager>* manager_mojo,
#if DCHECK_IS_ON() int32_t id) {
{ (*manager_mojo)->DeletedDiscardableSharedMemory(id);
base::AutoLock lock(manager_->GetLock());
DCHECK(!is_locked_);
}
#endif
span_->shared_memory()->Purge(base::Time::Now());
} }
base::trace_event::MemoryAllocatorDump* ClientDiscardableSharedMemoryManager:: } // namespace
DiscardableMemoryImpl::CreateMemoryAllocatorDump(
const char* name,
base::trace_event::ProcessMemoryDump* pmd) const {
return manager_->CreateMemoryAllocatorDump(span_.get(), name, pmd);
}
ClientDiscardableSharedMemoryManager::ClientDiscardableSharedMemoryManager( ClientDiscardableSharedMemoryManager::ClientDiscardableSharedMemoryManager(
mojo::PendingRemote<mojom::DiscardableSharedMemoryManager> manager, mojo::PendingRemote<mojom::DiscardableSharedMemoryManager> manager,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
: heap_(std::make_unique<DiscardableSharedMemoryHeap>()), : io_task_runner_(std::move(io_task_runner)),
io_task_runner_(std::move(io_task_runner)),
manager_mojo_(std::make_unique< manager_mojo_(std::make_unique<
mojo::Remote<mojom::DiscardableSharedMemoryManager>>()) { mojo::Remote<mojom::DiscardableSharedMemoryManager>>()),
heap_(std::make_unique<DiscardableSharedMemoryHeap>()) {
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
this, "ClientDiscardableSharedMemoryManager", this, "ClientDiscardableSharedMemoryManager",
base::ThreadTaskRunnerHandle::Get()); base::ThreadTaskRunnerHandle::Get());
...@@ -177,11 +144,6 @@ ClientDiscardableSharedMemoryManager::ClientDiscardableSharedMemoryManager( ...@@ -177,11 +144,6 @@ ClientDiscardableSharedMemoryManager::ClientDiscardableSharedMemoryManager(
std::move(manager))); std::move(manager)));
} }
ClientDiscardableSharedMemoryManager::ClientDiscardableSharedMemoryManager(
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
: heap_(std::make_unique<DiscardableSharedMemoryHeap>()),
io_task_runner_(std::move(io_task_runner)) {}
ClientDiscardableSharedMemoryManager::~ClientDiscardableSharedMemoryManager() { ClientDiscardableSharedMemoryManager::~ClientDiscardableSharedMemoryManager() {
base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
this); this);
...@@ -263,10 +225,7 @@ ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory( ...@@ -263,10 +225,7 @@ ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
// at least one span from the free lists. // at least one span from the free lists.
MemoryUsageChanged(heap_->GetSize(), heap_->GetSizeOfFreeLists()); MemoryUsageChanged(heap_->GetSize(), heap_->GetSizeOfFreeLists());
auto discardable_memory = return std::make_unique<DiscardableMemoryImpl>(this, std::move(free_span));
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 // Release purged memory to free up the address space before we attempt to
...@@ -320,10 +279,7 @@ ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory( ...@@ -320,10 +279,7 @@ ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
MemoryUsageChanged(heap_->GetSize(), heap_->GetSizeOfFreeLists()); MemoryUsageChanged(heap_->GetSize(), heap_->GetSizeOfFreeLists());
auto discardable_memory = return std::make_unique<DiscardableMemoryImpl>(this, std::move(new_span));
std::make_unique<DiscardableMemoryImpl>(this, std::move(new_span));
allocated_memory_.insert(discardable_memory.get());
return std::move(discardable_memory);
} }
bool ClientDiscardableSharedMemoryManager::OnMemoryDump( bool ClientDiscardableSharedMemoryManager::OnMemoryDump(
...@@ -338,46 +294,11 @@ size_t ClientDiscardableSharedMemoryManager::GetBytesAllocated() const { ...@@ -338,46 +294,11 @@ size_t ClientDiscardableSharedMemoryManager::GetBytesAllocated() const {
return heap_->GetSize() - heap_->GetSizeOfFreeLists(); return heap_->GetSize() - heap_->GetSizeOfFreeLists();
} }
void ClientDiscardableSharedMemoryManager::PurgeUnlockedMemory() {
{
base::AutoLock lock(lock_);
// 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_->GetLock()| is the same as |this->lock_|, as
// verified by the DCHECK.
DCHECK_EQ(&lock_, &mem->manager_->GetLock());
mem->manager_->GetLock().AssertAcquired();
auto span = mem->Purge();
if (span) {
allocated_memory_.erase(prev);
ReleaseSpan(std::move(span));
}
}
}
ReleaseFreeMemoryImpl();
}
void ClientDiscardableSharedMemoryManager::ReleaseFreeMemory() { void ClientDiscardableSharedMemoryManager::ReleaseFreeMemory() {
if (base::FeatureList::IsEnabled(kPurgeUnlockedMemory)) {
PurgeUnlockedMemory();
} else {
ReleaseFreeMemoryImpl();
}
}
void ClientDiscardableSharedMemoryManager::ReleaseFreeMemoryImpl() {
TRACE_EVENT0("blink", TRACE_EVENT0("blink",
"ClientDiscardableSharedMemoryManager::ReleaseFreeMemory()"); "ClientDiscardableSharedMemoryManager::ReleaseFreeMemory()");
base::AutoLock lock(lock_); base::AutoLock lock(lock_);
size_t heap_size_prior_to_releasing_memory = heap_->GetSize(); size_t heap_size_prior_to_releasing_memory = heap_->GetSize();
// Release both purged and free memory. // Release both purged and free memory.
...@@ -390,6 +311,8 @@ void ClientDiscardableSharedMemoryManager::ReleaseFreeMemoryImpl() { ...@@ -390,6 +311,8 @@ void ClientDiscardableSharedMemoryManager::ReleaseFreeMemoryImpl() {
bool ClientDiscardableSharedMemoryManager::LockSpan( bool ClientDiscardableSharedMemoryManager::LockSpan(
DiscardableSharedMemoryHeap::Span* span) { DiscardableSharedMemoryHeap::Span* span) {
base::AutoLock lock(lock_);
if (!span->shared_memory()) if (!span->shared_memory())
return false; return false;
...@@ -415,6 +338,8 @@ bool ClientDiscardableSharedMemoryManager::LockSpan( ...@@ -415,6 +338,8 @@ bool ClientDiscardableSharedMemoryManager::LockSpan(
void ClientDiscardableSharedMemoryManager::UnlockSpan( void ClientDiscardableSharedMemoryManager::UnlockSpan(
DiscardableSharedMemoryHeap::Span* span) { DiscardableSharedMemoryHeap::Span* span) {
base::AutoLock lock(lock_);
DCHECK(span->shared_memory()); DCHECK(span->shared_memory());
size_t offset = span->start() * base::GetPageSize() - size_t offset = span->start() * base::GetPageSize() -
reinterpret_cast<size_t>(span->shared_memory()->memory()); reinterpret_cast<size_t>(span->shared_memory()->memory());
...@@ -424,18 +349,9 @@ void ClientDiscardableSharedMemoryManager::UnlockSpan( ...@@ -424,18 +349,9 @@ void ClientDiscardableSharedMemoryManager::UnlockSpan(
return span->shared_memory()->Unlock(offset, length); return span->shared_memory()->Unlock(offset, length);
} }
void ClientDiscardableSharedMemoryManager::ReleaseMemory(
DiscardableMemoryImpl* memory,
std::unique_ptr<DiscardableSharedMemoryHeap::Span> span) {
DCHECK(span);
auto removed = allocated_memory_.erase(memory);
DCHECK_EQ(removed, 1u);
ReleaseSpan(std::move(span));
}
void ClientDiscardableSharedMemoryManager::ReleaseSpan( void ClientDiscardableSharedMemoryManager::ReleaseSpan(
std::unique_ptr<DiscardableSharedMemoryHeap::Span> span) { std::unique_ptr<DiscardableSharedMemoryHeap::Span> span) {
DCHECK(span); base::AutoLock lock(lock_);
// Delete span instead of merging it into free lists if memory is gone. // Delete span instead of merging it into free lists if memory is gone.
if (!span->shared_memory()) if (!span->shared_memory())
...@@ -464,7 +380,6 @@ ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory( ...@@ -464,7 +380,6 @@ ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
"ClientDiscardableSharedMemoryManager::" "ClientDiscardableSharedMemoryManager::"
"AllocateLockedDiscardableSharedMemory", "AllocateLockedDiscardableSharedMemory",
"size", size, "id", id); "size", size, "id", id);
base::UnsafeSharedMemoryRegion region; base::UnsafeSharedMemoryRegion region;
base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED); base::WaitableEvent::InitialState::NOT_SIGNALED);
...@@ -479,8 +394,8 @@ ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory( ...@@ -479,8 +394,8 @@ ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
event.Wait(); event.Wait();
// This is likely address space exhaustion in the the browser process. We // 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 // don't want to crash the browser process for that, which is why the check is
// is here, and not there. // here, and not there.
if (!region.IsValid()) if (!region.IsValid())
return nullptr; return nullptr;
......
...@@ -8,8 +8,9 @@ ...@@ -8,8 +8,9 @@
#include <stddef.h> #include <stddef.h>
#include <memory> #include <memory>
#include <set>
#include "base/callback_helpers.h"
#include "base/macros.h"
#include "base/memory/discardable_memory_allocator.h" #include "base/memory/discardable_memory_allocator.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/unsafe_shared_memory_region.h" #include "base/memory/unsafe_shared_memory_region.h"
...@@ -46,16 +47,12 @@ class DISCARDABLE_MEMORY_EXPORT ClientDiscardableSharedMemoryManager ...@@ -46,16 +47,12 @@ class DISCARDABLE_MEMORY_EXPORT ClientDiscardableSharedMemoryManager
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) override; base::trace_event::ProcessMemoryDump* pmd) override;
// Purge any unlocked memory that was allocated by this manager.
void PurgeUnlockedMemory();
// Release memory and associated resources that have been purged. // Release memory and associated resources that have been purged.
void ReleaseFreeMemory() override; void ReleaseFreeMemory() override;
bool LockSpan(DiscardableSharedMemoryHeap::Span* span) bool LockSpan(DiscardableSharedMemoryHeap::Span* span);
EXCLUSIVE_LOCKS_REQUIRED(GetLock()); void UnlockSpan(DiscardableSharedMemoryHeap::Span* span);
void UnlockSpan(DiscardableSharedMemoryHeap::Span* span) void ReleaseSpan(std::unique_ptr<DiscardableSharedMemoryHeap::Span> span);
EXCLUSIVE_LOCKS_REQUIRED(GetLock());
base::trace_event::MemoryAllocatorDump* CreateMemoryAllocatorDump( base::trace_event::MemoryAllocatorDump* CreateMemoryAllocatorDump(
DiscardableSharedMemoryHeap::Span* span, DiscardableSharedMemoryHeap::Span* span,
...@@ -73,47 +70,8 @@ class DISCARDABLE_MEMORY_EXPORT ClientDiscardableSharedMemoryManager ...@@ -73,47 +70,8 @@ class DISCARDABLE_MEMORY_EXPORT ClientDiscardableSharedMemoryManager
bytes_allocated_limit_for_testing_ = limit; bytes_allocated_limit_for_testing_ = limit;
} }
// We only have protected members for testing, everything else should be
// either public or private.
protected:
explicit ClientDiscardableSharedMemoryManager(
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
std::unique_ptr<DiscardableSharedMemoryHeap> heap_ GUARDED_BY(lock_);
mutable base::Lock lock_;
private: private:
class DiscardableMemoryImpl : public base::DiscardableMemory { std::unique_ptr<base::DiscardableSharedMemory>
public:
DiscardableMemoryImpl(
ClientDiscardableSharedMemoryManager* manager,
std::unique_ptr<DiscardableSharedMemoryHeap::Span> span);
~DiscardableMemoryImpl() override;
DiscardableMemoryImpl(const DiscardableMemoryImpl&) = delete;
DiscardableMemoryImpl& operator=(const DiscardableMemoryImpl&) = delete;
// Overridden from base::DiscardableMemory:
bool Lock() override;
void Unlock() override;
void* data() const override;
void DiscardForTesting() override;
base::trace_event::MemoryAllocatorDump* CreateMemoryAllocatorDump(
const char* name,
base::trace_event::ProcessMemoryDump* pmd) const override;
// Returns |span_| if unlocked, otherwise nullptr.
std::unique_ptr<DiscardableSharedMemoryHeap::Span> Purge()
EXCLUSIVE_LOCKS_REQUIRED(manager_->GetLock());
private:
friend class ClientDiscardableSharedMemoryManager;
ClientDiscardableSharedMemoryManager* const manager_;
std::unique_ptr<DiscardableSharedMemoryHeap::Span> span_;
bool is_locked_ GUARDED_BY(manager_->GetLock());
};
// This is only virtual for testing.
virtual std::unique_ptr<base::DiscardableSharedMemory>
AllocateLockedDiscardableSharedMemory(size_t size, int32_t id); AllocateLockedDiscardableSharedMemory(size_t size, int32_t id);
void AllocateOnIO(size_t size, void AllocateOnIO(size_t size,
int32_t id, int32_t id,
...@@ -123,27 +81,18 @@ class DISCARDABLE_MEMORY_EXPORT ClientDiscardableSharedMemoryManager ...@@ -123,27 +81,18 @@ class DISCARDABLE_MEMORY_EXPORT ClientDiscardableSharedMemoryManager
base::ScopedClosureRunner closure_runner, base::ScopedClosureRunner closure_runner,
base::UnsafeSharedMemoryRegion ret_region); base::UnsafeSharedMemoryRegion ret_region);
// This is only virtual for testing. void DeletedDiscardableSharedMemory(int32_t id);
virtual void DeletedDiscardableSharedMemory(int32_t id);
void MemoryUsageChanged(size_t new_bytes_allocated, void MemoryUsageChanged(size_t new_bytes_allocated,
size_t new_bytes_free) const; size_t new_bytes_free) const;
void ReleaseFreeMemoryImpl();
void ReleaseMemory(DiscardableMemoryImpl* memory,
std::unique_ptr<DiscardableSharedMemoryHeap::Span> span)
EXCLUSIVE_LOCKS_REQUIRED(lock_);
void ReleaseSpan(std::unique_ptr<DiscardableSharedMemoryHeap::Span> span)
EXCLUSIVE_LOCKS_REQUIRED(lock_);
base::Lock& GetLock() { return lock_; }
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
// TODO(penghuang): Switch to SharedRemote when it starts supporting // TODO(penghuang): Switch to SharedRemote when it starts supporting
// sync method call. // sync method call.
std::unique_ptr<mojo::Remote<mojom::DiscardableSharedMemoryManager>> std::unique_ptr<mojo::Remote<mojom::DiscardableSharedMemoryManager>>
manager_mojo_; manager_mojo_;
// Holds all locked and unlocked instances which have not yet been purged. mutable base::Lock lock_;
std::set<DiscardableMemoryImpl*> allocated_memory_ GUARDED_BY(lock_); std::unique_ptr<DiscardableSharedMemoryHeap> heap_ GUARDED_BY(lock_);
size_t bytes_allocated_limit_for_testing_ = 0; size_t bytes_allocated_limit_for_testing_ = 0;
DISALLOW_COPY_AND_ASSIGN(ClientDiscardableSharedMemoryManager); DISALLOW_COPY_AND_ASSIGN(ClientDiscardableSharedMemoryManager);
......
// Copyright 2020 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 "base/memory/discardable_memory.h"
#include "base/memory/discardable_shared_memory.h"
#include "base/process/process_metrics.h"
#include "base/synchronization/lock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace discardable_memory {
namespace {
using base::Location;
using base::OnceClosure;
using base::TimeDelta;
class TestSingleThreadTaskRunner : public base::SingleThreadTaskRunner {
~TestSingleThreadTaskRunner() override = default;
bool PostTask(const Location& from_here, OnceClosure task) { return true; }
template <class T>
bool DeleteSoon(const Location& from_here, const T* object) {
return true;
}
bool PostDelayedTask(const Location& from_here,
OnceClosure task,
TimeDelta delay) override {
return true;
}
bool PostNonNestableDelayedTask(const Location& from_here,
OnceClosure task,
TimeDelta delay) override {
return true;
}
bool RunsTasksInCurrentSequence() const override { return true; }
};
class TestClientDiscardableSharedMemoryManager
: public ClientDiscardableSharedMemoryManager {
public:
TestClientDiscardableSharedMemoryManager()
: ClientDiscardableSharedMemoryManager(
base::MakeRefCounted<TestSingleThreadTaskRunner>()) {}
~TestClientDiscardableSharedMemoryManager() override = default;
std::unique_ptr<base::DiscardableSharedMemory>
AllocateLockedDiscardableSharedMemory(size_t size, int32_t id) override {
auto shared_memory = std::make_unique<base::DiscardableSharedMemory>();
shared_memory->CreateAndMap(size);
return shared_memory;
}
void DeletedDiscardableSharedMemory(int32_t id) override {}
size_t GetSize() const {
base::AutoLock lock(lock_);
return heap_->GetSize();
}
size_t GetSizeOfFreeLists() const {
base::AutoLock lock(lock_);
return heap_->GetSizeOfFreeLists();
}
};
// This test allocates a single piece of memory, then verifies that calling
// |PurgeUnlockedMemory| only affects the memory when it is unlocked.
TEST(ClientDiscardableSharedMemoryManagerTest, Simple) {
const size_t page_size = base::GetPageSize();
TestClientDiscardableSharedMemoryManager client;
// Initially, we should have no memory allocated
ASSERT_EQ(client.GetBytesAllocated(), 0u);
ASSERT_EQ(client.GetSizeOfFreeLists(), 0u);
auto mem = client.AllocateLockedDiscardableMemory(page_size);
// After allocation, we should have allocated a single piece of memory.
EXPECT_EQ(client.GetBytesAllocated(), page_size);
client.PurgeUnlockedMemory();
// All our memory is locked, so calling |PurgeUnlockedMemory| should have no
// effect.
EXPECT_EQ(client.GetBytesAllocated(), base::GetPageSize());
mem->Unlock();
// Unlocking has no effect on the amount of memory we have allocated.
EXPECT_EQ(client.GetBytesAllocated(), base::GetPageSize());
client.PurgeUnlockedMemory();
// Now that |mem| is unlocked, the call to |PurgeUnlockedMemory| will
// remove it.
EXPECT_EQ(client.GetBytesAllocated(), 0u);
}
// This test allocates multiple pieces of memory, then unlocks them one by one,
// verifying that |PurgeUnlockedMemory| only affects the unlocked pieces of
// memory.
TEST(ClientDiscardableSharedMemoryManagerTest, MultipleOneByOne) {
const size_t page_size = base::GetPageSize();
TestClientDiscardableSharedMemoryManager client;
ASSERT_EQ(client.GetBytesAllocated(), 0u);
ASSERT_EQ(client.GetSizeOfFreeLists(), 0u);
auto mem1 = client.AllocateLockedDiscardableMemory(page_size * 2.2);
auto mem2 = client.AllocateLockedDiscardableMemory(page_size * 1.1);
auto mem3 = client.AllocateLockedDiscardableMemory(page_size * 3.5);
auto mem4 = client.AllocateLockedDiscardableMemory(page_size * 0.2);
EXPECT_EQ(client.GetBytesAllocated(), 10 * page_size);
// Does nothing because everything is locked.
client.PurgeUnlockedMemory();
EXPECT_EQ(client.GetBytesAllocated(), 10 * page_size);
mem1->Unlock();
// Does nothing, since we don't have any free memory, just unlocked memory.
client.ReleaseFreeMemory();
EXPECT_EQ(client.GetBytesAllocated(), 10 * page_size);
// This gets rid of |mem1| (which is unlocked), but not the rest of the
// memory.
client.PurgeUnlockedMemory();
EXPECT_EQ(client.GetBytesAllocated(), 7 * page_size);
// We do similar checks to above for the rest of the memory.
mem2->Unlock();
client.PurgeUnlockedMemory();
EXPECT_EQ(client.GetBytesAllocated(), 5 * page_size);
mem3->Unlock();
client.PurgeUnlockedMemory();
EXPECT_EQ(client.GetBytesAllocated(), 1 * page_size);
mem4->Unlock();
client.PurgeUnlockedMemory();
EXPECT_EQ(client.GetBytesAllocated(), 0 * page_size);
}
// This test allocates multiple pieces of memory, then unlocks them all,
// verifying that |PurgeUnlockedMemory| only affects the unlocked pieces of
// memory.
TEST(ClientDiscardableSharedMemoryManagerTest, MultipleAtOnce) {
const size_t page_size = base::GetPageSize();
TestClientDiscardableSharedMemoryManager client;
ASSERT_EQ(client.GetBytesAllocated(), 0u);
ASSERT_EQ(client.GetSizeOfFreeLists(), 0u);
auto mem1 = client.AllocateLockedDiscardableMemory(page_size * 2.2);
auto mem2 = client.AllocateLockedDiscardableMemory(page_size * 1.1);
auto mem3 = client.AllocateLockedDiscardableMemory(page_size * 3.5);
auto mem4 = client.AllocateLockedDiscardableMemory(page_size * 0.2);
EXPECT_EQ(client.GetBytesAllocated(), 10 * page_size);
// Does nothing because everything is locked.
client.PurgeUnlockedMemory();
EXPECT_EQ(client.GetBytesAllocated(), 10 * page_size);
// Unlock all pieces of memory at once.
mem1->Unlock();
mem2->Unlock();
mem3->Unlock();
mem4->Unlock();
client.PurgeUnlockedMemory();
EXPECT_EQ(client.GetBytesAllocated(), 0 * page_size);
}
// Tests that FreeLists are only released once all memory has been released.
TEST(ClientDiscardableSharedMemoryManagerTest, Release) {
const size_t page_size = base::GetPageSize();
TestClientDiscardableSharedMemoryManager client;
ASSERT_EQ(client.GetBytesAllocated(), 0u);
ASSERT_EQ(client.GetSizeOfFreeLists(), 0u);
auto mem1 = client.AllocateLockedDiscardableMemory(page_size * 3);
auto mem2 = client.AllocateLockedDiscardableMemory(page_size * 2);
size_t freelist_size = client.GetSizeOfFreeLists();
EXPECT_EQ(client.GetBytesAllocated(), 5 * page_size);
mem1 = nullptr;
// Less memory is now allocated, but freelists are grown.
EXPECT_EQ(client.GetBytesAllocated(), page_size * 2);
EXPECT_EQ(client.GetSizeOfFreeLists(), freelist_size + page_size * 3);
client.PurgeUnlockedMemory();
// Purging doesn't remove any memory since none is unlocked, also doesn't
// remove freelists since we still have some.
EXPECT_EQ(client.GetBytesAllocated(), page_size * 2);
EXPECT_EQ(client.GetSizeOfFreeLists(), freelist_size + page_size * 3);
mem2 = nullptr;
// No memory is allocated, but freelists are grown.
EXPECT_EQ(client.GetBytesAllocated(), 0u);
EXPECT_EQ(client.GetSizeOfFreeLists(), freelist_size + page_size * 5);
client.PurgeUnlockedMemory();
// Purging now shrinks freelists as well.
EXPECT_EQ(client.GetBytesAllocated(), 0u);
EXPECT_EQ(client.GetSizeOfFreeLists(), 0u);
}
// Similar to previous test, but makes sure that freelist still shrinks when
// last piece of memory was just unlocked instead of released.
TEST(ClientDiscardableSharedMemoryManagerTest, ReleaseUnlocked) {
const size_t page_size = base::GetPageSize();
TestClientDiscardableSharedMemoryManager client;
ASSERT_EQ(client.GetBytesAllocated(), 0u);
ASSERT_EQ(client.GetSizeOfFreeLists(), 0u);
auto mem1 = client.AllocateLockedDiscardableMemory(page_size * 3);
auto mem2 = client.AllocateLockedDiscardableMemory(page_size * 2);
size_t freelist_size = client.GetSizeOfFreeLists();
EXPECT_EQ(client.GetBytesAllocated(), 5 * page_size);
mem1 = nullptr;
// Less memory is now allocated, but freelists are grown.
EXPECT_EQ(client.GetBytesAllocated(), page_size * 2);
EXPECT_EQ(client.GetSizeOfFreeLists(), freelist_size + page_size * 3);
client.PurgeUnlockedMemory();
// Purging doesn't remove any memory since none is unlocked, also doesn't
// remove freelists since we still have some.
EXPECT_EQ(client.GetBytesAllocated(), page_size * 2);
EXPECT_EQ(client.GetSizeOfFreeLists(), freelist_size + page_size * 3);
mem2->Unlock();
// No change in memory usage, since memory was only unlocked not released.
EXPECT_EQ(client.GetBytesAllocated(), page_size * 2);
EXPECT_EQ(client.GetSizeOfFreeLists(), freelist_size + page_size * 3);
client.PurgeUnlockedMemory();
// Purging now shrinks freelists as well.
EXPECT_EQ(client.GetBytesAllocated(), 0u);
EXPECT_EQ(client.GetSizeOfFreeLists(), 0u);
}
} // namespace
} // namespace discardable_memory
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment