Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(498)

Unified Diff: base/memory/discardable_memory_provider.cc

Issue 17106004: Add discardable memory emulation for non-android/mac platforms (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressing review. Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..9be289b9c604abbf79b8516d83014ddfe60502f5
--- /dev/null
+++ b/base/memory/discardable_memory_provider.cc
@@ -0,0 +1,217 @@
+// Copyright 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/debug/trace_event.h"
+#include "base/memory/discardable_memory.h"
+#include "base/synchronization/lock.h"
+#include "base/sys_info.h"
+
+namespace base {
+namespace internal {
+
+namespace {
+
+// If this is given a valid value via SetInstanceForTest, this pointer will be
+// returned by GetInstance rather than the usual singleton.
+static 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),
+ enforcing_policy_(false),
+ memory_pressure_listener_(
+ base::Bind(&DiscardableMemoryProvider::NotifyMemoryPressure)) {
+}
+
+DiscardableMemoryProvider::~DiscardableMemoryProvider() {
+ AutoLock lock(allocations_lock_);
+ AllocationMap::iterator it = allocations_.begin();
+ for (; it != allocations_.end(); ++it)
willchan no longer on Chromium 2013/10/01 18:47:25 Mind putting braces around this? I know it works,
reveman 2013/10/09 22:40:24 Done.
+ 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(
willchan no longer on Chromium 2013/10/01 18:47:25 OK, there's a bug here. NotifyMemoryPressure will
reveman 2013/10/09 22:40:24 This is supposed to be fixed in my latest patch. P
+ MemoryPressureListener::MemoryPressureLevel pressure_level) {
+ switch (pressure_level) {
+ case MemoryPressureListener::MEMORY_PRESSURE_MODERATE:
+ DiscardableMemoryProvider::GetInstance()->PurgeLRU();
+ break;
+ case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL:
+ DiscardableMemoryProvider::GetInstance()->PurgeAll();
+ break;
+ default:
+ NOTREACHED();
reveman 2013/10/09 22:40:24 Note: I removed the default case and moved the NOT
+ }
+}
+
+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(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(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_LE(bytes, bytes_allocated_);
+ bytes_allocated_ -= bytes;
+ }
+ EnforcePolicy();
+}
+
+bool DiscardableMemoryProvider::DidAccess(DiscardableMemory* discardable) {
+ AutoLock lock(allocations_lock_);
+ // NB: |allocations_| is an MRU cache, and use of |Get| here updates that
+ // cache.
+ AllocationMap::iterator it = allocations_.Get(discardable);
+ return it != allocations_.end();
+}
+
+void DiscardableMemoryProvider::PurgeAll() {
+ TRACE_EVENT0("base", "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_;
+ }
+
+ TRACE_EVENT0("base", "DiscardableMemoryProvider::PurgeLRU");
+ 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() {
+ {
+ AutoLock lock(bytes_allocated_lock_);
+ if (enforcing_policy_)
+ return;
+ }
+
+ bool exceeded_bound = false;
+ {
+ AutoLock lock(bytes_allocated_lock_);
+ enforcing_policy_ = true;
+ if (discardable_memory_limit_ == 0) {
+ enforcing_policy_ = false;
+ return;
+ }
+ exceeded_bound = bytes_allocated_ > discardable_memory_limit_;
+ }
+
+ if (exceeded_bound)
+ PurgeLRU();
+
+ {
+ AutoLock lock(bytes_allocated_lock_);
+ enforcing_policy_ = false;
+ }
+}
+
+} // namespace internal
+} // namespace base

Powered by Google App Engine
This is Rietveld 408576698