Commit 2ce02284 authored by reveman's avatar reveman Committed by Commit bot

content: Release all free discardable memory when idle.

This adds better tracking of free discardable memory in child
processes and the ability to release the memory without having
the browser process purge the memory.

BUG=460995
TEST=content_unittests --gtest_filter=DiscardableSharedMemoryHeapTest.*

Review URL: https://codereview.chromium.org/981403002

Cr-Commit-Position: refs/heads/master@{#319779}
parent d20d7b3d
...@@ -147,6 +147,7 @@ size_t RegisterChromeCrashKeys() { ...@@ -147,6 +147,7 @@ size_t RegisterChromeCrashKeys() {
// base/: // base/:
{ "dm-usage", kSmallSize }, { "dm-usage", kSmallSize },
{ "dm-usage-free", kSmallSize },
{ "total-dm-usage", kSmallSize }, { "total-dm-usage", kSmallSize },
// content/: // content/:
{ kFontKeyName, kSmallSize}, { kFontKeyName, kSmallSize},
......
...@@ -52,12 +52,14 @@ class DiscardableMemoryShmemChunkImpl ...@@ -52,12 +52,14 @@ class DiscardableMemoryShmemChunkImpl
ChildDiscardableSharedMemoryManager::ChildDiscardableSharedMemoryManager( ChildDiscardableSharedMemoryManager::ChildDiscardableSharedMemoryManager(
ThreadSafeSender* sender) ThreadSafeSender* sender)
: heap_(base::GetPageSize()), sender_(sender), bytes_allocated_(0) { : heap_(base::GetPageSize()), sender_(sender) {
} }
ChildDiscardableSharedMemoryManager::~ChildDiscardableSharedMemoryManager() { ChildDiscardableSharedMemoryManager::~ChildDiscardableSharedMemoryManager() {
if (bytes_allocated_) // TODO(reveman): Determine if this DCHECK can be enabled. crbug.com/430533
BytesAllocatedChanged(0); // DCHECK_EQ(heap_.GetSize(), heap_.GetFreeListSize());
if (heap_.GetSize())
MemoryUsageChanged(0, 0);
} }
scoped_ptr<base::DiscardableMemoryShmemChunk> scoped_ptr<base::DiscardableMemoryShmemChunk>
...@@ -76,6 +78,7 @@ ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory( ...@@ -76,6 +78,7 @@ ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
// Round up to multiple of page size. // Round up to multiple of page size.
size_t pages = (size + base::GetPageSize() - 1) / base::GetPageSize(); size_t pages = (size + base::GetPageSize() - 1) / base::GetPageSize();
size_t heap_size_prior_to_releasing_purged_memory = heap_.GetSize();
for (;;) { for (;;) {
// Search free list for available space. // Search free list for available space.
scoped_ptr<DiscardableSharedMemoryHeap::Span> free_span = scoped_ptr<DiscardableSharedMemoryHeap::Span> free_span =
...@@ -91,28 +94,27 @@ ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory( ...@@ -91,28 +94,27 @@ ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
free_span->length() * base::GetPageSize()) == free_span->length() * base::GetPageSize()) ==
base::DiscardableSharedMemory::FAILED) { base::DiscardableSharedMemory::FAILED) {
DCHECK(!free_span->shared_memory()->IsMemoryResident()); DCHECK(!free_span->shared_memory()->IsMemoryResident());
// We have to release free memory before |free_span| can be destroyed. // We have to release purged memory before |free_span| can be destroyed.
size_t bytes_released = heap_.ReleaseFreeMemory(); heap_.ReleasePurgedMemory();
DCHECK_NE(bytes_released, 0u);
DCHECK_LE(bytes_released, bytes_allocated_);
bytes_allocated_ -= bytes_released;
BytesAllocatedChanged(bytes_allocated_);
DCHECK(!free_span->shared_memory()); DCHECK(!free_span->shared_memory());
continue; continue;
} }
// Memory usage is guaranteed to have changed after having removed
// at least one span from the free list.
MemoryUsageChanged(heap_.GetSize(), heap_.GetFreeListSize());
return make_scoped_ptr( return make_scoped_ptr(
new DiscardableMemoryShmemChunkImpl(this, free_span.Pass())); new DiscardableMemoryShmemChunkImpl(this, free_span.Pass()));
} }
// Release free memory and free up the address space. Failing to do this // Release purged memory to free up the address space before we attempt to
// can result in the child process running out of address space. // allocate more memory.
size_t bytes_released = heap_.ReleaseFreeMemory(); heap_.ReleasePurgedMemory();
if (bytes_released) {
DCHECK_LE(bytes_released, bytes_allocated_); // Make sure crash keys are up to date in case allocation fails.
bytes_allocated_ -= bytes_released; if (heap_.GetSize() != heap_size_prior_to_releasing_purged_memory)
BytesAllocatedChanged(bytes_allocated_); MemoryUsageChanged(heap_.GetSize(), heap_.GetFreeListSize());
}
size_t pages_to_allocate = size_t pages_to_allocate =
std::max(kAllocationSize / base::GetPageSize(), pages); std::max(kAllocationSize / base::GetPageSize(), pages);
...@@ -122,11 +124,6 @@ ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory( ...@@ -122,11 +124,6 @@ ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
scoped_ptr<base::DiscardableSharedMemory> shared_memory( scoped_ptr<base::DiscardableSharedMemory> shared_memory(
AllocateLockedDiscardableSharedMemory(allocation_size_in_bytes)); AllocateLockedDiscardableSharedMemory(allocation_size_in_bytes));
// Note: use mapped size rather than |allocation_size_in_bytes| as
// the latter only represent the amount of memory available for usage.
bytes_allocated_ += shared_memory->mapped_size();
BytesAllocatedChanged(bytes_allocated_);
// Create span for allocated memory. // Create span for allocated memory.
scoped_ptr<DiscardableSharedMemoryHeap::Span> new_span( scoped_ptr<DiscardableSharedMemoryHeap::Span> new_span(
heap_.Grow(shared_memory.Pass(), allocation_size_in_bytes)); heap_.Grow(shared_memory.Pass(), allocation_size_in_bytes));
...@@ -142,6 +139,8 @@ ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory( ...@@ -142,6 +139,8 @@ ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
heap_.MergeIntoFreeList(leftover.Pass()); heap_.MergeIntoFreeList(leftover.Pass());
} }
MemoryUsageChanged(heap_.GetSize(), heap_.GetFreeListSize());
return make_scoped_ptr( return make_scoped_ptr(
new DiscardableMemoryShmemChunkImpl(this, new_span.Pass())); new DiscardableMemoryShmemChunkImpl(this, new_span.Pass()));
} }
...@@ -149,12 +148,14 @@ ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory( ...@@ -149,12 +148,14 @@ ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
void ChildDiscardableSharedMemoryManager::ReleaseFreeMemory() { void ChildDiscardableSharedMemoryManager::ReleaseFreeMemory() {
base::AutoLock lock(lock_); base::AutoLock lock(lock_);
size_t bytes_released = heap_.ReleaseFreeMemory(); size_t heap_size_prior_to_releasing_memory = heap_.GetSize();
if (bytes_released) {
DCHECK_LE(bytes_released, bytes_allocated_); // Release both purged and free memory.
bytes_allocated_ -= bytes_released; heap_.ReleasePurgedMemory();
BytesAllocatedChanged(bytes_allocated_); heap_.ReleaseFreeMemory();
}
if (heap_.GetSize() != heap_size_prior_to_releasing_memory)
MemoryUsageChanged(heap_.GetSize(), heap_.GetFreeListSize());
} }
bool ChildDiscardableSharedMemoryManager::LockSpan( bool ChildDiscardableSharedMemoryManager::LockSpan(
...@@ -208,6 +209,9 @@ void ChildDiscardableSharedMemoryManager::ReleaseSpan( ...@@ -208,6 +209,9 @@ void ChildDiscardableSharedMemoryManager::ReleaseSpan(
span->shared_memory()->Purge(base::Time::Now()); span->shared_memory()->Purge(base::Time::Now());
heap_.MergeIntoFreeList(span.Pass()); heap_.MergeIntoFreeList(span.Pass());
// Bytes of free memory changed.
MemoryUsageChanged(heap_.GetSize(), heap_.GetFreeListSize());
} }
scoped_ptr<base::DiscardableSharedMemory> scoped_ptr<base::DiscardableSharedMemory>
...@@ -230,14 +234,19 @@ ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory( ...@@ -230,14 +234,19 @@ ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
return memory.Pass(); return memory.Pass();
} }
void ChildDiscardableSharedMemoryManager::BytesAllocatedChanged( void ChildDiscardableSharedMemoryManager::MemoryUsageChanged(
size_t new_bytes_allocated) const { size_t new_bytes_total,
TRACE_COUNTER_ID1("renderer", "DiscardableMemoryUsage", this, size_t new_bytes_free) const {
new_bytes_allocated); TRACE_COUNTER2("renderer", "DiscardableMemoryUsage", "allocated",
new_bytes_total - new_bytes_free, "free", new_bytes_free);
static const char kDiscardableMemoryUsageKey[] = "dm-usage"; static const char kDiscardableMemoryUsageKey[] = "dm-usage";
base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey, base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey,
base::Uint64ToString(new_bytes_allocated)); base::Uint64ToString(new_bytes_total));
static const char kDiscardableMemoryUsageFreeKey[] = "dm-usage-free";
base::debug::SetCrashKeyValue(kDiscardableMemoryUsageFreeKey,
base::Uint64ToString(new_bytes_free));
} }
} // namespace content } // namespace content
...@@ -36,12 +36,12 @@ class CONTENT_EXPORT ChildDiscardableSharedMemoryManager ...@@ -36,12 +36,12 @@ class CONTENT_EXPORT ChildDiscardableSharedMemoryManager
private: private:
scoped_ptr<base::DiscardableSharedMemory> scoped_ptr<base::DiscardableSharedMemory>
AllocateLockedDiscardableSharedMemory(size_t size); AllocateLockedDiscardableSharedMemory(size_t size);
void BytesAllocatedChanged(size_t new_bytes_allocated) const; void MemoryUsageChanged(size_t new_bytes_allocated,
size_t new_bytes_free) const;
mutable base::Lock lock_; mutable base::Lock lock_;
DiscardableSharedMemoryHeap heap_; DiscardableSharedMemoryHeap heap_;
scoped_refptr<ThreadSafeSender> sender_; scoped_refptr<ThreadSafeSender> sender_;
size_t bytes_allocated_;
DISALLOW_COPY_AND_ASSIGN(ChildDiscardableSharedMemoryManager); DISALLOW_COPY_AND_ASSIGN(ChildDiscardableSharedMemoryManager);
}; };
......
...@@ -29,16 +29,37 @@ DiscardableSharedMemoryHeap::Span::Span( ...@@ -29,16 +29,37 @@ DiscardableSharedMemoryHeap::Span::Span(
DiscardableSharedMemoryHeap::Span::~Span() { DiscardableSharedMemoryHeap::Span::~Span() {
} }
DiscardableSharedMemoryHeap::ScopedMemorySegment::ScopedMemorySegment(
DiscardableSharedMemoryHeap* heap,
scoped_ptr<base::DiscardableSharedMemory> shared_memory,
size_t size)
: heap_(heap), shared_memory_(shared_memory.Pass()), size_(size) {
}
DiscardableSharedMemoryHeap::ScopedMemorySegment::~ScopedMemorySegment() {
heap_->ReleaseMemory(shared_memory_.get(), size_);
// Purge memory. This has no effect if already purged.
shared_memory_->Purge(base::Time::Now());
}
bool DiscardableSharedMemoryHeap::ScopedMemorySegment::IsUsed() const {
return heap_->IsMemoryUsed(shared_memory_.get(), size_);
}
bool DiscardableSharedMemoryHeap::ScopedMemorySegment::IsResident() const {
return heap_->IsMemoryResident(shared_memory_.get());
}
DiscardableSharedMemoryHeap::DiscardableSharedMemoryHeap(size_t block_size) DiscardableSharedMemoryHeap::DiscardableSharedMemoryHeap(size_t block_size)
: block_size_(block_size) { : block_size_(block_size), num_blocks_(0), num_free_blocks_(0) {
DCHECK_NE(block_size_, 0u); DCHECK_NE(block_size_, 0u);
DCHECK(IsPowerOfTwo(block_size_)); DCHECK(IsPowerOfTwo(block_size_));
} }
DiscardableSharedMemoryHeap::~DiscardableSharedMemoryHeap() { DiscardableSharedMemoryHeap::~DiscardableSharedMemoryHeap() {
for (auto shared_memory : shared_memory_segments_) memory_segments_.clear();
ReleaseMemory(shared_memory); DCHECK_EQ(num_blocks_, 0u);
DCHECK_EQ(num_free_blocks_, 0u);
DCHECK(free_spans_.empty()); DCHECK(free_spans_.empty());
} }
...@@ -59,9 +80,11 @@ scoped_ptr<DiscardableSharedMemoryHeap::Span> DiscardableSharedMemoryHeap::Grow( ...@@ -59,9 +80,11 @@ scoped_ptr<DiscardableSharedMemoryHeap::Span> DiscardableSharedMemoryHeap::Grow(
DCHECK(spans_.find(span->start_ + span->length_ - 1) == spans_.end()); DCHECK(spans_.find(span->start_ + span->length_ - 1) == spans_.end());
RegisterSpan(span.get()); RegisterSpan(span.get());
// Start tracking if segment is resident by adding it to num_blocks_ += span->length_;
// |shared_memory_segments_|.
shared_memory_segments_.push_back(shared_memory.release()); // Start tracking if segment is resident by adding it to |memory_segments_|.
memory_segments_.push_back(
new ScopedMemorySegment(this, shared_memory.Pass(), size));
return span.Pass(); return span.Pass();
} }
...@@ -69,6 +92,9 @@ scoped_ptr<DiscardableSharedMemoryHeap::Span> DiscardableSharedMemoryHeap::Grow( ...@@ -69,6 +92,9 @@ scoped_ptr<DiscardableSharedMemoryHeap::Span> DiscardableSharedMemoryHeap::Grow(
void DiscardableSharedMemoryHeap::MergeIntoFreeList(scoped_ptr<Span> span) { void DiscardableSharedMemoryHeap::MergeIntoFreeList(scoped_ptr<Span> span) {
DCHECK(span->shared_memory_); DCHECK(span->shared_memory_);
// First add length of |span| to |num_free_blocks_|.
num_free_blocks_ += span->length_;
// Merge with previous span if possible. // Merge with previous span if possible.
SpanMap::iterator prev_it = spans_.find(span->start_ - 1); SpanMap::iterator prev_it = spans_.find(span->start_ - 1);
if (prev_it != spans_.end() && IsInFreeList(prev_it->second)) { if (prev_it != spans_.end() && IsInFreeList(prev_it->second)) {
...@@ -142,30 +168,33 @@ DiscardableSharedMemoryHeap::SearchFreeList(size_t blocks) { ...@@ -142,30 +168,33 @@ DiscardableSharedMemoryHeap::SearchFreeList(size_t blocks) {
return best ? Carve(best, blocks) : nullptr; return best ? Carve(best, blocks) : nullptr;
} }
size_t DiscardableSharedMemoryHeap::ReleaseFreeMemory() { void DiscardableSharedMemoryHeap::ReleaseFreeMemory() {
size_t bytes_released = 0; // Erase all free segments after rearranging the segments in such a way
size_t i = 0; // that used segments precede all free segments.
memory_segments_.erase(
// Release memory for all non-resident segments. std::partition(
while (i < shared_memory_segments_.size()) { memory_segments_.begin(), memory_segments_.end(),
base::DiscardableSharedMemory* shared_memory = shared_memory_segments_[i]; [](const ScopedMemorySegment* segment) { return segment->IsUsed(); }),
memory_segments_.end());
// Skip segment if still resident. }
if (shared_memory->IsMemoryResident()) {
++i;
continue;
}
bytes_released += shared_memory->mapped_size();
// Release the memory and unregistering all associated spans. void DiscardableSharedMemoryHeap::ReleasePurgedMemory() {
ReleaseMemory(shared_memory); // Erase all purged segments after rearranging the segments in such a way
// that resident segments precede all purged segments.
memory_segments_.erase(
std::partition(memory_segments_.begin(), memory_segments_.end(),
[](const ScopedMemorySegment* segment) {
return segment->IsResident();
}),
memory_segments_.end());
}
std::swap(shared_memory_segments_[i], shared_memory_segments_.back()); size_t DiscardableSharedMemoryHeap::GetSize() const {
shared_memory_segments_.pop_back(); return num_blocks_ * block_size_;
} }
return bytes_released; size_t DiscardableSharedMemoryHeap::GetFreeListSize() const {
return num_free_blocks_ * block_size_;
} }
scoped_ptr<DiscardableSharedMemoryHeap::Span> scoped_ptr<DiscardableSharedMemoryHeap::Span>
...@@ -194,6 +223,11 @@ DiscardableSharedMemoryHeap::Carve(Span* span, size_t blocks) { ...@@ -194,6 +223,11 @@ DiscardableSharedMemoryHeap::Carve(Span* span, size_t blocks) {
spans_[serving->start_ + blocks - 1] = serving.get(); spans_[serving->start_ + blocks - 1] = serving.get();
} }
// |serving| is no longer in the free list, remove its length from
// |num_free_blocks_|.
DCHECK_GE(num_free_blocks_, serving->length_);
num_free_blocks_ -= serving->length_;
return serving.Pass(); return serving.Pass();
} }
...@@ -214,11 +248,30 @@ void DiscardableSharedMemoryHeap::UnregisterSpan(Span* span) { ...@@ -214,11 +248,30 @@ void DiscardableSharedMemoryHeap::UnregisterSpan(Span* span) {
} }
} }
bool DiscardableSharedMemoryHeap::IsMemoryUsed(
const base::DiscardableSharedMemory* shared_memory,
size_t size) {
size_t offset =
reinterpret_cast<size_t>(shared_memory->memory()) / block_size_;
size_t length = size / block_size_;
DCHECK(spans_.find(offset) != spans_.end());
Span* span = spans_[offset];
DCHECK_LE(span->length_, length);
// Memory is used if first span is not in free list or shorter than segment.
return !IsInFreeList(span) || span->length_ != length;
}
bool DiscardableSharedMemoryHeap::IsMemoryResident(
const base::DiscardableSharedMemory* shared_memory) {
return shared_memory->IsMemoryResident();
}
void DiscardableSharedMemoryHeap::ReleaseMemory( void DiscardableSharedMemoryHeap::ReleaseMemory(
base::DiscardableSharedMemory* shared_memory) { const base::DiscardableSharedMemory* shared_memory,
size_t size) {
size_t offset = size_t offset =
reinterpret_cast<size_t>(shared_memory->memory()) / block_size_; reinterpret_cast<size_t>(shared_memory->memory()) / block_size_;
size_t end = offset + shared_memory->mapped_size() / block_size_; size_t end = offset + size / block_size_;
while (offset < end) { while (offset < end) {
DCHECK(spans_.find(offset) != spans_.end()); DCHECK(spans_.find(offset) != spans_.end());
Span* span = spans_[offset]; Span* span = spans_[offset];
...@@ -228,9 +281,15 @@ void DiscardableSharedMemoryHeap::ReleaseMemory( ...@@ -228,9 +281,15 @@ void DiscardableSharedMemoryHeap::ReleaseMemory(
offset += span->length_; offset += span->length_;
// If |span| is in the free list, remove it. DCHECK_GE(num_blocks_, span->length_);
if (IsInFreeList(span)) num_blocks_ -= span->length_;
// If |span| is in the free list, remove it and update |num_free_blocks_|.
if (IsInFreeList(span)) {
DCHECK_GE(num_free_blocks_, span->length_);
num_free_blocks_ -= span->length_;
RemoveFromFreeList(span); RemoveFromFreeList(span);
}
} }
} }
......
...@@ -5,11 +5,8 @@ ...@@ -5,11 +5,8 @@
#ifndef CONTENT_COMMON_DISCARDABLE_SHARED_MEMORY_HEAP_H_ #ifndef CONTENT_COMMON_DISCARDABLE_SHARED_MEMORY_HEAP_H_
#define CONTENT_COMMON_DISCARDABLE_SHARED_MEMORY_HEAP_H_ #define CONTENT_COMMON_DISCARDABLE_SHARED_MEMORY_HEAP_H_
#include <bitset>
#include "base/containers/hash_tables.h" #include "base/containers/hash_tables.h"
#include "base/containers/linked_list.h" #include "base/containers/linked_list.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h" #include "base/memory/scoped_vector.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
...@@ -68,21 +65,53 @@ class CONTENT_EXPORT DiscardableSharedMemoryHeap { ...@@ -68,21 +65,53 @@ class CONTENT_EXPORT DiscardableSharedMemoryHeap {
// memory. If found, the span is removed from the free list and returned. // memory. If found, the span is removed from the free list and returned.
scoped_ptr<Span> SearchFreeList(size_t blocks); scoped_ptr<Span> SearchFreeList(size_t blocks);
// Release shared memory segments that have been purged. Returns bytes of // Release free shared memory segments.
// memory that were released. void ReleaseFreeMemory();
size_t ReleaseFreeMemory();
// Release shared memory segments that have been purged.
void ReleasePurgedMemory();
// Returns total bytes of memory in heap.
size_t GetSize() const;
// Returns bytes of memory currently in the free list.
size_t GetFreeListSize() const;
private: private:
class ScopedMemorySegment {
public:
ScopedMemorySegment(DiscardableSharedMemoryHeap* heap,
scoped_ptr<base::DiscardableSharedMemory> shared_memory,
size_t size);
~ScopedMemorySegment();
bool IsUsed() const;
bool IsResident() const;
private:
DiscardableSharedMemoryHeap* const heap_;
scoped_ptr<base::DiscardableSharedMemory> shared_memory_;
const size_t size_;
DISALLOW_COPY_AND_ASSIGN(ScopedMemorySegment);
};
scoped_ptr<Span> RemoveFromFreeList(Span* span); scoped_ptr<Span> RemoveFromFreeList(Span* span);
scoped_ptr<Span> Carve(Span* span, size_t blocks); scoped_ptr<Span> Carve(Span* span, size_t blocks);
void RegisterSpan(Span* span); void RegisterSpan(Span* span);
void UnregisterSpan(Span* span); void UnregisterSpan(Span* span);
void ReleaseMemory(base::DiscardableSharedMemory* shared_memory); bool IsMemoryUsed(const base::DiscardableSharedMemory* shared_memory,
size_t size);
bool IsMemoryResident(const base::DiscardableSharedMemory* shared_memory);
void ReleaseMemory(const base::DiscardableSharedMemory* shared_memory,
size_t size);
size_t block_size_; size_t block_size_;
size_t num_blocks_;
size_t num_free_blocks_;
// Discardable shared memory instances. // Vector of memory segments.
ScopedVector<base::DiscardableSharedMemory> shared_memory_segments_; ScopedVector<ScopedMemorySegment> memory_segments_;
// Mapping from first/last block of span to Span instance. // Mapping from first/last block of span to Span instance.
typedef base::hash_map<size_t, Span*> SpanMap; typedef base::hash_map<size_t, Span*> SpanMap;
......
...@@ -11,12 +11,16 @@ ...@@ -11,12 +11,16 @@
namespace content { namespace content {
namespace { namespace {
class DiscardableSharedMemoryHeapTest : public testing::Test {}; TEST(DiscardableSharedMemoryHeapTest, Basic) {
TEST_F(DiscardableSharedMemoryHeapTest, Basic) {
size_t block_size = base::GetPageSize(); size_t block_size = base::GetPageSize();
DiscardableSharedMemoryHeap heap(block_size); DiscardableSharedMemoryHeap heap(block_size);
// Initial size should be 0.
EXPECT_EQ(0u, heap.GetSize());
// Initial free list size should be 0.
EXPECT_EQ(0u, heap.GetFreeListSize());
// Free list is initially empty. // Free list is initially empty.
EXPECT_FALSE(heap.SearchFreeList(1)); EXPECT_FALSE(heap.SearchFreeList(1));
...@@ -31,12 +35,21 @@ TEST_F(DiscardableSharedMemoryHeapTest, Basic) { ...@@ -31,12 +35,21 @@ TEST_F(DiscardableSharedMemoryHeapTest, Basic) {
scoped_ptr<DiscardableSharedMemoryHeap::Span> new_span( scoped_ptr<DiscardableSharedMemoryHeap::Span> new_span(
heap.Grow(memory.Pass(), memory_size)); heap.Grow(memory.Pass(), memory_size));
// Size should match |memory_size|.
EXPECT_EQ(memory_size, heap.GetSize());
// Free list size should still be 0.
EXPECT_EQ(0u, heap.GetFreeListSize());
// Free list should still be empty as |new_span| is currently in use. // Free list should still be empty as |new_span| is currently in use.
EXPECT_FALSE(heap.SearchFreeList(1)); EXPECT_FALSE(heap.SearchFreeList(1));
// Done using |new_span|. Merge it into the free list. // Done using |new_span|. Merge it into the free list.
heap.MergeIntoFreeList(new_span.Pass()); heap.MergeIntoFreeList(new_span.Pass());
// Free list size should now match |memory_size|.
EXPECT_EQ(memory_size, heap.GetFreeListSize());
// Free list should not contain a span that is larger than kBlocks. // Free list should not contain a span that is larger than kBlocks.
EXPECT_FALSE(heap.SearchFreeList(kBlocks + 1)); EXPECT_FALSE(heap.SearchFreeList(kBlocks + 1));
...@@ -52,7 +65,7 @@ TEST_F(DiscardableSharedMemoryHeapTest, Basic) { ...@@ -52,7 +65,7 @@ TEST_F(DiscardableSharedMemoryHeapTest, Basic) {
heap.MergeIntoFreeList(span.Pass()); heap.MergeIntoFreeList(span.Pass());
} }
TEST_F(DiscardableSharedMemoryHeapTest, SplitAndMerge) { TEST(DiscardableSharedMemoryHeapTest, SplitAndMerge) {
size_t block_size = base::GetPageSize(); size_t block_size = base::GetPageSize();
DiscardableSharedMemoryHeap heap(block_size); DiscardableSharedMemoryHeap heap(block_size);
...@@ -105,7 +118,7 @@ TEST_F(DiscardableSharedMemoryHeapTest, SplitAndMerge) { ...@@ -105,7 +118,7 @@ TEST_F(DiscardableSharedMemoryHeapTest, SplitAndMerge) {
heap.MergeIntoFreeList(large_span.Pass()); heap.MergeIntoFreeList(large_span.Pass());
} }
TEST_F(DiscardableSharedMemoryHeapTest, MergeSingleBlockSpan) { TEST(DiscardableSharedMemoryHeapTest, MergeSingleBlockSpan) {
size_t block_size = base::GetPageSize(); size_t block_size = base::GetPageSize();
DiscardableSharedMemoryHeap heap(block_size); DiscardableSharedMemoryHeap heap(block_size);
...@@ -130,7 +143,7 @@ TEST_F(DiscardableSharedMemoryHeapTest, MergeSingleBlockSpan) { ...@@ -130,7 +143,7 @@ TEST_F(DiscardableSharedMemoryHeapTest, MergeSingleBlockSpan) {
heap.MergeIntoFreeList(leftover.Pass()); heap.MergeIntoFreeList(leftover.Pass());
} }
TEST_F(DiscardableSharedMemoryHeapTest, Grow) { TEST(DiscardableSharedMemoryHeapTest, Grow) {
size_t block_size = base::GetPageSize(); size_t block_size = base::GetPageSize();
DiscardableSharedMemoryHeap heap(block_size); DiscardableSharedMemoryHeap heap(block_size);
...@@ -161,7 +174,33 @@ TEST_F(DiscardableSharedMemoryHeapTest, Grow) { ...@@ -161,7 +174,33 @@ TEST_F(DiscardableSharedMemoryHeapTest, Grow) {
heap.MergeIntoFreeList(span2.Pass()); heap.MergeIntoFreeList(span2.Pass());
} }
TEST_F(DiscardableSharedMemoryHeapTest, ReleaseFreeMemory) { TEST(DiscardableSharedMemoryHeapTest, ReleaseFreeMemory) {
size_t block_size = base::GetPageSize();
DiscardableSharedMemoryHeap heap(block_size);
scoped_ptr<base::DiscardableSharedMemory> memory(
new base::DiscardableSharedMemory);
ASSERT_TRUE(memory->CreateAndMap(block_size));
scoped_ptr<DiscardableSharedMemoryHeap::Span> span =
heap.Grow(memory.Pass(), block_size);
// Free list should be empty.
EXPECT_EQ(0u, heap.GetFreeListSize());
heap.ReleaseFreeMemory();
// Size should still match |block_size|.
EXPECT_EQ(block_size, heap.GetSize());
heap.MergeIntoFreeList(span.Pass());
heap.ReleaseFreeMemory();
// Memory should have been released.
EXPECT_EQ(0u, heap.GetSize());
EXPECT_EQ(0u, heap.GetFreeListSize());
}
TEST(DiscardableSharedMemoryHeapTest, ReleasePurgedMemory) {
size_t block_size = base::GetPageSize(); size_t block_size = base::GetPageSize();
DiscardableSharedMemoryHeap heap(block_size); DiscardableSharedMemoryHeap heap(block_size);
...@@ -177,10 +216,13 @@ TEST_F(DiscardableSharedMemoryHeapTest, ReleaseFreeMemory) { ...@@ -177,10 +216,13 @@ TEST_F(DiscardableSharedMemoryHeapTest, ReleaseFreeMemory) {
// Purge and release shared memory. // Purge and release shared memory.
bool rv = span->shared_memory()->Purge(base::Time::Now()); bool rv = span->shared_memory()->Purge(base::Time::Now());
EXPECT_TRUE(rv); EXPECT_TRUE(rv);
heap.ReleaseFreeMemory(); heap.ReleasePurgedMemory();
// Shared memory backing for |span| should be gone. // Shared memory backing for |span| should be gone.
EXPECT_FALSE(span->shared_memory()); EXPECT_FALSE(span->shared_memory());
// Size should be 0.
EXPECT_EQ(0u, heap.GetSize());
} }
} // namespace } // namespace
......
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