Index: base/memory/discardable_memory_provider.cc |
diff --git a/base/memory/discardable_memory_provider.cc b/base/memory/discardable_memory_provider.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c77e854828d4cc9678b06263436554cc0c1b439c |
--- /dev/null |
+++ b/base/memory/discardable_memory_provider.cc |
@@ -0,0 +1,196 @@ |
+// Copyright (c) 2013 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 "base/memory/discardable_memory_provider.h" |
+ |
+#include "base/bind.h" |
+#include "base/containers/hash_tables.h" |
+#include "base/containers/mru_cache.h" |
+#include "base/memory/discardable_memory.h" |
+#include "base/memory/singleton.h" |
+#include "base/synchronization/lock.h" |
+#include "base/sys_info.h" |
+ |
+namespace base { |
+ |
+namespace { |
+ |
+// If this is given a valid value via SetInstanceForTest, this pointer will be |
+// returned by GetInstance rather than the usual singleton. |
+static base::DiscardableMemoryProvider* s_provider_for_test = NULL; |
+ |
+// This is admittedly pretty magical. It's approximately enough memory for two |
+// 2560x1600 images. |
+static const size_t kDefaultDiscardableMemoryLimit = 32 * 1024 * 1024; |
+static const size_t kDefaultBytesToReclaimUnderModeratePressure = |
+ kDefaultDiscardableMemoryLimit / 2; |
+ |
+} // namespace |
+ |
+DiscardableMemoryProvider::DiscardableMemoryProvider() |
+ : allocations_(AllocationMap::NO_AUTO_EVICT), |
+ bytes_allocated_(0), |
+ discardable_memory_limit_(kDefaultDiscardableMemoryLimit), |
+ bytes_to_reclaim_under_moderate_pressure_( |
+ kDefaultBytesToReclaimUnderModeratePressure), |
+ memory_pressure_listener_(new MemoryPressureListener( |
+ base::Bind(&DiscardableMemoryProvider::NotifyMemoryPressure))) { |
+} |
+ |
+DiscardableMemoryProvider::~DiscardableMemoryProvider() { |
+ AutoLock lock(allocations_lock_); |
+ AllocationMap::iterator it = allocations_.begin(); |
+ for (; it != allocations_.end(); ++it) |
+ if (it->first->memory_) |
+ it->first->Deallocate(); |
+} |
+ |
+// static |
+DiscardableMemoryProvider* DiscardableMemoryProvider::GetInstance() { |
+ if (s_provider_for_test) |
+ return s_provider_for_test; |
+ return Singleton<DiscardableMemoryProvider>::get(); |
+} |
+ |
+// static |
+void DiscardableMemoryProvider::SetInstanceForTest( |
+ DiscardableMemoryProvider* provider) { |
+ s_provider_for_test = provider; |
+} |
+ |
+// static |
+void DiscardableMemoryProvider::NotifyMemoryPressure( |
+ MemoryPressureListener::MemoryPressureLevel pressureLevel) { |
+ switch (pressureLevel) { |
+ case MemoryPressureListener::MEMORY_PRESSURE_MODERATE: |
+ DiscardableMemoryProvider::GetInstance()->PurgeLRU(); |
+ break; |
+ case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL: |
+ DiscardableMemoryProvider::GetInstance()->PurgeAll(); |
+ break; |
+ default: |
+ NOTREACHED(); |
+ } |
+} |
+ |
+void DiscardableMemoryProvider::SetDiscardableMemoryLimit(size_t bytes) { |
+ { |
+ AutoLock lock(bytes_allocated_lock_); |
+ discardable_memory_limit_ = bytes; |
+ } |
+ EnforcePolicy(); |
+} |
+ |
+size_t DiscardableMemoryProvider::discardable_memory_limit() const { |
+ AutoLock lock( |
+ const_cast<DiscardableMemoryProvider*>(this)->bytes_allocated_lock_); |
+ return discardable_memory_limit_; |
+} |
+ |
+void DiscardableMemoryProvider::SetBytesToReclaimUnderModeratePressure( |
+ size_t bytes) { |
+ { |
+ AutoLock lock(bytes_allocated_lock_); |
+ bytes_to_reclaim_under_moderate_pressure_ = bytes; |
+ } |
+ EnforcePolicy(); |
+} |
+ |
+size_t DiscardableMemoryProvider:: |
+ bytes_to_reclaim_under_moderate_pressure() const { |
+ AutoLock lock( |
+ const_cast<DiscardableMemoryProvider*>(this)->bytes_allocated_lock_); |
+ return bytes_to_reclaim_under_moderate_pressure_; |
+} |
+ |
+void DiscardableMemoryProvider::Register(DiscardableMemory* discardable) { |
+ DCHECK(allocations_.Peek(discardable) == allocations_.end()); |
+ { |
+ AutoLock lock(allocations_lock_); |
+ allocations_.Put(discardable, true); |
+ } |
+ EnforcePolicy(); |
+} |
+ |
+void DiscardableMemoryProvider::Unregister(DiscardableMemory* discardable) { |
+ { |
+ AutoLock lock(allocations_lock_); |
+ AllocationMap::iterator it = allocations_.Peek(discardable); |
+ if (it != allocations_.end()) |
+ allocations_.Erase(it); |
+ } |
+ EnforcePolicy(); |
+} |
+ |
+void DiscardableMemoryProvider::DidAllocate(size_t bytes) { |
+ { |
+ AutoLock lock(bytes_allocated_lock_); |
+ bytes_allocated_ += bytes; |
+ } |
+ EnforcePolicy(); |
+} |
+ |
+void DiscardableMemoryProvider::DidDeallocate(size_t bytes) { |
+ { |
+ AutoLock lock(bytes_allocated_lock_); |
+ DCHECK(bytes <= bytes_allocated_); |
+ bytes_allocated_ -= bytes; |
+ } |
+ EnforcePolicy(); |
+} |
+ |
+bool DiscardableMemoryProvider::DidAccess(DiscardableMemory* discardable) { |
+ AutoLock lock(allocations_lock_); |
+ AllocationMap::iterator it = allocations_.Get(discardable); |
+ return it != allocations_.end(); |
+} |
+ |
+void DiscardableMemoryProvider::PurgeAll() { |
+ AutoLock lock(allocations_lock_); |
+ AllocationMap::iterator it = allocations_.begin(); |
+ for (; it != allocations_.end(); ++it) { |
+ if (it->first->memory_ && !it->first->is_locked()) { |
+ it->first->Deallocate(); |
+ DCHECK(!it->first->memory_); |
+ } |
+ } |
+} |
+ |
+void DiscardableMemoryProvider::PurgeLRU() { |
+ size_t limit = 0; |
+ { |
+ AutoLock lock(bytes_allocated_lock_); |
+ if (bytes_to_reclaim_under_moderate_pressure_ == 0) |
+ return; |
+ |
+ if (bytes_to_reclaim_under_moderate_pressure_ < discardable_memory_limit_) |
+ limit = bytes_allocated_ - bytes_to_reclaim_under_moderate_pressure_; |
+ } |
+ |
+ AutoLock lock(allocations_lock_); |
+ AllocationMap::reverse_iterator it = allocations_.rbegin(); |
+ for(; it != allocations_.rend(); ++it) { |
+ if (!it->first->memory_ || it->first->is_locked()) |
+ continue; |
+ it->first->Deallocate(); |
+ DCHECK(!it->first->memory_); |
+ AutoLock bytes_lock(bytes_allocated_lock_); |
+ if (bytes_allocated_ <= limit) |
+ break; |
+ } |
+} |
+ |
+void DiscardableMemoryProvider::EnforcePolicy() { |
+ bool exceeded_bound = false; |
+ { |
+ AutoLock lock(bytes_allocated_lock_); |
+ if (discardable_memory_limit_ == 0) |
+ return; |
+ exceeded_bound = bytes_allocated_ > discardable_memory_limit_; |
+ } |
+ if (exceeded_bound) |
+ PurgeLRU(); |
+} |
+ |
+} // namespace base |