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") {
"//components/content_settings/browser:unit_tests",
"//components/contextual_search/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/service:unit_tests",
"//components/dom_distiller/content/browser:unit_tests",
......
......@@ -21,15 +21,3 @@ component("client") {
"//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 @@
#include "components/discardable_memory/client/client_discardable_shared_memory_manager.h"
#include <inttypes.h>
#include <algorithm>
#include <memory>
#include <utility>
#include "base/atomic_sequence_num.h"
#include "base/bind.h"
#include "base/feature_list.h"
#include "base/macros.h"
#include "base/memory/discardable_memory.h"
#include "base/memory/discardable_shared_memory.h"
......@@ -32,13 +34,6 @@ namespace {
// Global atomic to generate unique discardable shared memory IDs.
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() {
const size_t kOneMegabyteInBytes = 1024 * 1024;
......@@ -67,108 +62,80 @@ size_t GetDefaultAllocationSize() {
#endif
}
void InitManagerMojoOnIO(
mojo::Remote<mojom::DiscardableSharedMemoryManager>* manager_mojo,
mojo::PendingRemote<mojom::DiscardableSharedMemoryManager> remote) {
manager_mojo->Bind(std::move(remote));
}
class DiscardableMemoryImpl : public base::DiscardableMemory {
public:
DiscardableMemoryImpl(ClientDiscardableSharedMemoryManager* manager,
std::unique_ptr<DiscardableSharedMemoryHeap::Span> span)
: manager_(manager), span_(std::move(span)), is_locked_(true) {}
void DeletedDiscardableSharedMemoryOnIO(
mojo::Remote<mojom::DiscardableSharedMemoryManager>* manager_mojo,
int32_t id) {
(*manager_mojo)->DeletedDiscardableSharedMemory(id);
}
~DiscardableMemoryImpl() override {
if (is_locked_)
manager_->UnlockSpan(span_.get());
} // namespace
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;
manager_->ReleaseSpan(std::move(span_));
}
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() {
base::AutoLock lock(manager_->GetLock());
DCHECK(!is_locked_);
if (!manager_->LockSpan(span_.get()))
return false;
if (!span_)
return false;
is_locked_ = true;
return true;
}
void Unlock() override {
DCHECK(is_locked_);
if (!manager_->LockSpan(span_.get()))
return false;
manager_->UnlockSpan(span_.get());
is_locked_ = false;
}
void* data() const override {
DCHECK(is_locked_);
return reinterpret_cast<void*>(span_->start() * base::GetPageSize());
}
is_locked_ = true;
return true;
}
void DiscardForTesting() override {
DCHECK(!is_locked_);
span_->shared_memory()->Purge(base::Time::Now());
}
void ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::Unlock() {
base::AutoLock lock(manager_->GetLock());
DCHECK(is_locked_);
DCHECK(span_);
base::trace_event::MemoryAllocatorDump* CreateMemoryAllocatorDump(
const char* name,
base::trace_event::ProcessMemoryDump* pmd) const override {
return manager_->CreateMemoryAllocatorDump(span_.get(), name, pmd);
}
manager_->UnlockSpan(span_.get());
is_locked_ = false;
}
private:
ClientDiscardableSharedMemoryManager* const manager_;
std::unique_ptr<DiscardableSharedMemoryHeap::Span> span_;
bool is_locked_;
std::unique_ptr<DiscardableSharedMemoryHeap::Span>
ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::Purge() {
DCHECK(span_);
if (is_locked_)
return nullptr;
return std::move(span_);
}
DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryImpl);
};
void* ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::data()
const {
#if DCHECK_IS_ON()
{
base::AutoLock lock(manager_->GetLock());
DCHECK(is_locked_);
}
#endif
return reinterpret_cast<void*>(span_->start() * base::GetPageSize());
void InitManagerMojoOnIO(
mojo::Remote<mojom::DiscardableSharedMemoryManager>* manager_mojo,
mojo::PendingRemote<mojom::DiscardableSharedMemoryManager> remote) {
manager_mojo->Bind(std::move(remote));
}
void ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::
DiscardForTesting() {
#if DCHECK_IS_ON()
{
base::AutoLock lock(manager_->GetLock());
DCHECK(!is_locked_);
}
#endif
span_->shared_memory()->Purge(base::Time::Now());
void DeletedDiscardableSharedMemoryOnIO(
mojo::Remote<mojom::DiscardableSharedMemoryManager>* manager_mojo,
int32_t id) {
(*manager_mojo)->DeletedDiscardableSharedMemory(id);
}
base::trace_event::MemoryAllocatorDump* ClientDiscardableSharedMemoryManager::
DiscardableMemoryImpl::CreateMemoryAllocatorDump(
const char* name,
base::trace_event::ProcessMemoryDump* pmd) const {
return manager_->CreateMemoryAllocatorDump(span_.get(), name, pmd);
}
} // namespace
ClientDiscardableSharedMemoryManager::ClientDiscardableSharedMemoryManager(
mojo::PendingRemote<mojom::DiscardableSharedMemoryManager> manager,
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<
mojo::Remote<mojom::DiscardableSharedMemoryManager>>()) {
mojo::Remote<mojom::DiscardableSharedMemoryManager>>()),
heap_(std::make_unique<DiscardableSharedMemoryHeap>()) {
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
this, "ClientDiscardableSharedMemoryManager",
base::ThreadTaskRunnerHandle::Get());
......@@ -177,11 +144,6 @@ ClientDiscardableSharedMemoryManager::ClientDiscardableSharedMemoryManager(
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() {
base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
this);
......@@ -263,10 +225,7 @@ ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
// at least one span from the free lists.
MemoryUsageChanged(heap_->GetSize(), heap_->GetSizeOfFreeLists());
auto discardable_memory =
std::make_unique<DiscardableMemoryImpl>(this, std::move(free_span));
allocated_memory_.insert(discardable_memory.get());
return std::move(discardable_memory);
return std::make_unique<DiscardableMemoryImpl>(this, std::move(free_span));
}
// Release purged memory to free up the address space before we attempt to
......@@ -320,10 +279,7 @@ ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
MemoryUsageChanged(heap_->GetSize(), heap_->GetSizeOfFreeLists());
auto discardable_memory =
std::make_unique<DiscardableMemoryImpl>(this, std::move(new_span));
allocated_memory_.insert(discardable_memory.get());
return std::move(discardable_memory);
return std::make_unique<DiscardableMemoryImpl>(this, std::move(new_span));
}
bool ClientDiscardableSharedMemoryManager::OnMemoryDump(
......@@ -338,46 +294,11 @@ size_t ClientDiscardableSharedMemoryManager::GetBytesAllocated() const {
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() {
if (base::FeatureList::IsEnabled(kPurgeUnlockedMemory)) {
PurgeUnlockedMemory();
} else {
ReleaseFreeMemoryImpl();
}
}
void ClientDiscardableSharedMemoryManager::ReleaseFreeMemoryImpl() {
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.
......@@ -390,6 +311,8 @@ void ClientDiscardableSharedMemoryManager::ReleaseFreeMemoryImpl() {
bool ClientDiscardableSharedMemoryManager::LockSpan(
DiscardableSharedMemoryHeap::Span* span) {
base::AutoLock lock(lock_);
if (!span->shared_memory())
return false;
......@@ -415,6 +338,8 @@ bool ClientDiscardableSharedMemoryManager::LockSpan(
void ClientDiscardableSharedMemoryManager::UnlockSpan(
DiscardableSharedMemoryHeap::Span* span) {
base::AutoLock lock(lock_);
DCHECK(span->shared_memory());
size_t offset = span->start() * base::GetPageSize() -
reinterpret_cast<size_t>(span->shared_memory()->memory());
......@@ -424,18 +349,9 @@ void ClientDiscardableSharedMemoryManager::UnlockSpan(
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(
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.
if (!span->shared_memory())
......@@ -464,7 +380,6 @@ ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
"ClientDiscardableSharedMemoryManager::"
"AllocateLockedDiscardableSharedMemory",
"size", size, "id", id);
base::UnsafeSharedMemoryRegion region;
base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
......@@ -479,8 +394,8 @@ ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
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.
// don't want to crash the browser process for that, which is why the check is
// here, and not there.
if (!region.IsValid())
return nullptr;
......
......@@ -8,8 +8,9 @@
#include <stddef.h>
#include <memory>
#include <set>
#include "base/callback_helpers.h"
#include "base/macros.h"
#include "base/memory/discardable_memory_allocator.h"
#include "base/memory/ref_counted.h"
#include "base/memory/unsafe_shared_memory_region.h"
......@@ -46,16 +47,12 @@ class DISCARDABLE_MEMORY_EXPORT ClientDiscardableSharedMemoryManager
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
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.
void ReleaseFreeMemory() override;
bool LockSpan(DiscardableSharedMemoryHeap::Span* span)
EXCLUSIVE_LOCKS_REQUIRED(GetLock());
void UnlockSpan(DiscardableSharedMemoryHeap::Span* span)
EXCLUSIVE_LOCKS_REQUIRED(GetLock());
bool LockSpan(DiscardableSharedMemoryHeap::Span* span);
void UnlockSpan(DiscardableSharedMemoryHeap::Span* span);
void ReleaseSpan(std::unique_ptr<DiscardableSharedMemoryHeap::Span> span);
base::trace_event::MemoryAllocatorDump* CreateMemoryAllocatorDump(
DiscardableSharedMemoryHeap::Span* span,
......@@ -73,47 +70,8 @@ class DISCARDABLE_MEMORY_EXPORT ClientDiscardableSharedMemoryManager
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:
class DiscardableMemoryImpl : public base::DiscardableMemory {
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>
std::unique_ptr<base::DiscardableSharedMemory>
AllocateLockedDiscardableSharedMemory(size_t size, int32_t id);
void AllocateOnIO(size_t size,
int32_t id,
......@@ -123,27 +81,18 @@ class DISCARDABLE_MEMORY_EXPORT ClientDiscardableSharedMemoryManager
base::ScopedClosureRunner closure_runner,
base::UnsafeSharedMemoryRegion ret_region);
// This is only virtual for testing.
virtual void DeletedDiscardableSharedMemory(int32_t id);
void DeletedDiscardableSharedMemory(int32_t id);
void MemoryUsageChanged(size_t new_bytes_allocated,
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_;
// TODO(penghuang): Switch to SharedRemote when it starts supporting
// sync method call.
std::unique_ptr<mojo::Remote<mojom::DiscardableSharedMemoryManager>>
manager_mojo_;
// Holds all locked and unlocked instances which have not yet been purged.
std::set<DiscardableMemoryImpl*> allocated_memory_ GUARDED_BY(lock_);
mutable base::Lock lock_;
std::unique_ptr<DiscardableSharedMemoryHeap> heap_ GUARDED_BY(lock_);
size_t bytes_allocated_limit_for_testing_ = 0;
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