diff --git a/base/memory/discardable_memory.h b/base/memory/discardable_memory.h index d16ed3e8480f93..5b74705234ff6c 100644 --- a/base/memory/discardable_memory.h +++ b/base/memory/discardable_memory.h @@ -64,14 +64,6 @@ class BASE_EXPORT DiscardableMemory { public: virtual ~DiscardableMemory() {} - // Call this on a thread with a MessageLoop current to allow discardable - // memory implementations to respond to memory pressure signals. - static void RegisterMemoryPressureListeners(); - - // Call this to prevent discardable memory implementations from responding - // to memory pressure signals. - static void UnregisterMemoryPressureListeners(); - // Gets the discardable memory type with a given name. static DiscardableMemoryType GetNamedType(const std::string& name); diff --git a/base/memory/discardable_memory_android.cc b/base/memory/discardable_memory_android.cc index 8988c2b8d2bd46..acf29ac85f11eb 100644 --- a/base/memory/discardable_memory_android.cc +++ b/base/memory/discardable_memory_android.cc @@ -32,10 +32,7 @@ size_t GetOptimalAshmemRegionSizeForAllocator() { // Holds the shared state used for allocations. struct SharedState { SharedState() - : manager(kAshmemMemoryLimit, - kAshmemMemoryLimit, - kAshmemMemoryLimit, - TimeDelta::Max()), + : manager(kAshmemMemoryLimit, kAshmemMemoryLimit, TimeDelta::Max()), allocator(kAshmemAllocatorName, GetOptimalAshmemRegionSizeForAllocator()) {} @@ -46,16 +43,6 @@ LazyInstance::Leaky g_shared_state = LAZY_INSTANCE_INITIALIZER; } // namespace -// static -void DiscardableMemory::RegisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners(); -} - -// static -void DiscardableMemory::UnregisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners(); -} - // static bool DiscardableMemory::ReduceMemoryUsage() { return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); diff --git a/base/memory/discardable_memory_emulated.cc b/base/memory/discardable_memory_emulated.cc index 340a181834ad3d..f9097b186e1405 100644 --- a/base/memory/discardable_memory_emulated.cc +++ b/base/memory/discardable_memory_emulated.cc @@ -13,14 +13,12 @@ namespace { // This is admittedly pretty magical. const size_t kEmulatedMemoryLimit = 512 * 1024 * 1024; const size_t kEmulatedSoftMemoryLimit = 32 * 1024 * 1024; -const size_t kEmulatedBytesToKeepUnderModeratePressure = 4 * 1024 * 1024; const size_t kEmulatedHardMemoryLimitExpirationTimeMs = 1000; struct SharedState { SharedState() : manager(kEmulatedMemoryLimit, kEmulatedSoftMemoryLimit, - kEmulatedBytesToKeepUnderModeratePressure, TimeDelta::FromMilliseconds( kEmulatedHardMemoryLimitExpirationTimeMs)) {} @@ -45,18 +43,14 @@ DiscardableMemoryEmulated::~DiscardableMemoryEmulated() { } // static -void DiscardableMemoryEmulated::RegisterMemoryPressureListeners() { - g_shared_state.Pointer()->manager.RegisterMemoryPressureListener(); -} - -// static -void DiscardableMemoryEmulated::UnregisterMemoryPressureListeners() { - g_shared_state.Pointer()->manager.UnregisterMemoryPressureListener(); +bool DiscardableMemoryEmulated::ReduceMemoryUsage() { + return g_shared_state.Pointer()->manager.ReduceMemoryUsage(); } // static -bool DiscardableMemoryEmulated::ReduceMemoryUsage() { - return g_shared_state.Pointer()->manager.ReduceMemoryUsage(); +void DiscardableMemoryEmulated::ReduceMemoryUsageUntilWithinLimit( + size_t bytes) { + g_shared_state.Pointer()->manager.ReduceMemoryUsageUntilWithinLimit(bytes); } // static diff --git a/base/memory/discardable_memory_emulated.h b/base/memory/discardable_memory_emulated.h index 64e99511b7caad..d928513527ea89 100644 --- a/base/memory/discardable_memory_emulated.h +++ b/base/memory/discardable_memory_emulated.h @@ -19,10 +19,13 @@ class DiscardableMemoryEmulated explicit DiscardableMemoryEmulated(size_t bytes); virtual ~DiscardableMemoryEmulated(); - static void RegisterMemoryPressureListeners(); - static void UnregisterMemoryPressureListeners(); static bool ReduceMemoryUsage(); + // TODO(reveman): Remove this as it is breaking the discardable memory design + // principle that implementations should not rely on information this is + // unavailable in kernel space. crbug.com/400423 + BASE_EXPORT static void ReduceMemoryUsageUntilWithinLimit(size_t bytes); + static void PurgeForTesting(); bool Initialize(); diff --git a/base/memory/discardable_memory_linux.cc b/base/memory/discardable_memory_linux.cc index b9342e92043cad..578b2c1c756049 100644 --- a/base/memory/discardable_memory_linux.cc +++ b/base/memory/discardable_memory_linux.cc @@ -10,16 +10,6 @@ namespace base { -// static -void DiscardableMemory::RegisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners(); -} - -// static -void DiscardableMemory::UnregisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners(); -} - // static bool DiscardableMemory::ReduceMemoryUsage() { return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); diff --git a/base/memory/discardable_memory_mac.cc b/base/memory/discardable_memory_mac.cc index b2184e7d589c41..fa6a23145c389b 100644 --- a/base/memory/discardable_memory_mac.cc +++ b/base/memory/discardable_memory_mac.cc @@ -25,11 +25,7 @@ namespace { const size_t kMacMemoryLimit = 512 * 1024 * 1024; struct SharedState { - SharedState() - : manager(kMacMemoryLimit, - kMacMemoryLimit, - kMacMemoryLimit, - TimeDelta::Max()) {} + SharedState() : manager(kMacMemoryLimit, kMacMemoryLimit, TimeDelta::Max()) {} internal::DiscardableMemoryManager manager; }; @@ -159,16 +155,6 @@ class DiscardableMemoryMac } // namespace -// static -void DiscardableMemory::RegisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners(); -} - -// static -void DiscardableMemory::UnregisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners(); -} - // static bool DiscardableMemory::ReduceMemoryUsage() { return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); diff --git a/base/memory/discardable_memory_manager.cc b/base/memory/discardable_memory_manager.cc index d976da203c4a88..3647b7b2f91a68 100644 --- a/base/memory/discardable_memory_manager.cc +++ b/base/memory/discardable_memory_manager.cc @@ -18,14 +18,11 @@ namespace internal { DiscardableMemoryManager::DiscardableMemoryManager( size_t memory_limit, size_t soft_memory_limit, - size_t bytes_to_keep_under_moderate_pressure, TimeDelta hard_memory_limit_expiration_time) : allocations_(AllocationMap::NO_AUTO_EVICT), bytes_allocated_(0u), memory_limit_(memory_limit), soft_memory_limit_(soft_memory_limit), - bytes_to_keep_under_moderate_pressure_( - bytes_to_keep_under_moderate_pressure), hard_memory_limit_expiration_time_(hard_memory_limit_expiration_time) { BytesAllocatedChanged(bytes_allocated_); } @@ -35,20 +32,6 @@ DiscardableMemoryManager::~DiscardableMemoryManager() { DCHECK_EQ(0u, bytes_allocated_); } -void DiscardableMemoryManager::RegisterMemoryPressureListener() { - AutoLock lock(lock_); - DCHECK(base::MessageLoop::current()); - DCHECK(!memory_pressure_listener_); - memory_pressure_listener_.reset(new MemoryPressureListener(base::Bind( - &DiscardableMemoryManager::OnMemoryPressure, Unretained(this)))); -} - -void DiscardableMemoryManager::UnregisterMemoryPressureListener() { - AutoLock lock(lock_); - DCHECK(memory_pressure_listener_); - memory_pressure_listener_.reset(); -} - void DiscardableMemoryManager::SetMemoryLimit(size_t bytes) { AutoLock lock(lock_); memory_limit_ = bytes; @@ -61,12 +44,6 @@ void DiscardableMemoryManager::SetSoftMemoryLimit(size_t bytes) { soft_memory_limit_ = bytes; } -void DiscardableMemoryManager::SetBytesToKeepUnderModeratePressure( - size_t bytes) { - AutoLock lock(lock_); - bytes_to_keep_under_moderate_pressure_ = bytes; -} - void DiscardableMemoryManager::SetHardMemoryLimitExpirationTime( TimeDelta hard_memory_limit_expiration_time) { AutoLock lock(lock_); @@ -77,13 +54,14 @@ bool DiscardableMemoryManager::ReduceMemoryUsage() { return PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit(); } +void DiscardableMemoryManager::ReduceMemoryUsageUntilWithinLimit(size_t bytes) { + AutoLock lock(lock_); + PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(), + bytes); +} + void DiscardableMemoryManager::Register(Allocation* allocation, size_t bytes) { AutoLock lock(lock_); - // A registered memory listener is currently required. This DCHECK can be - // moved or removed if we decide that it's useful to relax this condition. - // TODO(reveman): Enable this DCHECK when skia and blink are able to - // register memory pressure listeners. crbug.com/333907 - // DCHECK(memory_pressure_listener_); DCHECK(allocations_.Peek(allocation) == allocations_.end()); allocations_.Put(allocation, AllocationInfo(bytes)); } @@ -182,28 +160,6 @@ size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const { return bytes_allocated_; } -void DiscardableMemoryManager::OnMemoryPressure( - MemoryPressureListener::MemoryPressureLevel pressure_level) { - switch (pressure_level) { - case MemoryPressureListener::MEMORY_PRESSURE_MODERATE: - PurgeUntilWithinBytesToKeepUnderModeratePressure(); - return; - case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL: - PurgeAll(); - return; - } - - NOTREACHED(); -} - -void -DiscardableMemoryManager::PurgeUntilWithinBytesToKeepUnderModeratePressure() { - AutoLock lock(lock_); - - PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired( - Now(), bytes_to_keep_under_moderate_pressure_); -} - bool DiscardableMemoryManager:: PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit() { AutoLock lock(lock_); diff --git a/base/memory/discardable_memory_manager.h b/base/memory/discardable_memory_manager.h index a61f141c43034e..94b3c55108f227 100644 --- a/base/memory/discardable_memory_manager.h +++ b/base/memory/discardable_memory_manager.h @@ -8,7 +8,6 @@ #include "base/base_export.h" #include "base/containers/hash_tables.h" #include "base/containers/mru_cache.h" -#include "base/memory/memory_pressure_listener.h" #include "base/synchronization/lock.h" #include "base/time/time.h" @@ -60,27 +59,15 @@ namespace internal { // of all allocation instances (in case they need to be purged), and the total // amount of allocated memory (in case this forces a purge). When memory usage // reaches the limit, the manager purges the LRU memory. -// -// When notified of memory pressure, the manager either purges the LRU memory -- -// if the pressure is moderate -- or all discardable memory if the pressure is -// critical. class BASE_EXPORT_PRIVATE DiscardableMemoryManager { public: typedef DiscardableMemoryManagerAllocation Allocation; DiscardableMemoryManager(size_t memory_limit, size_t soft_memory_limit, - size_t bytes_to_keep_under_moderate_pressure, TimeDelta hard_memory_limit_expiration_time); virtual ~DiscardableMemoryManager(); - // Call this to register memory pressure listener. Must be called on a thread - // with a MessageLoop current. - void RegisterMemoryPressureListener(); - - // Call this to unregister memory pressure listener. - void UnregisterMemoryPressureListener(); - // The maximum number of bytes of memory that may be allocated before we force // a purge. void SetMemoryLimit(size_t bytes); @@ -89,9 +76,6 @@ class BASE_EXPORT_PRIVATE DiscardableMemoryManager { // limit expiration time without getting purged. void SetSoftMemoryLimit(size_t bytes); - // Sets the amount of memory to keep when we're under moderate pressure. - void SetBytesToKeepUnderModeratePressure(size_t bytes); - // Sets the memory usage cutoff time for hard memory limit. void SetHardMemoryLimitExpirationTime( TimeDelta hard_memory_limit_expiration_time); @@ -101,6 +85,10 @@ class BASE_EXPORT_PRIVATE DiscardableMemoryManager { // have been used. bool ReduceMemoryUsage(); + // This can be called to attempt to reduce memory footprint until within + // limit for bytes to keep under moderate pressure. + void ReduceMemoryUsageUntilWithinLimit(size_t bytes); + // Adds the given allocation to the manager's collection. void Register(Allocation* allocation, size_t bytes); @@ -141,14 +129,6 @@ class BASE_EXPORT_PRIVATE DiscardableMemoryManager { }; typedef HashingMRUCache AllocationMap; - // This can be called as a hint that the system is under memory pressure. - void OnMemoryPressure( - MemoryPressureListener::MemoryPressureLevel pressure_level); - - // Purges memory until usage is less or equal to - // |bytes_to_keep_under_moderate_pressure_|. - void PurgeUntilWithinBytesToKeepUnderModeratePressure(); - // Purges memory not used since |hard_memory_limit_expiration_time_| before // "right now" until usage is less or equal to |soft_memory_limit_|. // Returns true if total amount of memory is less or equal to soft memory @@ -185,14 +165,6 @@ class BASE_EXPORT_PRIVATE DiscardableMemoryManager { // notification. size_t soft_memory_limit_; - // Under moderate memory pressure, we will purge memory until usage is within - // this limit. - size_t bytes_to_keep_under_moderate_pressure_; - - // Allows us to be respond when the system reports that it is under memory - // pressure. - scoped_ptr memory_pressure_listener_; - // Amount of time it takes for an allocation to become affected by // |soft_memory_limit_|. TimeDelta hard_memory_limit_expiration_time_; diff --git a/base/memory/discardable_memory_manager_unittest.cc b/base/memory/discardable_memory_manager_unittest.cc index ef5739a6526a5e..674499fe0ea0bd 100644 --- a/base/memory/discardable_memory_manager_unittest.cc +++ b/base/memory/discardable_memory_manager_unittest.cc @@ -5,7 +5,6 @@ #include "base/memory/discardable_memory_manager.h" #include "base/bind.h" -#include "base/run_loop.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -46,7 +45,6 @@ class TestAllocationImpl : public internal::DiscardableMemoryManagerAllocation { // something else needs to explicit set the limit. const size_t kDefaultMemoryLimit = 1024; const size_t kDefaultSoftMemoryLimit = kDefaultMemoryLimit; -const size_t kDefaultBytesToKeepUnderModeratePressure = kDefaultMemoryLimit; class TestDiscardableMemoryManagerImpl : public internal::DiscardableMemoryManager { @@ -54,7 +52,6 @@ class TestDiscardableMemoryManagerImpl TestDiscardableMemoryManagerImpl() : DiscardableMemoryManager(kDefaultMemoryLimit, kDefaultSoftMemoryLimit, - kDefaultBytesToKeepUnderModeratePressure, TimeDelta::Max()) {} void SetNow(TimeTicks now) { now_ = now; } @@ -68,9 +65,7 @@ class TestDiscardableMemoryManagerImpl class DiscardableMemoryManagerTestBase { public: - DiscardableMemoryManagerTestBase() { - manager_.RegisterMemoryPressureListener(); - } + DiscardableMemoryManagerTestBase() {} protected: enum LockStatus { @@ -85,10 +80,6 @@ class DiscardableMemoryManagerTestBase { void SetSoftMemoryLimit(size_t bytes) { manager_.SetSoftMemoryLimit(bytes); } - void SetBytesToKeepUnderModeratePressure(size_t bytes) { - manager_.SetBytesToKeepUnderModeratePressure(bytes); - } - void SetHardMemoryLimitExpirationTime(TimeDelta time) { manager_.SetHardMemoryLimitExpirationTime(time); } @@ -127,10 +118,15 @@ class DiscardableMemoryManagerTestBase { void SetNow(TimeTicks now) { manager_.SetNow(now); } + void PurgeAll() { return manager_.PurgeAll(); } + bool ReduceMemoryUsage() { return manager_.ReduceMemoryUsage(); } + void ReduceMemoryUsageUntilWithinLimit(size_t bytes) { + manager_.ReduceMemoryUsageUntilWithinLimit(bytes); + } + private: - MessageLoopForIO message_loop_; TestDiscardableMemoryManagerImpl manager_; }; @@ -192,11 +188,7 @@ TEST_F(DiscardableMemoryManagerTest, LockAfterPurge) { EXPECT_TRUE(CanBePurged(&allocation)); // Force the system to purge. - MemoryPressureListener::NotifyMemoryPressure( - MemoryPressureListener::MEMORY_PRESSURE_CRITICAL); - - // Required because ObserverListThreadSafe notifies via PostTask. - RunLoop().RunUntilIdle(); + PurgeAll(); EXPECT_EQ(LOCK_STATUS_PURGED, Lock(&allocation)); EXPECT_FALSE(CanBePurged(&allocation)); @@ -301,17 +293,14 @@ class DiscardableMemoryManagerPermutationTest TestAllocationImpl allocation_[3]; }; -// Verify that memory was discarded in the correct order after applying -// memory pressure. -TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedModeratePressure) { +// Verify that memory was discarded in the correct order after reducing usage to +// limit. +TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscarded) { RegisterAndUseAllocations(); - SetBytesToKeepUnderModeratePressure(1024); SetMemoryLimit(2048); - MemoryPressureListener::NotifyMemoryPressure( - MemoryPressureListener::MEMORY_PRESSURE_MODERATE); - RunLoop().RunUntilIdle(); + ReduceMemoryUsageUntilWithinLimit(1024); EXPECT_NE(LOCK_STATUS_FAILED, Lock(allocation(2))); EXPECT_EQ(LOCK_STATUS_PURGED, Lock(allocation(1))); @@ -326,7 +315,6 @@ TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedModeratePressure) { TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedExceedLimit) { RegisterAndUseAllocations(); - SetBytesToKeepUnderModeratePressure(1024); SetMemoryLimit(2048); EXPECT_NE(LOCK_STATUS_FAILED, Lock(allocation(2))); @@ -340,7 +328,6 @@ TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedExceedLimit) { // Verify that no more memory than necessary was discarded after changing // memory limit. TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedAmount) { - SetBytesToKeepUnderModeratePressure(2048); SetMemoryLimit(4096); RegisterAndUseAllocations(); @@ -358,9 +345,7 @@ TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedAmount) { TEST_P(DiscardableMemoryManagerPermutationTest, PurgeFreesAllUnlocked) { RegisterAndUseAllocations(); - MemoryPressureListener::NotifyMemoryPressure( - MemoryPressureListener::MEMORY_PRESSURE_CRITICAL); - RunLoop().RunUntilIdle(); + PurgeAll(); for (int i = 0; i < 3; ++i) { if (i == 0) diff --git a/base/memory/discardable_memory_unittest.cc b/base/memory/discardable_memory_unittest.cc index dc0e2cd21262b4..516a96b5a4fa25 100644 --- a/base/memory/discardable_memory_unittest.cc +++ b/base/memory/discardable_memory_unittest.cc @@ -6,7 +6,6 @@ #include -#include "base/run_loop.h" #include "testing/gtest/include/gtest/gtest.h" #if defined(OS_ANDROID) @@ -19,12 +18,8 @@ namespace { class DiscardableMemoryTest : public testing::TestWithParam { public: - DiscardableMemoryTest() : message_loop_(MessageLoop::TYPE_IO) { - // Register memory pressure listeners now that we have a message loop. - DiscardableMemory::RegisterMemoryPressureListeners(); - } + DiscardableMemoryTest() {} virtual ~DiscardableMemoryTest() { - DiscardableMemory::UnregisterMemoryPressureListeners(); } protected: @@ -32,9 +27,6 @@ class DiscardableMemoryTest return DiscardableMemory::CreateLockedMemoryWithType( GetParam(), size).Pass(); } - - private: - MessageLoop message_loop_; }; const size_t kSize = 1024; diff --git a/base/memory/discardable_memory_win.cc b/base/memory/discardable_memory_win.cc index b9342e92043cad..578b2c1c756049 100644 --- a/base/memory/discardable_memory_win.cc +++ b/base/memory/discardable_memory_win.cc @@ -10,16 +10,6 @@ namespace base { -// static -void DiscardableMemory::RegisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners(); -} - -// static -void DiscardableMemory::UnregisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners(); -} - // static bool DiscardableMemory::ReduceMemoryUsage() { return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index 37fa0900916969..daff6bc8dad3e7 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc @@ -15,6 +15,7 @@ #include "base/lazy_instance.h" #include "base/logging.h" #include "base/memory/discardable_memory.h" +#include "base/memory/discardable_memory_emulated.h" #include "base/memory/shared_memory.h" #include "base/metrics/field_trial.h" #include "base/metrics/histogram.h" @@ -179,6 +180,9 @@ const int kMaxRasterThreads = 64; // allocation that exceeds this limit. const size_t kImageCacheSingleAllocationByteLimit = 64 * 1024 * 1024; +const size_t kEmulatedDiscardableMemoryBytesToKeepWhenWidgetsHidden = + 4 * 1024 * 1024; + // Keep the global RenderThreadImpl in a TLS slot so it is impossible to access // incorrectly from the wrong thread. base::LazyInstance > @@ -545,10 +549,6 @@ void RenderThreadImpl::Init() { base::DiscardableMemory::SetPreferredType(type); - // Allow discardable memory implementations to register memory pressure - // listeners. - base::DiscardableMemory::RegisterMemoryPressureListeners(); - // AllocateGpuMemoryBuffer must be used exclusively on one thread but // it doesn't have to be the same thread RenderThreadImpl is created on. allocate_gpu_memory_buffer_thread_checker_.DetachFromThread(); @@ -1623,6 +1623,12 @@ void RenderThreadImpl::WidgetHidden() { hidden_widget_count_++; if (widget_count_ && hidden_widget_count_ == widget_count_) { + // TODO(reveman): Remove this when we have a better mechanism to prevent + // total discardable memory used by all renderers from growing too large. + base::internal::DiscardableMemoryEmulated:: + ReduceMemoryUsageUntilWithinLimit( + kEmulatedDiscardableMemoryBytesToKeepWhenWidgetsHidden); + if (GetContentClient()->renderer()->RunIdleHandlerWhenWidgetsHidden()) ScheduleIdleHandler(kInitialIdleHandlerDelayMs); } diff --git a/content/renderer/render_thread_impl_browsertest.cc b/content/renderer/render_thread_impl_browsertest.cc index f5a0d1d7f7df75..55a4379c25505c 100644 --- a/content/renderer/render_thread_impl_browsertest.cc +++ b/content/renderer/render_thread_impl_browsertest.cc @@ -3,6 +3,8 @@ // found in the LICENSE file. #include "base/command_line.h" +#include "base/memory/discardable_memory.h" +#include "base/memory/scoped_vector.h" #include "content/public/browser/content_browser_client.h" #include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" @@ -70,5 +72,55 @@ TEST_F(RenderThreadImplBrowserTest, base::Bind(&CheckRenderThreadInputHandlerManager, thread)); } +// Checks that emulated discardable memory is discarded when the last widget +// is hidden. +TEST_F(RenderThreadImplBrowserTest, + EmulatedDiscardableMemoryDiscardedWhenWidgetsHidden) { + ContentClient content_client; + ContentBrowserClient content_browser_client; + ContentRendererClient content_renderer_client; + SetContentClient(&content_client); + SetBrowserClientForTesting(&content_browser_client); + SetRendererClientForTesting(&content_renderer_client); + base::MessageLoopForIO message_loop_; + + std::string channel_id = + IPC::Channel::GenerateVerifiedChannelID(std::string()); + DummyListener dummy_listener; + scoped_ptr channel( + IPC::Channel::CreateServer(channel_id, &dummy_listener)); + ASSERT_TRUE(channel->Connect()); + + scoped_ptr mock_process(new MockRenderProcess); + // Owned by mock_process. + RenderThreadImpl* thread = new RenderThreadImpl(channel_id); + thread->EnsureWebKitInitialized(); + thread->WidgetCreated(); + + // Allocate 128MB of discardable memory. + ScopedVector discardable_memory; + for (int i = 0; i < 32; ++i) { + discardable_memory.push_back( + base::DiscardableMemory::CreateLockedMemoryWithType( + base::DISCARDABLE_MEMORY_TYPE_EMULATED, 4 * 1024 * 1024).release()); + ASSERT_TRUE(discardable_memory.back()); + discardable_memory.back()->Unlock(); + } + + // Hide all widgets. + thread->WidgetHidden(); + + // Count how much memory is left, should be at most one block. + int blocks_left = 0; + for (auto iter = discardable_memory.begin(); iter != discardable_memory.end(); + ++iter) { + if ((*iter)->Lock() == base::DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS) + ++blocks_left; + } + EXPECT_LE(blocks_left, 1); + + thread->WidgetDestroyed(); +} + } // namespace } // namespace content