Commit 1500e1a7 authored by reveman's avatar reveman Committed by Commit bot

content: Add DeletedDiscardableSharedMemory IPC.

This provides a mechanism for child processes to notify the
browser process that a discardable memory segment has been
deleted. This reduces the chance of the browser unnecessarily
purging a memory segment that is still useful and it makes
total discardable memory usage stats in the browser process
better match current usage by all child processes.

BUG=
TEST=content_unittests --gtest_filter=DiscardableSharedMemoryHeapTest.DeletedCallback, content_browsertests --gtest_filter=ChildDiscardableSharedMemoryManagerBrowserTest.ReleaseFreeMemory

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

Cr-Commit-Position: refs/heads/master@{#320848}
parent 159f0e04
...@@ -37,7 +37,6 @@ ...@@ -37,7 +37,6 @@
#include "content/common/cookie_data.h" #include "content/common/cookie_data.h"
#include "content/common/frame_messages.h" #include "content/common/frame_messages.h"
#include "content/common/gpu/client/gpu_memory_buffer_impl.h" #include "content/common/gpu/client/gpu_memory_buffer_impl.h"
#include "content/common/host_discardable_shared_memory_manager.h"
#include "content/common/host_shared_bitmap_manager.h" #include "content/common/host_shared_bitmap_manager.h"
#include "content/common/media/media_param_traits.h" #include "content/common/media/media_param_traits.h"
#include "content/common/view_messages.h" #include "content/common/view_messages.h"
...@@ -422,6 +421,8 @@ bool RenderMessageFilter::OnMessageReceived(const IPC::Message& message) { ...@@ -422,6 +421,8 @@ bool RenderMessageFilter::OnMessageReceived(const IPC::Message& message) {
IPC_MESSAGE_HANDLER( IPC_MESSAGE_HANDLER(
ChildProcessHostMsg_SyncAllocateLockedDiscardableSharedMemory, ChildProcessHostMsg_SyncAllocateLockedDiscardableSharedMemory,
OnAllocateLockedDiscardableSharedMemory) OnAllocateLockedDiscardableSharedMemory)
IPC_MESSAGE_HANDLER(ChildProcessHostMsg_DeletedDiscardableSharedMemory,
OnDeletedDiscardableSharedMemory)
IPC_MESSAGE_HANDLER(ViewHostMsg_DidGenerateCacheableMetadata, IPC_MESSAGE_HANDLER(ViewHostMsg_DidGenerateCacheableMetadata,
OnCacheableMetadataAvailable) OnCacheableMetadataAvailable)
IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_Keygen, OnKeygen) IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_Keygen, OnKeygen)
...@@ -954,10 +955,17 @@ void RenderMessageFilter::OnDeletedSharedBitmap(const cc::SharedBitmapId& id) { ...@@ -954,10 +955,17 @@ void RenderMessageFilter::OnDeletedSharedBitmap(const cc::SharedBitmapId& id) {
void RenderMessageFilter::OnAllocateLockedDiscardableSharedMemory( void RenderMessageFilter::OnAllocateLockedDiscardableSharedMemory(
uint32 size, uint32 size,
DiscardableSharedMemoryId id,
base::SharedMemoryHandle* handle) { base::SharedMemoryHandle* handle) {
HostDiscardableSharedMemoryManager::current() HostDiscardableSharedMemoryManager::current()
->AllocateLockedDiscardableSharedMemoryForChild( ->AllocateLockedDiscardableSharedMemoryForChild(PeerHandle(), size, id,
PeerHandle(), size, handle); handle);
}
void RenderMessageFilter::OnDeletedDiscardableSharedMemory(
DiscardableSharedMemoryId id) {
HostDiscardableSharedMemoryManager::current()
->ChildDeletedDiscardableSharedMemory(id, PeerHandle());
} }
net::CookieStore* RenderMessageFilter::GetCookieStoreForURL( net::CookieStore* RenderMessageFilter::GetCookieStoreForURL(
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "cc/resources/shared_bitmap_manager.h" #include "cc/resources/shared_bitmap_manager.h"
#include "content/common/host_discardable_shared_memory_manager.h"
#include "content/common/host_shared_bitmap_manager.h" #include "content/common/host_shared_bitmap_manager.h"
#include "content/public/browser/browser_message_filter.h" #include "content/public/browser/browser_message_filter.h"
#include "content/public/common/three_d_api_types.h" #include "content/public/common/three_d_api_types.h"
...@@ -245,7 +246,9 @@ class CONTENT_EXPORT RenderMessageFilter : public BrowserMessageFilter { ...@@ -245,7 +246,9 @@ class CONTENT_EXPORT RenderMessageFilter : public BrowserMessageFilter {
// Browser side discardable shared memory allocation. // Browser side discardable shared memory allocation.
void OnAllocateLockedDiscardableSharedMemory( void OnAllocateLockedDiscardableSharedMemory(
uint32 size, uint32 size,
DiscardableSharedMemoryId id,
base::SharedMemoryHandle* handle); base::SharedMemoryHandle* handle);
void OnDeletedDiscardableSharedMemory(DiscardableSharedMemoryId id);
void OnCacheableMetadataAvailable(const GURL& url, void OnCacheableMetadataAvailable(const GURL& url,
base::Time expected_response_time, base::Time expected_response_time,
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "content/child/child_discardable_shared_memory_manager.h" #include "content/child/child_discardable_shared_memory_manager.h"
#include "base/atomic_sequence_num.h"
#include "base/bind.h"
#include "base/debug/crash_logging.h" #include "base/debug/crash_logging.h"
#include "base/memory/discardable_shared_memory.h" #include "base/memory/discardable_shared_memory.h"
#include "base/metrics/histogram.h" #include "base/metrics/histogram.h"
...@@ -23,6 +25,9 @@ const size_t kAllocationSize = 32 * 1024 * 1024; ...@@ -23,6 +25,9 @@ const size_t kAllocationSize = 32 * 1024 * 1024;
const size_t kAllocationSize = 4 * 1024 * 1024; const size_t kAllocationSize = 4 * 1024 * 1024;
#endif #endif
// Global atomic to generate unique discardable shared memory IDs.
base::StaticAtomicSequenceNumber g_next_discardable_shared_memory_id;
class DiscardableMemoryShmemChunkImpl class DiscardableMemoryShmemChunkImpl
: public base::DiscardableMemoryShmemChunk { : public base::DiscardableMemoryShmemChunk {
public: public:
...@@ -48,6 +53,11 @@ class DiscardableMemoryShmemChunkImpl ...@@ -48,6 +53,11 @@ class DiscardableMemoryShmemChunkImpl
DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryShmemChunkImpl); DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryShmemChunkImpl);
}; };
void DeletedDiscardableSharedMemory(scoped_refptr<ThreadSafeSender> sender,
DiscardableSharedMemoryId id) {
sender->Send(new ChildProcessHostMsg_DeletedDiscardableSharedMemory(id));
}
} // namespace } // namespace
ChildDiscardableSharedMemoryManager::ChildDiscardableSharedMemoryManager( ChildDiscardableSharedMemoryManager::ChildDiscardableSharedMemoryManager(
...@@ -132,13 +142,17 @@ ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory( ...@@ -132,13 +142,17 @@ ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
std::max(kAllocationSize / base::GetPageSize(), pages); std::max(kAllocationSize / base::GetPageSize(), pages);
size_t allocation_size_in_bytes = pages_to_allocate * base::GetPageSize(); size_t allocation_size_in_bytes = pages_to_allocate * base::GetPageSize();
DiscardableSharedMemoryId new_id =
g_next_discardable_shared_memory_id.GetNext();
// Ask parent process to allocate a new discardable shared memory segment. // Ask parent process to allocate a new discardable shared memory segment.
scoped_ptr<base::DiscardableSharedMemory> shared_memory( scoped_ptr<base::DiscardableSharedMemory> shared_memory(
AllocateLockedDiscardableSharedMemory(allocation_size_in_bytes)); AllocateLockedDiscardableSharedMemory(allocation_size_in_bytes, new_id));
// 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,
base::Bind(&DeletedDiscardableSharedMemory, sender_, new_id)));
// Unlock and insert any left over memory into free lists. // Unlock and insert any left over memory into free lists.
if (pages < pages_to_allocate) { if (pages < pages_to_allocate) {
...@@ -223,17 +237,17 @@ void ChildDiscardableSharedMemoryManager::ReleaseSpan( ...@@ -223,17 +237,17 @@ void ChildDiscardableSharedMemoryManager::ReleaseSpan(
scoped_ptr<base::DiscardableSharedMemory> scoped_ptr<base::DiscardableSharedMemory>
ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory( ChildDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
size_t size) { size_t size,
TRACE_EVENT1("renderer", DiscardableSharedMemoryId id) {
TRACE_EVENT2("renderer",
"ChildDiscardableSharedMemoryManager::" "ChildDiscardableSharedMemoryManager::"
"AllocateLockedDiscardableSharedMemory", "AllocateLockedDiscardableSharedMemory",
"size", "size", size, "id", id);
size);
base::SharedMemoryHandle handle = base::SharedMemory::NULLHandle(); base::SharedMemoryHandle handle = base::SharedMemory::NULLHandle();
sender_->Send( sender_->Send(
new ChildProcessHostMsg_SyncAllocateLockedDiscardableSharedMemory( new ChildProcessHostMsg_SyncAllocateLockedDiscardableSharedMemory(
size, &handle)); size, id, &handle));
CHECK(base::SharedMemory::IsHandleValid(handle)); CHECK(base::SharedMemory::IsHandleValid(handle));
scoped_ptr<base::DiscardableSharedMemory> memory( scoped_ptr<base::DiscardableSharedMemory> memory(
new base::DiscardableSharedMemory(handle)); new base::DiscardableSharedMemory(handle));
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "content/child/thread_safe_sender.h" #include "content/child/thread_safe_sender.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "content/common/discardable_shared_memory_heap.h" #include "content/common/discardable_shared_memory_heap.h"
#include "content/common/host_discardable_shared_memory_manager.h"
namespace content { namespace content {
...@@ -35,7 +36,8 @@ class CONTENT_EXPORT ChildDiscardableSharedMemoryManager ...@@ -35,7 +36,8 @@ class CONTENT_EXPORT ChildDiscardableSharedMemoryManager
private: private:
scoped_ptr<base::DiscardableSharedMemory> scoped_ptr<base::DiscardableSharedMemory>
AllocateLockedDiscardableSharedMemory(size_t size); AllocateLockedDiscardableSharedMemory(size_t size,
DiscardableSharedMemoryId 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;
......
...@@ -111,4 +111,31 @@ IN_PROC_BROWSER_TEST_F(ChildDiscardableSharedMemoryManagerBrowserTest, ...@@ -111,4 +111,31 @@ IN_PROC_BROWSER_TEST_F(ChildDiscardableSharedMemoryManagerBrowserTest,
} }
} }
IN_PROC_BROWSER_TEST_F(ChildDiscardableSharedMemoryManagerBrowserTest,
DISABLED_ReleaseFreeMemory) {
const size_t kSize = 1024 * 1024; // 1MiB.
NavigateToURL(shell(), GURL(url::kAboutBlankURL));
scoped_ptr<base::DiscardableMemoryShmemChunk> memory;
PostTaskToInProcessRendererAndWait(base::Bind(
&ChildDiscardableSharedMemoryManagerBrowserTest::AllocateLockedMemory,
kSize, &memory));
PostTaskToInProcessRendererAndWait(
base::Bind(&ChildDiscardableSharedMemoryManagerBrowserTest::UnlockMemory,
memory.get()));
PostTaskToInProcessRendererAndWait(
base::Bind(&ChildDiscardableSharedMemoryManagerBrowserTest::FreeMemory,
base::Passed(&memory)));
EXPECT_GE(HostDiscardableSharedMemoryManager::current()->GetBytesAllocated(),
kSize);
PostTaskToInProcessRendererAndWait(base::Bind(
&ChildDiscardableSharedMemoryManagerBrowserTest::ReleaseFreeMemory));
EXPECT_EQ(HostDiscardableSharedMemoryManager::current()->GetBytesAllocated(),
0u);
}
} // content } // content
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/values.h" #include "base/values.h"
#include "cc/resources/shared_bitmap_manager.h" #include "cc/resources/shared_bitmap_manager.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "content/common/host_discardable_shared_memory_manager.h"
#include "ipc/ipc_message_macros.h" #include "ipc/ipc_message_macros.h"
#include "ui/gfx/gpu_memory_buffer.h" #include "ui/gfx/gpu_memory_buffer.h"
...@@ -196,7 +197,13 @@ IPC_MESSAGE_CONTROL2(ChildProcessHostMsg_DeletedGpuMemoryBuffer, ...@@ -196,7 +197,13 @@ IPC_MESSAGE_CONTROL2(ChildProcessHostMsg_DeletedGpuMemoryBuffer,
// Asks the browser to create a block of discardable shared memory for the // Asks the browser to create a block of discardable shared memory for the
// child process. // child process.
IPC_SYNC_MESSAGE_CONTROL1_1( IPC_SYNC_MESSAGE_CONTROL2_1(
ChildProcessHostMsg_SyncAllocateLockedDiscardableSharedMemory, ChildProcessHostMsg_SyncAllocateLockedDiscardableSharedMemory,
uint32 /* size */, uint32 /* size */,
content::DiscardableSharedMemoryId,
base::SharedMemoryHandle) base::SharedMemoryHandle)
// Informs the browser that the child deleted a block of discardable shared
// memory.
IPC_MESSAGE_CONTROL1(ChildProcessHostMsg_DeletedDiscardableSharedMemory,
content::DiscardableSharedMemoryId)
...@@ -34,14 +34,17 @@ DiscardableSharedMemoryHeap::Span::~Span() { ...@@ -34,14 +34,17 @@ DiscardableSharedMemoryHeap::Span::~Span() {
DiscardableSharedMemoryHeap::ScopedMemorySegment::ScopedMemorySegment( DiscardableSharedMemoryHeap::ScopedMemorySegment::ScopedMemorySegment(
DiscardableSharedMemoryHeap* heap, DiscardableSharedMemoryHeap* heap,
scoped_ptr<base::DiscardableSharedMemory> shared_memory, scoped_ptr<base::DiscardableSharedMemory> shared_memory,
size_t size) size_t size,
: heap_(heap), shared_memory_(shared_memory.Pass()), size_(size) { const base::Closure& deleted_callback)
: heap_(heap),
shared_memory_(shared_memory.Pass()),
size_(size),
deleted_callback_(deleted_callback) {
} }
DiscardableSharedMemoryHeap::ScopedMemorySegment::~ScopedMemorySegment() { DiscardableSharedMemoryHeap::ScopedMemorySegment::~ScopedMemorySegment() {
heap_->ReleaseMemory(shared_memory_.get(), size_); heap_->ReleaseMemory(shared_memory_.get(), size_);
// Purge memory. This has no effect if already purged. deleted_callback_.Run();
shared_memory_->Purge(base::Time::Now());
} }
bool DiscardableSharedMemoryHeap::ScopedMemorySegment::IsUsed() const { bool DiscardableSharedMemoryHeap::ScopedMemorySegment::IsUsed() const {
...@@ -71,7 +74,8 @@ DiscardableSharedMemoryHeap::~DiscardableSharedMemoryHeap() { ...@@ -71,7 +74,8 @@ DiscardableSharedMemoryHeap::~DiscardableSharedMemoryHeap() {
scoped_ptr<DiscardableSharedMemoryHeap::Span> DiscardableSharedMemoryHeap::Grow( scoped_ptr<DiscardableSharedMemoryHeap::Span> DiscardableSharedMemoryHeap::Grow(
scoped_ptr<base::DiscardableSharedMemory> shared_memory, scoped_ptr<base::DiscardableSharedMemory> shared_memory,
size_t size) { size_t size,
const base::Closure& deleted_callback) {
// Memory must be aligned to block size. // Memory must be aligned to block size.
DCHECK_EQ( DCHECK_EQ(
reinterpret_cast<size_t>(shared_memory->memory()) & (block_size_ - 1), reinterpret_cast<size_t>(shared_memory->memory()) & (block_size_ - 1),
...@@ -89,8 +93,8 @@ scoped_ptr<DiscardableSharedMemoryHeap::Span> DiscardableSharedMemoryHeap::Grow( ...@@ -89,8 +93,8 @@ scoped_ptr<DiscardableSharedMemoryHeap::Span> DiscardableSharedMemoryHeap::Grow(
num_blocks_ += span->length_; num_blocks_ += span->length_;
// Start tracking if segment is resident by adding it to |memory_segments_|. // Start tracking if segment is resident by adding it to |memory_segments_|.
memory_segments_.push_back( memory_segments_.push_back(new ScopedMemorySegment(this, shared_memory.Pass(),
new ScopedMemorySegment(this, shared_memory.Pass(), size)); size, deleted_callback));
return span.Pass(); return span.Pass();
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#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 "base/callback.h"
#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/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
...@@ -48,9 +49,11 @@ class CONTENT_EXPORT DiscardableSharedMemoryHeap { ...@@ -48,9 +49,11 @@ class CONTENT_EXPORT DiscardableSharedMemoryHeap {
// Grow heap using |shared_memory| and return a span for this new memory. // Grow heap using |shared_memory| and return a span for this new memory.
// |shared_memory| must be aligned to the block size and |size| must be a // |shared_memory| must be aligned to the block size and |size| must be a
// multiple of the block size. // multiple of the block size. |deleted_callback| is called when
// |shared_memory| has been deleted.
scoped_ptr<Span> Grow(scoped_ptr<base::DiscardableSharedMemory> shared_memory, scoped_ptr<Span> Grow(scoped_ptr<base::DiscardableSharedMemory> shared_memory,
size_t size); size_t size,
const base::Closure& deleted_callback);
// Merge |span| into the free lists. This will coalesce |span| with // Merge |span| into the free lists. This will coalesce |span| with
// neighboring free spans when possible. // neighboring free spans when possible.
...@@ -85,7 +88,8 @@ class CONTENT_EXPORT DiscardableSharedMemoryHeap { ...@@ -85,7 +88,8 @@ class CONTENT_EXPORT DiscardableSharedMemoryHeap {
public: public:
ScopedMemorySegment(DiscardableSharedMemoryHeap* heap, ScopedMemorySegment(DiscardableSharedMemoryHeap* heap,
scoped_ptr<base::DiscardableSharedMemory> shared_memory, scoped_ptr<base::DiscardableSharedMemory> shared_memory,
size_t size); size_t size,
const base::Closure& deleted_callback);
~ScopedMemorySegment(); ~ScopedMemorySegment();
bool IsUsed() const; bool IsUsed() const;
...@@ -95,6 +99,7 @@ class CONTENT_EXPORT DiscardableSharedMemoryHeap { ...@@ -95,6 +99,7 @@ class CONTENT_EXPORT DiscardableSharedMemoryHeap {
DiscardableSharedMemoryHeap* const heap_; DiscardableSharedMemoryHeap* const heap_;
scoped_ptr<base::DiscardableSharedMemory> shared_memory_; scoped_ptr<base::DiscardableSharedMemory> shared_memory_;
const size_t size_; const size_t size_;
const base::Closure deleted_callback_;
DISALLOW_COPY_AND_ASSIGN(ScopedMemorySegment); DISALLOW_COPY_AND_ASSIGN(ScopedMemorySegment);
}; };
......
...@@ -22,6 +22,9 @@ namespace { ...@@ -22,6 +22,9 @@ namespace {
const int kTimeLimitMs = 2000; const int kTimeLimitMs = 2000;
const int kTimeCheckInterval = 8192; const int kTimeCheckInterval = 8192;
void NullTask() {
}
TEST(DiscardableSharedMemoryHeapTest, SearchFreeLists) { TEST(DiscardableSharedMemoryHeapTest, SearchFreeLists) {
size_t block_size = base::GetPageSize(); size_t block_size = base::GetPageSize();
DiscardableSharedMemoryHeap heap(block_size); DiscardableSharedMemoryHeap heap(block_size);
...@@ -34,7 +37,8 @@ TEST(DiscardableSharedMemoryHeapTest, SearchFreeLists) { ...@@ -34,7 +37,8 @@ TEST(DiscardableSharedMemoryHeapTest, SearchFreeLists) {
scoped_ptr<base::DiscardableSharedMemory> memory( scoped_ptr<base::DiscardableSharedMemory> memory(
new base::DiscardableSharedMemory); new base::DiscardableSharedMemory);
ASSERT_TRUE(memory->CreateAndMap(segment_size)); ASSERT_TRUE(memory->CreateAndMap(segment_size));
heap.MergeIntoFreeLists(heap.Grow(memory.Pass(), segment_size).Pass()); heap.MergeIntoFreeLists(
heap.Grow(memory.Pass(), segment_size, base::Bind(NullTask)).Pass());
} }
unsigned kSeed = 1; unsigned kSeed = 1;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "content/common/discardable_shared_memory_heap.h" #include "content/common/discardable_shared_memory_heap.h"
#include "base/bind.h"
#include "base/memory/discardable_shared_memory.h" #include "base/memory/discardable_shared_memory.h"
#include "base/process/process_metrics.h" #include "base/process/process_metrics.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -11,6 +12,9 @@ ...@@ -11,6 +12,9 @@
namespace content { namespace content {
namespace { namespace {
void NullTask() {
}
TEST(DiscardableSharedMemoryHeapTest, Basic) { TEST(DiscardableSharedMemoryHeapTest, Basic) {
size_t block_size = base::GetPageSize(); size_t block_size = base::GetPageSize();
DiscardableSharedMemoryHeap heap(block_size); DiscardableSharedMemoryHeap heap(block_size);
...@@ -33,7 +37,7 @@ TEST(DiscardableSharedMemoryHeapTest, Basic) { ...@@ -33,7 +37,7 @@ TEST(DiscardableSharedMemoryHeapTest, Basic) {
// Create new span for memory. // Create new span for memory.
scoped_ptr<DiscardableSharedMemoryHeap::Span> new_span( scoped_ptr<DiscardableSharedMemoryHeap::Span> new_span(
heap.Grow(memory.Pass(), memory_size)); heap.Grow(memory.Pass(), memory_size, base::Bind(NullTask)));
// Size should match |memory_size|. // Size should match |memory_size|.
EXPECT_EQ(memory_size, heap.GetSize()); EXPECT_EQ(memory_size, heap.GetSize());
...@@ -76,7 +80,7 @@ TEST(DiscardableSharedMemoryHeapTest, SplitAndMerge) { ...@@ -76,7 +80,7 @@ TEST(DiscardableSharedMemoryHeapTest, SplitAndMerge) {
new base::DiscardableSharedMemory); new base::DiscardableSharedMemory);
ASSERT_TRUE(memory->CreateAndMap(memory_size)); ASSERT_TRUE(memory->CreateAndMap(memory_size));
scoped_ptr<DiscardableSharedMemoryHeap::Span> new_span( scoped_ptr<DiscardableSharedMemoryHeap::Span> new_span(
heap.Grow(memory.Pass(), memory_size)); heap.Grow(memory.Pass(), memory_size, base::Bind(NullTask)));
// Split span into two. // Split span into two.
scoped_ptr<DiscardableSharedMemoryHeap::Span> leftover = scoped_ptr<DiscardableSharedMemoryHeap::Span> leftover =
...@@ -131,7 +135,7 @@ TEST(DiscardableSharedMemoryHeapTest, MergeSingleBlockSpan) { ...@@ -131,7 +135,7 @@ TEST(DiscardableSharedMemoryHeapTest, MergeSingleBlockSpan) {
new base::DiscardableSharedMemory); new base::DiscardableSharedMemory);
ASSERT_TRUE(memory->CreateAndMap(memory_size)); ASSERT_TRUE(memory->CreateAndMap(memory_size));
scoped_ptr<DiscardableSharedMemoryHeap::Span> new_span( scoped_ptr<DiscardableSharedMemoryHeap::Span> new_span(
heap.Grow(memory.Pass(), memory_size)); heap.Grow(memory.Pass(), memory_size, base::Bind(NullTask)));
// Split span into two. // Split span into two.
scoped_ptr<DiscardableSharedMemoryHeap::Span> leftover = scoped_ptr<DiscardableSharedMemoryHeap::Span> leftover =
...@@ -152,7 +156,8 @@ TEST(DiscardableSharedMemoryHeapTest, Grow) { ...@@ -152,7 +156,8 @@ TEST(DiscardableSharedMemoryHeapTest, Grow) {
scoped_ptr<base::DiscardableSharedMemory> memory1( scoped_ptr<base::DiscardableSharedMemory> memory1(
new base::DiscardableSharedMemory); new base::DiscardableSharedMemory);
ASSERT_TRUE(memory1->CreateAndMap(block_size)); ASSERT_TRUE(memory1->CreateAndMap(block_size));
heap.MergeIntoFreeLists(heap.Grow(memory1.Pass(), block_size).Pass()); heap.MergeIntoFreeLists(
heap.Grow(memory1.Pass(), block_size, base::Bind(NullTask)).Pass());
// Remove a span from free lists. // Remove a span from free lists.
scoped_ptr<DiscardableSharedMemoryHeap::Span> span1 = scoped_ptr<DiscardableSharedMemoryHeap::Span> span1 =
...@@ -166,7 +171,8 @@ TEST(DiscardableSharedMemoryHeapTest, Grow) { ...@@ -166,7 +171,8 @@ TEST(DiscardableSharedMemoryHeapTest, Grow) {
scoped_ptr<base::DiscardableSharedMemory> memory2( scoped_ptr<base::DiscardableSharedMemory> memory2(
new base::DiscardableSharedMemory); new base::DiscardableSharedMemory);
ASSERT_TRUE(memory2->CreateAndMap(block_size)); ASSERT_TRUE(memory2->CreateAndMap(block_size));
heap.MergeIntoFreeLists(heap.Grow(memory2.Pass(), block_size).Pass()); heap.MergeIntoFreeLists(
heap.Grow(memory2.Pass(), block_size, base::Bind(NullTask)).Pass());
// Memory should now be available. // Memory should now be available.
scoped_ptr<DiscardableSharedMemoryHeap::Span> span2 = scoped_ptr<DiscardableSharedMemoryHeap::Span> span2 =
...@@ -186,7 +192,7 @@ TEST(DiscardableSharedMemoryHeapTest, ReleaseFreeMemory) { ...@@ -186,7 +192,7 @@ TEST(DiscardableSharedMemoryHeapTest, ReleaseFreeMemory) {
new base::DiscardableSharedMemory); new base::DiscardableSharedMemory);
ASSERT_TRUE(memory->CreateAndMap(block_size)); ASSERT_TRUE(memory->CreateAndMap(block_size));
scoped_ptr<DiscardableSharedMemoryHeap::Span> span = scoped_ptr<DiscardableSharedMemoryHeap::Span> span =
heap.Grow(memory.Pass(), block_size); heap.Grow(memory.Pass(), block_size, base::Bind(NullTask));
// Free lists should be empty. // Free lists should be empty.
EXPECT_EQ(0u, heap.GetSizeOfFreeLists()); EXPECT_EQ(0u, heap.GetSizeOfFreeLists());
...@@ -212,7 +218,7 @@ TEST(DiscardableSharedMemoryHeapTest, ReleasePurgedMemory) { ...@@ -212,7 +218,7 @@ TEST(DiscardableSharedMemoryHeapTest, ReleasePurgedMemory) {
new base::DiscardableSharedMemory); new base::DiscardableSharedMemory);
ASSERT_TRUE(memory->CreateAndMap(block_size)); ASSERT_TRUE(memory->CreateAndMap(block_size));
scoped_ptr<DiscardableSharedMemoryHeap::Span> span = scoped_ptr<DiscardableSharedMemoryHeap::Span> span =
heap.Grow(memory.Pass(), block_size); heap.Grow(memory.Pass(), block_size, base::Bind(NullTask));
// Unlock memory so it can be purged. // Unlock memory so it can be purged.
span->shared_memory()->Unlock(0, 0); span->shared_memory()->Unlock(0, 0);
...@@ -239,7 +245,8 @@ TEST(DiscardableSharedMemoryHeapTest, Slack) { ...@@ -239,7 +245,8 @@ TEST(DiscardableSharedMemoryHeapTest, Slack) {
scoped_ptr<base::DiscardableSharedMemory> memory( scoped_ptr<base::DiscardableSharedMemory> memory(
new base::DiscardableSharedMemory); new base::DiscardableSharedMemory);
ASSERT_TRUE(memory->CreateAndMap(memory_size)); ASSERT_TRUE(memory->CreateAndMap(memory_size));
heap.MergeIntoFreeLists(heap.Grow(memory.Pass(), memory_size).Pass()); heap.MergeIntoFreeLists(
heap.Grow(memory.Pass(), memory_size, base::Bind(NullTask)).Pass());
// No free span that is less or equal to 3 + 1. // No free span that is less or equal to 3 + 1.
EXPECT_FALSE(heap.SearchFreeLists(3, 1)); EXPECT_FALSE(heap.SearchFreeLists(3, 1));
...@@ -257,5 +264,27 @@ TEST(DiscardableSharedMemoryHeapTest, Slack) { ...@@ -257,5 +264,27 @@ TEST(DiscardableSharedMemoryHeapTest, Slack) {
heap.MergeIntoFreeLists(span.Pass()); heap.MergeIntoFreeLists(span.Pass());
} }
void OnDeleted(bool* deleted) {
*deleted = true;
}
TEST(DiscardableSharedMemoryHeapTest, DeletedCallback) {
size_t block_size = base::GetPageSize();
DiscardableSharedMemoryHeap heap(block_size);
scoped_ptr<base::DiscardableSharedMemory> memory(
new base::DiscardableSharedMemory);
ASSERT_TRUE(memory->CreateAndMap(block_size));
bool deleted = false;
scoped_ptr<DiscardableSharedMemoryHeap::Span> span =
heap.Grow(memory.Pass(), block_size,
base::Bind(OnDeleted, base::Unretained(&deleted)));
heap.MergeIntoFreeLists(span.Pass());
heap.ReleaseFreeMemory();
EXPECT_TRUE(deleted);
}
} // namespace } // namespace
} // namespace content } // namespace content
...@@ -7,10 +7,11 @@ ...@@ -7,10 +7,11 @@
#include <vector> #include <vector>
#include "base/containers/hash_tables.h"
#include "base/memory/discardable_memory_shmem_allocator.h" #include "base/memory/discardable_memory_shmem_allocator.h"
#include "base/memory/discardable_shared_memory.h" #include "base/memory/discardable_shared_memory.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/memory_pressure_listener.h" #include "base/memory/memory_pressure_listener.h"
#include "base/memory/ref_counted.h"
#include "base/memory/shared_memory.h" #include "base/memory/shared_memory.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/process/process_handle.h" #include "base/process/process_handle.h"
...@@ -18,6 +19,7 @@ ...@@ -18,6 +19,7 @@
#include "content/common/content_export.h" #include "content/common/content_export.h"
namespace content { namespace content {
typedef int32_t DiscardableSharedMemoryId;
// Implementation of DiscardableMemoryShmemAllocator that allocates and // Implementation of DiscardableMemoryShmemAllocator that allocates and
// manages discardable memory segments for the browser process and child // manages discardable memory segments for the browser process and child
...@@ -41,8 +43,14 @@ class CONTENT_EXPORT HostDiscardableSharedMemoryManager ...@@ -41,8 +43,14 @@ class CONTENT_EXPORT HostDiscardableSharedMemoryManager
void AllocateLockedDiscardableSharedMemoryForChild( void AllocateLockedDiscardableSharedMemoryForChild(
base::ProcessHandle process_handle, base::ProcessHandle process_handle,
size_t size, size_t size,
DiscardableSharedMemoryId id,
base::SharedMemoryHandle* shared_memory_handle); base::SharedMemoryHandle* shared_memory_handle);
// Call this to notify the manager that child process associated with
// |process_handle| has deleted discardable memory segment with |id|.
void ChildDeletedDiscardableSharedMemory(DiscardableSharedMemoryId id,
base::ProcessHandle process_handle);
// Call this to notify the manager that child process associated with // Call this to notify the manager that child process associated with
// |process_handle| has been removed. The manager will use this to release // |process_handle| has been removed. The manager will use this to release
// memory segments allocated for child process to the OS. // memory segments allocated for child process to the OS.
...@@ -55,30 +63,44 @@ class CONTENT_EXPORT HostDiscardableSharedMemoryManager ...@@ -55,30 +63,44 @@ class CONTENT_EXPORT HostDiscardableSharedMemoryManager
// Reduce memory usage if above current memory limit. // Reduce memory usage if above current memory limit.
void EnforceMemoryPolicy(); void EnforceMemoryPolicy();
// Returns bytes of allocated discardable memory.
size_t GetBytesAllocated();
private: private:
struct MemorySegment { class MemorySegment : public base::RefCountedThreadSafe<MemorySegment> {
MemorySegment(linked_ptr<base::DiscardableSharedMemory> memory, public:
base::ProcessHandle process_handle); MemorySegment(scoped_ptr<base::DiscardableSharedMemory> memory);
base::DiscardableSharedMemory* memory() const { return memory_.get(); }
private:
friend class base::RefCountedThreadSafe<MemorySegment>;
~MemorySegment(); ~MemorySegment();
linked_ptr<base::DiscardableSharedMemory> memory; scoped_ptr<base::DiscardableSharedMemory> memory_;
base::ProcessHandle process_handle;
DISALLOW_COPY_AND_ASSIGN(MemorySegment);
}; };
static bool CompareMemoryUsageTime(const MemorySegment& a, static bool CompareMemoryUsageTime(const scoped_refptr<MemorySegment>& a,
const MemorySegment& b) { const scoped_refptr<MemorySegment>& b) {
// In this system, LRU memory segment is evicted first. // In this system, LRU memory segment is evicted first.
return a.memory->last_known_usage() > b.memory->last_known_usage(); return a->memory()->last_known_usage() > b->memory()->last_known_usage();
} }
void AllocateLockedDiscardableSharedMemory( void AllocateLockedDiscardableSharedMemory(
base::ProcessHandle process_handle, base::ProcessHandle process_handle,
size_t size, size_t size,
DiscardableSharedMemoryId id,
base::SharedMemoryHandle* shared_memory_handle); base::SharedMemoryHandle* shared_memory_handle);
void DeletedDiscardableSharedMemory(DiscardableSharedMemoryId id,
base::ProcessHandle process_handle);
void OnMemoryPressure( void OnMemoryPressure(
base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level); base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
void ReduceMemoryUsageUntilWithinMemoryLimit(); void ReduceMemoryUsageUntilWithinMemoryLimit();
void ReduceMemoryUsageUntilWithinLimit(size_t limit); void ReduceMemoryUsageUntilWithinLimit(size_t limit);
void ReleaseMemory(base::DiscardableSharedMemory* memory);
void BytesAllocatedChanged(size_t new_bytes_allocated) const; void BytesAllocatedChanged(size_t new_bytes_allocated) const;
// Virtual for tests. // Virtual for tests.
...@@ -86,9 +108,13 @@ class CONTENT_EXPORT HostDiscardableSharedMemoryManager ...@@ -86,9 +108,13 @@ class CONTENT_EXPORT HostDiscardableSharedMemoryManager
virtual void ScheduleEnforceMemoryPolicy(); virtual void ScheduleEnforceMemoryPolicy();
base::Lock lock_; base::Lock lock_;
typedef base::hash_map<DiscardableSharedMemoryId,
scoped_refptr<MemorySegment>> MemorySegmentMap;
typedef base::hash_map<base::ProcessHandle, MemorySegmentMap> ProcessMap;
ProcessMap processes_;
// Note: The elements in |segments_| are arranged in such a way that they form // Note: The elements in |segments_| are arranged in such a way that they form
// a heap. The LRU memory segment always first. // a heap. The LRU memory segment always first.
typedef std::vector<MemorySegment> MemorySegmentVector; typedef std::vector<scoped_refptr<MemorySegment>> MemorySegmentVector;
MemorySegmentVector segments_; MemorySegmentVector segments_;
size_t memory_limit_; size_t memory_limit_;
size_t bytes_allocated_; size_t bytes_allocated_;
......
...@@ -67,7 +67,7 @@ TEST_F(HostDiscardableSharedMemoryManagerTest, AllocateForChild) { ...@@ -67,7 +67,7 @@ TEST_F(HostDiscardableSharedMemoryManagerTest, AllocateForChild) {
base::SharedMemoryHandle shared_handle; base::SharedMemoryHandle shared_handle;
manager_->AllocateLockedDiscardableSharedMemoryForChild( manager_->AllocateLockedDiscardableSharedMemoryForChild(
base::GetCurrentProcessHandle(), kDataSize, &shared_handle); base::GetCurrentProcessHandle(), kDataSize, 0, &shared_handle);
ASSERT_TRUE(base::SharedMemory::IsHandleValid(shared_handle)); ASSERT_TRUE(base::SharedMemory::IsHandleValid(shared_handle));
TestDiscardableSharedMemory memory(shared_handle); TestDiscardableSharedMemory memory(shared_handle);
...@@ -88,7 +88,7 @@ TEST_F(HostDiscardableSharedMemoryManagerTest, Purge) { ...@@ -88,7 +88,7 @@ TEST_F(HostDiscardableSharedMemoryManagerTest, Purge) {
base::SharedMemoryHandle shared_handle1; base::SharedMemoryHandle shared_handle1;
manager_->AllocateLockedDiscardableSharedMemoryForChild( manager_->AllocateLockedDiscardableSharedMemoryForChild(
base::GetCurrentProcessHandle(), kDataSize, &shared_handle1); base::GetCurrentProcessHandle(), kDataSize, 1, &shared_handle1);
ASSERT_TRUE(base::SharedMemory::IsHandleValid(shared_handle1)); ASSERT_TRUE(base::SharedMemory::IsHandleValid(shared_handle1));
TestDiscardableSharedMemory memory1(shared_handle1); TestDiscardableSharedMemory memory1(shared_handle1);
...@@ -97,7 +97,7 @@ TEST_F(HostDiscardableSharedMemoryManagerTest, Purge) { ...@@ -97,7 +97,7 @@ TEST_F(HostDiscardableSharedMemoryManagerTest, Purge) {
base::SharedMemoryHandle shared_handle2; base::SharedMemoryHandle shared_handle2;
manager_->AllocateLockedDiscardableSharedMemoryForChild( manager_->AllocateLockedDiscardableSharedMemoryForChild(
base::GetCurrentProcessHandle(), kDataSize, &shared_handle2); base::GetCurrentProcessHandle(), kDataSize, 2, &shared_handle2);
ASSERT_TRUE(base::SharedMemory::IsHandleValid(shared_handle2)); ASSERT_TRUE(base::SharedMemory::IsHandleValid(shared_handle2));
TestDiscardableSharedMemory memory2(shared_handle2); TestDiscardableSharedMemory memory2(shared_handle2);
...@@ -152,7 +152,7 @@ TEST_F(HostDiscardableSharedMemoryManagerTest, EnforceMemoryPolicy) { ...@@ -152,7 +152,7 @@ TEST_F(HostDiscardableSharedMemoryManagerTest, EnforceMemoryPolicy) {
base::SharedMemoryHandle shared_handle; base::SharedMemoryHandle shared_handle;
manager_->AllocateLockedDiscardableSharedMemoryForChild( manager_->AllocateLockedDiscardableSharedMemoryForChild(
base::GetCurrentProcessHandle(), kDataSize, &shared_handle); base::GetCurrentProcessHandle(), kDataSize, 0, &shared_handle);
ASSERT_TRUE(base::SharedMemory::IsHandleValid(shared_handle)); ASSERT_TRUE(base::SharedMemory::IsHandleValid(shared_handle));
TestDiscardableSharedMemory memory(shared_handle); TestDiscardableSharedMemory memory(shared_handle);
......
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