| 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..6bd529f9ff17df28e1bb9ea8d2cb54f57391c73a
|
| --- /dev/null
|
| +++ b/base/memory/discardable_memory_provider.cc
|
| @@ -0,0 +1,210 @@
|
| +// 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/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),
|
| + enforcing_policy_(false),
|
| + 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(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(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() {
|
| + {
|
| + 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 base
|
|
|