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

Unified Diff: base/memory/discardable_memory_emulated.cc

Issue 15650016: [Not for review] Discardable memory emulation (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 7 years, 6 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_emulated.cc
diff --git a/base/memory/discardable_memory_emulated.cc b/base/memory/discardable_memory_emulated.cc
new file mode 100644
index 0000000000000000000000000000000000000000..17f56360e20f4dd92ec10417582f0b841642e3d4
--- /dev/null
+++ b/base/memory/discardable_memory_emulated.cc
@@ -0,0 +1,213 @@
+// Copyright (c) 2012 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.h"
+
+#include "base/containers/hash_tables.h"
+#include "base/containers/mru_cache.h"
+#include "base/memory/singleton.h"
+#include "base/synchronization/lock.h"
+#include "base/sys_info.h"
+
+#if defined(COMPILER_GCC)
+namespace BASE_HASH_NAMESPACE {
+template <>
+struct hash<base::DiscardableMemory*> {
+ size_t operator()(base::DiscardableMemory* ptr) const {
+ return hash<size_t>()(reinterpret_cast<size_t>(ptr));
+ }
+};
+} // namespace BASE_HASH_NAMESPACE
+#endif // COMPILER
+
+namespace base {
+
+namespace {
+
+// These are definitely magical. Is there a nice way to set them?
+static size_t kCacheLimitUpperBound = 32 * 1024 * 1024;
+static size_t kCacheLimitPurgeBound = 4 * 1024 * 1024;
+static int64 kSystemMemoryLowerBound = 256 * 1024 * 1024;
+
+static bool SystemIsUnderMemoryPressure() {
+ return SysInfo::AmountOfAvailablePhysicalMemory() <
+ kSystemMemoryLowerBound;
Avi (use Gerrit) 2013/06/12 14:56:49 Way too magical. Does this function belong in SysI
+}
+
+typedef HashingMRUCache<DiscardableMemory*, bool> AllocationMap;
Avi (use Gerrit) 2013/06/12 14:56:49 bool? If that's just a placeholder, say so.
+
+} // namespace
+
+// This provider is accessed from multiple threads, so some care needs to be
+// taken to ensure thread safety. In general, the policy has been that
+// allocations are initiated by client code (via Lock or InitializeAndLock),
+// and deallocations are initiated by the provider. Given this policy, the
+// provider assumes that the lock has already been aquired by the provider
+// itself in the Purge/Policy/DidDeallocate methods, but does not make this
+// assumption in the rest of the interface and aquires the lock as appropriate.
+class DiscardableMemoryProvider {
Avi (use Gerrit) 2013/06/12 14:56:49 Give the member functions explanatory comments. Me
Avi (use Gerrit) 2013/06/12 14:56:49 DiscardableMemoryProvider desperately needs a unit
+ public:
+ DiscardableMemoryProvider()
+ : allocations_(AllocationMap::NO_AUTO_EVICT),
+ bytes_allocated_(0) {
+ }
+
+ virtual ~DiscardableMemoryProvider() {
+ AutoLock lock(lock_);
+ AllocationMap::iterator it = allocations_.begin();
+ for (; it != allocations_.end(); ++it)
+ if (it->first->Memory())
+ it->first->Deallocate();
+ }
+
+ static DiscardableMemoryProvider* GetInstance() {
+ return Singleton<DiscardableMemoryProvider>::get();
+ }
+
+ void Register(DiscardableMemory* discardable) {
+ Unregister(discardable);
Avi (use Gerrit) 2013/06/12 14:56:49 Why the Unregister? Register is only called from D
+ {
+ AutoLock lock(lock_);
+ allocations_.Put(discardable, true);
+ EnforcePolicy();
+ }
+ }
+
+ void Unregister(DiscardableMemory* discardable) {
+ if (discardable->is_locked())
+ discardable->Unlock();
+ {
+ AutoLock lock(lock_);
+ if (discardable->memory_)
+ discardable->Deallocate();
Avi (use Gerrit) 2013/06/12 14:56:49 Eh... Unregister is only called by ~DiscardableMe
+ AllocationMap::iterator it = allocations_.Peek(discardable);
+ if (it != allocations_.end()) {
+ allocations_.Erase(it);
+ EnforcePolicy();
+ }
+ }
+ }
+
+ void DidAllocate(size_t bytes) {
+ AutoLock lock(lock_);
+ bytes_allocated_ += bytes;
+ EnforcePolicy();
+ }
+
+ void DidDeallocate(size_t bytes) {
+ lock_.AssertAcquired();
+ DCHECK(bytes <= bytes_allocated_);
+ bytes_allocated_ -= bytes;
+ EnforcePolicy();
+ }
+
+ void DidAccess(DiscardableMemory* discardable) {
+ AutoLock lock(lock_);
+ allocations_.Get(discardable);
+ }
+
+ void ForcePurge() {
+ AutoLock lock(lock_);
+ PurgeAll();
+ }
+
+ void PurgeAll() {
+ 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 PurgeLRU() {
+ 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_);
+ if (bytes_allocated_ < kCacheLimitPurgeBound)
cpu_(ooo_6.6-7.5) 2013/06/13 20:16:06 I was expecting freeing N megs, not freeing until
+ break;
+ }
+ }
+
+ void EnforcePolicy() {
+ if (SystemIsUnderMemoryPressure())
+ PurgeAll();
cpu_(ooo_6.6-7.5) 2013/06/13 20:16:06 again, surprised on the full purge.
+ else if (bytes_allocated_ > kCacheLimitUpperBound)
+ PurgeLRU();
+ }
+
+ private:
+ AllocationMap allocations_;
+ bool visible_;
+ size_t bytes_allocated_;
+ Lock lock_;
Avi (use Gerrit) 2013/06/12 14:56:49 Member variables need comment love too. In particu
+
+ DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryProvider);
+};
+
+DiscardableMemory::~DiscardableMemory() {
+ DiscardableMemoryProvider::GetInstance()->Unregister(this);
+}
+
+bool DiscardableMemory::InitializeAndLock(size_t size) {
+ DiscardableMemoryProvider::GetInstance()->Register(this);
+ size_ = size;
+ return Lock() != DISCARDABLE_MEMORY_FAILED;
Avi (use Gerrit) 2013/06/12 14:56:49 LockDiscardableMemoryStatus status = Lock(); DCHEC
+}
+
+LockDiscardableMemoryStatus DiscardableMemory::Lock() {
+ DCHECK(!is_locked_);
+ DiscardableMemoryProvider::GetInstance()->DidAccess(this);
+ is_locked_ = true;
+
+ if (memory_)
+ return DISCARDABLE_MEMORY_SUCCESS;
+
+ if (Allocate())
+ return DISCARDABLE_MEMORY_PURGED;
+
+ return DISCARDABLE_MEMORY_FAILED;
+}
+
+void DiscardableMemory::Unlock() {
+ DCHECK(is_locked_);
+ DiscardableMemoryProvider::GetInstance()->DidAccess(this);
+ is_locked_ = false;
+}
+
+bool DiscardableMemory::Allocate() {
+ DCHECK(!memory_);
+ memory_ = malloc(size_ * sizeof(char));
Avi (use Gerrit) 2013/06/12 14:56:49 sizeof(char) is defined by the standard to be 1 (C
+ if (memory_)
+ DiscardableMemoryProvider::GetInstance()->DidAllocate(size_);
+ return memory_;
+}
+
+void DiscardableMemory::Deallocate() {
+ DCHECK(memory_);
+ free(memory_);
+ memory_ = NULL;
+ DiscardableMemoryProvider::GetInstance()->DidDeallocate(size_);
+}
+
+
+// static
+bool DiscardableMemory::PurgeForTestingSupported() {
+ return true;
+}
+
+// static
+void DiscardableMemory::PurgeForTesting() {
+ Purge();
+}
+
+void DiscardableMemory::Purge() {
+ DiscardableMemoryProvider::GetInstance()->ForcePurge();
+}
+
+} // namespace base

Powered by Google App Engine
This is Rietveld 408576698