diff options
Diffstat (limited to 'base/memory')
-rw-r--r-- | base/memory/discardable_memory_emulated.cc | 19 | ||||
-rw-r--r-- | base/memory/discardable_memory_emulated.h | 13 | ||||
-rw-r--r-- | base/memory/discardable_memory_manager.cc | 148 | ||||
-rw-r--r-- | base/memory/discardable_memory_manager.h | 137 | ||||
-rw-r--r-- | base/memory/discardable_memory_manager_unittest.cc | 378 |
5 files changed, 367 insertions, 328 deletions
diff --git a/base/memory/discardable_memory_emulated.cc b/base/memory/discardable_memory_emulated.cc index 82f887f..c9effe6 100644 --- a/base/memory/discardable_memory_emulated.cc +++ b/base/memory/discardable_memory_emulated.cc @@ -8,7 +8,6 @@ #include "base/memory/discardable_memory_manager.h" namespace base { - namespace { base::LazyInstance<internal::DiscardableMemoryManager>::Leaky g_manager = @@ -52,8 +51,7 @@ DiscardableMemoryLockStatus DiscardableMemoryEmulated::Lock() { DCHECK(!is_locked_); bool purged = false; - memory_ = g_manager.Pointer()->Acquire(this, &purged); - if (!memory_) + if (!g_manager.Pointer()->AcquireLock(this, &purged)) return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; is_locked_ = true; @@ -63,14 +61,27 @@ DiscardableMemoryLockStatus DiscardableMemoryEmulated::Lock() { void DiscardableMemoryEmulated::Unlock() { DCHECK(is_locked_); - g_manager.Pointer()->Release(this, memory_.Pass()); + g_manager.Pointer()->ReleaseLock(this); is_locked_ = false; } void* DiscardableMemoryEmulated::Memory() const { + DCHECK(is_locked_); DCHECK(memory_); return memory_.get(); } +bool DiscardableMemoryEmulated::AllocateAndAcquireLock(size_t bytes) { + if (memory_) + return true; + + memory_.reset(new uint8[bytes]); + return false; +} + +void DiscardableMemoryEmulated::Purge() { + memory_.reset(); +} + } // namespace internal } // namespace base diff --git a/base/memory/discardable_memory_emulated.h b/base/memory/discardable_memory_emulated.h index 20144ff..35ce08e 100644 --- a/base/memory/discardable_memory_emulated.h +++ b/base/memory/discardable_memory_emulated.h @@ -7,10 +7,14 @@ #include "base/memory/discardable_memory.h" +#include "base/memory/discardable_memory_manager.h" + namespace base { namespace internal { -class DiscardableMemoryEmulated : public DiscardableMemory { +class DiscardableMemoryEmulated + : public DiscardableMemory, + public internal::DiscardableMemoryManagerAllocation { public: explicit DiscardableMemoryEmulated(size_t size); virtual ~DiscardableMemoryEmulated(); @@ -27,8 +31,13 @@ class DiscardableMemoryEmulated : public DiscardableMemory { virtual void Unlock() OVERRIDE; virtual void* Memory() const OVERRIDE; + // Overridden from internal::DiscardableMemoryManagerAllocation: + virtual bool AllocateAndAcquireLock(size_t bytes) OVERRIDE; + virtual void ReleaseLock() OVERRIDE {} + virtual void Purge() OVERRIDE; + private: - scoped_ptr<uint8, FreeDeleter> memory_; + scoped_ptr<uint8[]> memory_; bool is_locked_; DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryEmulated); diff --git a/base/memory/discardable_memory_manager.cc b/base/memory/discardable_memory_manager.cc index 87d3270..1dff3e4d 100644 --- a/base/memory/discardable_memory_manager.cc +++ b/base/memory/discardable_memory_manager.cc @@ -9,25 +9,23 @@ #include "base/containers/mru_cache.h" #include "base/debug/trace_event.h" #include "base/synchronization/lock.h" -#include "base/sys_info.h" namespace base { namespace internal { - namespace { // This is admittedly pretty magical. It's approximately enough memory for four // 2560x1600 images. -static const size_t kDefaultDiscardableMemoryLimit = 64 * 1024 * 1024; +static const size_t kDefaultMemoryLimit = 64 * 1024 * 1024; static const size_t kDefaultBytesToKeepUnderModeratePressure = - kDefaultDiscardableMemoryLimit / 4; + kDefaultMemoryLimit / 4; } // namespace DiscardableMemoryManager::DiscardableMemoryManager() : allocations_(AllocationMap::NO_AUTO_EVICT), bytes_allocated_(0), - discardable_memory_limit_(kDefaultDiscardableMemoryLimit), + memory_limit_(kDefaultMemoryLimit), bytes_to_keep_under_moderate_pressure_( kDefaultBytesToKeepUnderModeratePressure) { BytesAllocatedChanged(); @@ -42,10 +40,8 @@ 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)))); + memory_pressure_listener_.reset(new MemoryPressureListener(base::Bind( + &DiscardableMemoryManager::OnMemoryPressure, Unretained(this)))); } void DiscardableMemoryManager::UnregisterMemoryPressureListener() { @@ -54,9 +50,9 @@ void DiscardableMemoryManager::UnregisterMemoryPressureListener() { memory_pressure_listener_.reset(); } -void DiscardableMemoryManager::SetDiscardableMemoryLimit(size_t bytes) { +void DiscardableMemoryManager::SetMemoryLimit(size_t bytes) { AutoLock lock(lock_); - discardable_memory_limit_ = bytes; + memory_limit_ = bytes; EnforcePolicyWithLockAcquired(); } @@ -66,90 +62,77 @@ void DiscardableMemoryManager::SetBytesToKeepUnderModeratePressure( bytes_to_keep_under_moderate_pressure_ = bytes; } -void DiscardableMemoryManager::Register( - const DiscardableMemory* discardable, size_t 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(discardable) == allocations_.end()); - allocations_.Put(discardable, Allocation(bytes)); + DCHECK(allocations_.Peek(allocation) == allocations_.end()); + allocations_.Put(allocation, AllocationInfo(bytes)); } -void DiscardableMemoryManager::Unregister( - const DiscardableMemory* discardable) { +void DiscardableMemoryManager::Unregister(Allocation* allocation) { AutoLock lock(lock_); - AllocationMap::iterator it = allocations_.Peek(discardable); - if (it == allocations_.end()) - return; - - if (it->second.memory) { - size_t bytes = it->second.bytes; - DCHECK_LE(bytes, bytes_allocated_); - bytes_allocated_ -= bytes; + AllocationMap::iterator it = allocations_.Peek(allocation); + DCHECK(it != allocations_.end()); + const AllocationInfo& info = it->second; + + if (info.purgable) { + size_t bytes_purgable = info.bytes; + DCHECK_LE(bytes_purgable, bytes_allocated_); + bytes_allocated_ -= bytes_purgable; BytesAllocatedChanged(); - free(it->second.memory); } allocations_.Erase(it); } -scoped_ptr<uint8, FreeDeleter> DiscardableMemoryManager::Acquire( - const DiscardableMemory* discardable, - bool* purged) { +bool DiscardableMemoryManager::AcquireLock(Allocation* allocation, + bool* purged) { AutoLock lock(lock_); - // NB: |allocations_| is an MRU cache, and use of |Get| here updates that + // Note: |allocations_| is an MRU cache, and use of |Get| here updates that // cache. - AllocationMap::iterator it = allocations_.Get(discardable); - CHECK(it != allocations_.end()); - - if (it->second.memory) { - scoped_ptr<uint8, FreeDeleter> memory(it->second.memory); - it->second.memory = NULL; - *purged = false; - return memory.Pass(); - } + AllocationMap::iterator it = allocations_.Get(allocation); + DCHECK(it != allocations_.end()); + AllocationInfo* info = &it->second; + + if (!info->bytes) + return false; - size_t bytes = it->second.bytes; - if (!bytes) - return scoped_ptr<uint8, FreeDeleter>(); + size_t bytes_required = info->purgable ? 0u : info->bytes; - if (discardable_memory_limit_) { + if (memory_limit_) { size_t limit = 0; - if (bytes < discardable_memory_limit_) - limit = discardable_memory_limit_ - bytes; + if (bytes_required < memory_limit_) + limit = memory_limit_ - bytes_required; PurgeLRUWithLockAcquiredUntilUsageIsWithin(limit); } // Check for overflow. - if (std::numeric_limits<size_t>::max() - bytes < bytes_allocated_) - return scoped_ptr<uint8, FreeDeleter>(); - - scoped_ptr<uint8, FreeDeleter> memory(static_cast<uint8*>(malloc(bytes))); - if (!memory) - return scoped_ptr<uint8, FreeDeleter>(); - - bytes_allocated_ += bytes; - BytesAllocatedChanged(); + if (std::numeric_limits<size_t>::max() - bytes_required < bytes_allocated_) + return false; - *purged = true; - return memory.Pass(); + *purged = !allocation->AllocateAndAcquireLock(info->bytes); + info->purgable = false; + if (bytes_required) { + bytes_allocated_ += bytes_required; + BytesAllocatedChanged(); + } + return true; } -void DiscardableMemoryManager::Release( - const DiscardableMemory* discardable, - scoped_ptr<uint8, FreeDeleter> memory) { +void DiscardableMemoryManager::ReleaseLock(Allocation* allocation) { AutoLock lock(lock_); - // NB: |allocations_| is an MRU cache, and use of |Get| here updates that + // Note: |allocations_| is an MRU cache, and use of |Get| here updates that // cache. - AllocationMap::iterator it = allocations_.Get(discardable); - CHECK(it != allocations_.end()); - - DCHECK(!it->second.memory); - it->second.memory = memory.release(); + AllocationMap::iterator it = allocations_.Get(allocation); + DCHECK(it != allocations_.end()); + AllocationInfo* info = &it->second; + allocation->ReleaseLock(); + info->purgable = true; EnforcePolicyWithLockAcquired(); } @@ -159,17 +142,17 @@ void DiscardableMemoryManager::PurgeAll() { } bool DiscardableMemoryManager::IsRegisteredForTest( - const DiscardableMemory* discardable) const { + Allocation* allocation) const { AutoLock lock(lock_); - AllocationMap::const_iterator it = allocations_.Peek(discardable); + AllocationMap::const_iterator it = allocations_.Peek(allocation); return it != allocations_.end(); } bool DiscardableMemoryManager::CanBePurgedForTest( - const DiscardableMemory* discardable) const { + Allocation* allocation) const { AutoLock lock(lock_); - AllocationMap::const_iterator it = allocations_.Peek(discardable); - return it != allocations_.end() && it->second.memory; + AllocationMap::const_iterator it = allocations_.Peek(allocation); + return it != allocations_.end() && it->second.purgable; } size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const { @@ -203,7 +186,8 @@ void DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin( TRACE_EVENT1( "base", "DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin", - "limit", limit); + "limit", + limit); lock_.AssertAcquired(); @@ -211,16 +195,19 @@ void DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin( for (AllocationMap::reverse_iterator it = allocations_.rbegin(); it != allocations_.rend(); ++it) { + Allocation* allocation = it->first; + AllocationInfo* info = &it->second; + if (bytes_allocated_ <= limit) break; - if (!it->second.memory) + if (!info->purgable) continue; - size_t bytes = it->second.bytes; - DCHECK_LE(bytes, bytes_allocated_); - bytes_allocated_ -= bytes; - free(it->second.memory); - it->second.memory = NULL; + size_t bytes_purgable = info->bytes; + DCHECK_LE(bytes_purgable, bytes_allocated_); + bytes_allocated_ -= bytes_purgable; + info->purgable = false; + allocation->Purge(); } if (bytes_allocated_ != bytes_allocated_before_purging) @@ -228,14 +215,11 @@ void DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin( } void DiscardableMemoryManager::EnforcePolicyWithLockAcquired() { - PurgeLRUWithLockAcquiredUntilUsageIsWithin(discardable_memory_limit_); + PurgeLRUWithLockAcquiredUntilUsageIsWithin(memory_limit_); } void DiscardableMemoryManager::BytesAllocatedChanged() const { - TRACE_COUNTER_ID1("base", - "DiscardableMemoryUsage", - this, - bytes_allocated_); + TRACE_COUNTER_ID1("base", "DiscardableMemoryUsage", this, bytes_allocated_); } } // namespace internal diff --git a/base/memory/discardable_memory_manager.h b/base/memory/discardable_memory_manager.h index 7c0bd5c..8447521 100644 --- a/base/memory/discardable_memory_manager.h +++ b/base/memory/discardable_memory_manager.h @@ -12,14 +12,38 @@ #include "base/synchronization/lock.h" namespace base { -class DiscardableMemory; +namespace internal { + +// This interface is used by the DiscardableMemoryManager class to provide some +// level of userspace control over discardable memory allocations. +class DiscardableMemoryManagerAllocation { + public: + // Allocate and acquire a lock that prevents the allocation from being purged + // by the system. Returns true if memory was previously allocated and is still + // resident. + virtual bool AllocateAndAcquireLock(size_t bytes) = 0; + + // Release a previously acquired lock on the allocation so that it can be + // purged by the system. + virtual void ReleaseLock() = 0; + + // Explicitly purge this allocation. It is illegal to call this while a lock + // is acquired on the allocation. + virtual void Purge() = 0; + + protected: + virtual ~DiscardableMemoryManagerAllocation() {} +}; + +} // namespace internal } // namespace base #if defined(COMPILER_GCC) namespace BASE_HASH_NAMESPACE { template <> -struct hash<const base::DiscardableMemory*> { - size_t operator()(const base::DiscardableMemory* ptr) const { +struct hash<base::internal::DiscardableMemoryManagerAllocation*> { + size_t operator()( + base::internal::DiscardableMemoryManagerAllocation* ptr) const { return hash<size_t>()(reinterpret_cast<size_t>(ptr)); } }; @@ -29,87 +53,82 @@ struct hash<const base::DiscardableMemory*> { namespace base { namespace internal { -// The DiscardableMemoryManager manages a collection of emulated -// DiscardableMemory instances. It is used on platforms that do not support -// discardable memory natively. It keeps track of all DiscardableMemory -// instances (in case they need to be purged), and the total amount of -// allocated memory (in case this forces a purge). -// -// 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. +// The DiscardableMemoryManager manages a collection of +// DiscardableMemoryManagerAllocation instances. It is used on platforms that +// need some level of userspace control over discardable memory. It keeps track +// 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. // -// NB - this class is an implementation detail. It has been exposed for testing -// purposes. You should not need to use this class directly. +// 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(); ~DiscardableMemoryManager(); - // Call this to register memory pressure listener. Must be called on a - // thread with a MessageLoop current. + // 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 discardable memory that may be allocated - // before we force a purge. If this amount is zero, it is interpreted as - // having no limit at all. - void SetDiscardableMemoryLimit(size_t bytes); + // The maximum number of bytes of memory that may be allocated before we force + // a purge. If this amount is zero, it is interpreted as having no limit at + // all. + void SetMemoryLimit(size_t bytes); // Sets the amount of memory to keep when we're under moderate pressure. void SetBytesToKeepUnderModeratePressure(size_t bytes); - // Adds the given discardable memory to the manager's collection. - void Register(const DiscardableMemory* discardable, size_t bytes); + // Adds the given allocation to the manager's collection. + void Register(Allocation* allocation, size_t bytes); - // Removes the given discardable memory from the manager's collection. - void Unregister(const DiscardableMemory* discardable); + // Removes the given allocation from the manager's collection. + void Unregister(Allocation* allocation); - // Returns NULL if an error occurred. Otherwise, returns the backing buffer - // and sets |purged| to indicate whether or not the backing buffer has been - // purged since last use. - scoped_ptr<uint8, FreeDeleter> Acquire( - const DiscardableMemory* discardable, bool* purged); + // Returns false if an error occurred. Otherwise, returns true and sets + // |purged| to indicate whether or not allocation has been purged since last + // use. + bool AcquireLock(Allocation* allocation, bool* purged); - // Release a previously acquired backing buffer. This gives the buffer back - // to the manager where it can be purged if necessary. - void Release(const DiscardableMemory* discardable, - scoped_ptr<uint8, FreeDeleter> memory); + // Release a previously acquired lock on allocation. This allows the manager + // to purge it if necessary. + void ReleaseLock(Allocation* allocation); // Purges all discardable memory. void PurgeAll(); - // Returns true if discardable memory has been added to the manager's - // collection. This should only be used by tests. - bool IsRegisteredForTest(const DiscardableMemory* discardable) const; + // Returns true if allocation has been added to the manager's collection. This + // should only be used by tests. + bool IsRegisteredForTest(Allocation* allocation) const; - // Returns true if discardable memory can be purged. This should only - // be used by tests. - bool CanBePurgedForTest(const DiscardableMemory* discardable) const; + // Returns true if allocation can be purged. This should only be used by + // tests. + bool CanBePurgedForTest(Allocation* allocation) const; - // Returns total amount of allocated discardable memory. This should only - // be used by tests. + // Returns total amount of allocated discardable memory. This should only be + // used by tests. size_t GetBytesAllocatedForTest() const; private: - struct Allocation { - explicit Allocation(size_t bytes) - : bytes(bytes), - memory(NULL) { - } - - size_t bytes; - uint8* memory; + struct AllocationInfo { + explicit AllocationInfo(size_t bytes) : bytes(bytes), purgable(false) {} + + const size_t bytes; + bool purgable; }; - typedef HashingMRUCache<const DiscardableMemory*, Allocation> AllocationMap; + typedef HashingMRUCache<Allocation*, AllocationInfo> AllocationMap; // This can be called as a hint that the system is under memory pressure. void OnMemoryPressure( MemoryPressureListener::MemoryPressureLevel pressure_level); - // Purges until discardable memory usage is within + // Purges memory until usage is within // |bytes_to_keep_under_moderate_pressure_|. void Purge(); @@ -117,8 +136,8 @@ class BASE_EXPORT_PRIVATE DiscardableMemoryManager { // Caller must acquire |lock_| prior to calling this function. void PurgeLRUWithLockAcquiredUntilUsageIsWithin(size_t limit); - // Ensures that we don't allocate beyond our memory limit. - // Caller must acquire |lock_| prior to calling this function. + // Ensures that we don't allocate beyond our memory limit. Caller must acquire + // |lock_| prior to calling this function. void EnforcePolicyWithLockAcquired(); // Called when a change to |bytes_allocated_| has been made. @@ -127,17 +146,17 @@ class BASE_EXPORT_PRIVATE DiscardableMemoryManager { // Needs to be held when accessing members. mutable Lock lock_; - // A MRU cache of all allocated bits of discardable memory. Used for purging. + // A MRU cache of all allocated bits of memory. Used for purging. AllocationMap allocations_; - // The total amount of allocated discardable memory. + // The total amount of allocated memory. size_t bytes_allocated_; - // The maximum number of bytes of discardable memory that may be allocated. - size_t discardable_memory_limit_; + // The maximum number of bytes of memory that may be allocated. + size_t memory_limit_; - // Under moderate memory pressure, we will purge until usage is within this - // 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 diff --git a/base/memory/discardable_memory_manager_unittest.cc b/base/memory/discardable_memory_manager_unittest.cc index 95e7c13..58a9603 100644 --- a/base/memory/discardable_memory_manager_unittest.cc +++ b/base/memory/discardable_memory_manager_unittest.cc @@ -5,156 +5,157 @@ #include "base/memory/discardable_memory_manager.h" #include "base/bind.h" -#include "base/memory/discardable_memory.h" #include "base/run_loop.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { +namespace { -class DiscardableMemoryManagerTestBase { +class TestAllocationImpl : public internal::DiscardableMemoryManagerAllocation { public: - class TestDiscardableMemory : public DiscardableMemory { - public: - TestDiscardableMemory( - internal::DiscardableMemoryManager* manager, size_t size) - : manager_(manager), - is_locked_(false) { - manager_->Register(this, size); - } + TestAllocationImpl() : is_allocated_(false), is_locked_(false) {} + virtual ~TestAllocationImpl() { DCHECK(!is_locked_); } + + // Overridden from internal::DiscardableMemoryManagerAllocation: + virtual bool AllocateAndAcquireLock(size_t bytes) OVERRIDE { + bool was_allocated = is_allocated_; + is_allocated_ = true; + DCHECK(!is_locked_); + is_locked_ = true; + return was_allocated; + } + virtual void ReleaseLock() OVERRIDE { + DCHECK(is_locked_); + is_locked_ = false; + } + virtual void Purge() OVERRIDE { + DCHECK(is_allocated_); + is_allocated_ = false; + } - virtual ~TestDiscardableMemory() { - if (is_locked_) - Unlock(); - manager_->Unregister(this); - } + bool is_locked() const { return is_locked_; } - // Overridden from DiscardableMemory: - virtual DiscardableMemoryLockStatus Lock() OVERRIDE { - DCHECK(!is_locked_); + private: + bool is_allocated_; + bool is_locked_; +}; - bool purged = false; - memory_ = manager_->Acquire(this, &purged); - if (!memory_) - return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; +class DiscardableMemoryManagerTestBase { + public: + DiscardableMemoryManagerTestBase() { + manager_.RegisterMemoryPressureListener(); + } - is_locked_ = true; - return purged ? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED - : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS; - } - virtual void Unlock() OVERRIDE { - DCHECK(is_locked_); - manager_->Release(this, memory_.Pass()); - is_locked_ = false; - } - virtual void* Memory() const OVERRIDE { - DCHECK(memory_); - return memory_.get(); - } + protected: + enum LockStatus { + LOCK_STATUS_FAILED, + LOCK_STATUS_PURGED, + LOCK_STATUS_SUCCESS + }; - private: - internal::DiscardableMemoryManager* manager_; - scoped_ptr<uint8, FreeDeleter> memory_; - bool is_locked_; + size_t BytesAllocated() const { return manager_.GetBytesAllocatedForTest(); } - DISALLOW_COPY_AND_ASSIGN(TestDiscardableMemory); - }; + void SetMemoryLimit(size_t bytes) { manager_.SetMemoryLimit(bytes); } - DiscardableMemoryManagerTestBase() - : manager_(new internal::DiscardableMemoryManager) { - manager_->RegisterMemoryPressureListener(); + void SetBytesToKeepUnderModeratePressure(size_t bytes) { + manager_.SetBytesToKeepUnderModeratePressure(bytes); } - protected: - bool IsRegistered(const DiscardableMemory* discardable) { - return manager_->IsRegisteredForTest(discardable); + void Register(TestAllocationImpl* allocation, size_t bytes) { + manager_.Register(allocation, bytes); } - bool CanBePurged(const DiscardableMemory* discardable) { - return manager_->CanBePurgedForTest(discardable); + void Unregister(TestAllocationImpl* allocation) { + manager_.Unregister(allocation); } - size_t BytesAllocated() const { - return manager_->GetBytesAllocatedForTest(); + bool IsRegistered(TestAllocationImpl* allocation) const { + return manager_.IsRegisteredForTest(allocation); } - void* Memory(const DiscardableMemory* discardable) const { - return discardable->Memory(); + LockStatus Lock(TestAllocationImpl* allocation) { + bool purged; + if (!manager_.AcquireLock(allocation, &purged)) + return LOCK_STATUS_FAILED; + return purged ? LOCK_STATUS_PURGED : LOCK_STATUS_SUCCESS; } - void SetDiscardableMemoryLimit(size_t bytes) { - manager_->SetDiscardableMemoryLimit(bytes); + void Unlock(TestAllocationImpl* allocation) { + manager_.ReleaseLock(allocation); } - void SetBytesToKeepUnderModeratePressure(size_t bytes) { - manager_->SetBytesToKeepUnderModeratePressure(bytes); + LockStatus RegisterAndLock(TestAllocationImpl* allocation, size_t bytes) { + manager_.Register(allocation, bytes); + return Lock(allocation); } - scoped_ptr<DiscardableMemory> CreateLockedMemory(size_t size) { - scoped_ptr<TestDiscardableMemory> memory( - new TestDiscardableMemory(manager_.get(), size)); - if (memory->Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_PURGED) - return scoped_ptr<DiscardableMemory>(); - return memory.PassAs<DiscardableMemory>(); + bool CanBePurged(TestAllocationImpl* allocation) const { + return manager_.CanBePurgedForTest(allocation); } private: MessageLoopForIO message_loop_; - scoped_ptr<internal::DiscardableMemoryManager> manager_; + internal::DiscardableMemoryManager manager_; }; -class DiscardableMemoryManagerTest - : public DiscardableMemoryManagerTestBase, - public testing::Test { +class DiscardableMemoryManagerTest : public DiscardableMemoryManagerTestBase, + public testing::Test { public: DiscardableMemoryManagerTest() {} }; -TEST_F(DiscardableMemoryManagerTest, CreateLockedMemory) { +TEST_F(DiscardableMemoryManagerTest, CreateAndLock) { size_t size = 1024; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_TRUE(IsRegistered(discardable.get())); - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get())); + TestAllocationImpl allocation; + Register(&allocation, size); + EXPECT_TRUE(IsRegistered(&allocation)); + EXPECT_EQ(LOCK_STATUS_PURGED, Lock(&allocation)); + EXPECT_TRUE(allocation.is_locked()); EXPECT_EQ(1024u, BytesAllocated()); - EXPECT_FALSE(CanBePurged(discardable.get())); + EXPECT_FALSE(CanBePurged(&allocation)); + Unlock(&allocation); + Unregister(&allocation); } -TEST_F(DiscardableMemoryManagerTest, CreateLockedMemoryZeroSize) { +TEST_F(DiscardableMemoryManagerTest, CreateZeroSize) { size_t size = 0; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_FALSE(discardable); - EXPECT_FALSE(IsRegistered(discardable.get())); + TestAllocationImpl allocation; + Register(&allocation, size); + EXPECT_TRUE(IsRegistered(&allocation)); + EXPECT_EQ(LOCK_STATUS_FAILED, Lock(&allocation)); EXPECT_EQ(0u, BytesAllocated()); + Unregister(&allocation); } TEST_F(DiscardableMemoryManagerTest, LockAfterUnlock) { size_t size = 1024; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_TRUE(IsRegistered(discardable.get())); - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get())); + TestAllocationImpl allocation; + RegisterAndLock(&allocation, size); EXPECT_EQ(1024u, BytesAllocated()); - EXPECT_FALSE(CanBePurged(discardable.get())); + EXPECT_FALSE(CanBePurged(&allocation)); // Now unlock so we can lock later. - discardable->Unlock(); - EXPECT_TRUE(CanBePurged(discardable.get())); + Unlock(&allocation); + EXPECT_TRUE(CanBePurged(&allocation)); - EXPECT_EQ(DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS, discardable->Lock()); - EXPECT_FALSE(CanBePurged(discardable.get())); + EXPECT_EQ(LOCK_STATUS_SUCCESS, Lock(&allocation)); + EXPECT_FALSE(CanBePurged(&allocation)); + Unlock(&allocation); + Unregister(&allocation); } TEST_F(DiscardableMemoryManagerTest, LockAfterPurge) { size_t size = 1024; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_TRUE(IsRegistered(discardable.get())); - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get())); + TestAllocationImpl allocation; + RegisterAndLock(&allocation, size); EXPECT_EQ(1024u, BytesAllocated()); - EXPECT_FALSE(CanBePurged(discardable.get())); + EXPECT_FALSE(CanBePurged(&allocation)); // Now unlock so we can lock later. - discardable->Unlock(); - EXPECT_TRUE(CanBePurged(discardable.get())); + Unlock(&allocation); + EXPECT_TRUE(CanBePurged(&allocation)); // Force the system to purge. MemoryPressureListener::NotifyMemoryPressure( @@ -163,43 +164,53 @@ TEST_F(DiscardableMemoryManagerTest, LockAfterPurge) { // Required because ObserverListThreadSafe notifies via PostTask. RunLoop().RunUntilIdle(); - EXPECT_EQ(DISCARDABLE_MEMORY_LOCK_STATUS_PURGED, discardable->Lock()); - EXPECT_FALSE(CanBePurged(discardable.get())); + EXPECT_EQ(LOCK_STATUS_PURGED, Lock(&allocation)); + EXPECT_FALSE(CanBePurged(&allocation)); + + Unlock(&allocation); + Unregister(&allocation); } TEST_F(DiscardableMemoryManagerTest, LockAfterPurgeAndCannotReallocate) { size_t size = 1024; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_TRUE(IsRegistered(discardable.get())); - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get())); + TestAllocationImpl allocation; + RegisterAndLock(&allocation, size); EXPECT_EQ(1024u, BytesAllocated()); - EXPECT_FALSE(CanBePurged(discardable.get())); + EXPECT_FALSE(CanBePurged(&allocation)); // Now unlock so we can lock later. - discardable->Unlock(); - EXPECT_TRUE(CanBePurged(discardable.get())); + Unlock(&allocation); + EXPECT_TRUE(CanBePurged(&allocation)); + + // Set max allowed allocation to 1 byte. This will cause the memory to be + // purged. + SetMemoryLimit(1); - // Set max allowed allocation to 1 byte. This will make cause the memory - // to be purged. - SetDiscardableMemoryLimit(1); + EXPECT_EQ(LOCK_STATUS_PURGED, Lock(&allocation)); + EXPECT_FALSE(CanBePurged(&allocation)); - EXPECT_EQ(DISCARDABLE_MEMORY_LOCK_STATUS_PURGED, discardable->Lock()); - EXPECT_FALSE(CanBePurged(discardable.get())); + Unlock(&allocation); + Unregister(&allocation); } TEST_F(DiscardableMemoryManagerTest, Overflow) { + size_t size = 1024; { - size_t size = 1024; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_TRUE(IsRegistered(discardable.get())); - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get())); + TestAllocationImpl allocation; + RegisterAndLock(&allocation, size); EXPECT_EQ(1024u, BytesAllocated()); size_t massive_size = std::numeric_limits<size_t>::max(); - const scoped_ptr<DiscardableMemory> massive_discardable( - CreateLockedMemory(massive_size)); - EXPECT_FALSE(massive_discardable); + TestAllocationImpl massive_allocation; + Register(&massive_allocation, massive_size); + EXPECT_EQ(LOCK_STATUS_FAILED, Lock(&massive_allocation)); EXPECT_EQ(1024u, BytesAllocated()); + + Unlock(&allocation); + EXPECT_EQ(LOCK_STATUS_PURGED, Lock(&massive_allocation)); + Unlock(&massive_allocation); + Unregister(&massive_allocation); + Unregister(&allocation); } EXPECT_EQ(0u, BytesAllocated()); } @@ -225,83 +236,93 @@ class DiscardableMemoryManagerPermutationTest DiscardableMemoryManagerPermutationTest() {} protected: - // Use discardable memory in order specified by ordering parameter. - void CreateAndUseDiscardableMemory() { + // Use memory in order specified by ordering parameter. + void RegisterAndUseAllocations() { for (int i = 0; i < 3; ++i) { - discardables_[i] = CreateLockedMemory(1024); - EXPECT_TRUE(discardables_[i]); - EXPECT_NE(static_cast<void*>(NULL), Memory(discardables_[i].get())); - discardables_[i]->Unlock(); + RegisterAndLock(&allocation_[i], 1024); + Unlock(&allocation_[i]); } for (int i = 0; i < 3; ++i) { int index = GetParam().ordering()[i]; - EXPECT_NE(DISCARDABLE_MEMORY_LOCK_STATUS_FAILED, - discardables_[index]->Lock()); + EXPECT_NE(LOCK_STATUS_FAILED, Lock(&allocation_[index])); // Leave i == 0 locked. if (i > 0) - discardables_[index]->Unlock(); + Unlock(&allocation_[index]); } } - DiscardableMemory* discardable(unsigned position) { - return discardables_[GetParam().ordering()[position]].get(); + TestAllocationImpl* allocation(unsigned position) { + return &allocation_[GetParam().ordering()[position]]; + } + + void UnlockAndUnregisterAllocations() { + for (int i = 0; i < 3; ++i) { + if (allocation_[i].is_locked()) + Unlock(&allocation_[i]); + Unregister(&allocation_[i]); + } } private: - scoped_ptr<DiscardableMemory> discardables_[3]; + TestAllocationImpl allocation_[3]; }; // Verify that memory was discarded in the correct order after applying // memory pressure. TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedModeratePressure) { - CreateAndUseDiscardableMemory(); + RegisterAndUseAllocations(); SetBytesToKeepUnderModeratePressure(1024); - SetDiscardableMemoryLimit(2048); + SetMemoryLimit(2048); MemoryPressureListener::NotifyMemoryPressure( MemoryPressureListener::MEMORY_PRESSURE_MODERATE); RunLoop().RunUntilIdle(); - EXPECT_NE(DISCARDABLE_MEMORY_LOCK_STATUS_FAILED, discardable(2)->Lock()); - EXPECT_NE(DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS, discardable(1)->Lock()); + EXPECT_NE(LOCK_STATUS_FAILED, Lock(allocation(2))); + EXPECT_EQ(LOCK_STATUS_PURGED, Lock(allocation(1))); // 0 should still be locked. - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable(0))); + EXPECT_TRUE(allocation(0)->is_locked()); + + UnlockAndUnregisterAllocations(); } // Verify that memory was discarded in the correct order after changing // memory limit. TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedExceedLimit) { - CreateAndUseDiscardableMemory(); + RegisterAndUseAllocations(); SetBytesToKeepUnderModeratePressure(1024); - SetDiscardableMemoryLimit(2048); + SetMemoryLimit(2048); - EXPECT_NE(DISCARDABLE_MEMORY_LOCK_STATUS_FAILED, discardable(2)->Lock()); - EXPECT_NE(DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS, discardable(1)->Lock()); + EXPECT_NE(LOCK_STATUS_FAILED, Lock(allocation(2))); + EXPECT_EQ(LOCK_STATUS_PURGED, Lock(allocation(1))); // 0 should still be locked. - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable(0))); + EXPECT_TRUE(allocation(0)->is_locked()); + + UnlockAndUnregisterAllocations(); } // Verify that no more memory than necessary was discarded after changing // memory limit. TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedAmount) { SetBytesToKeepUnderModeratePressure(2048); - SetDiscardableMemoryLimit(4096); + SetMemoryLimit(4096); - CreateAndUseDiscardableMemory(); + RegisterAndUseAllocations(); - SetDiscardableMemoryLimit(2048); + SetMemoryLimit(2048); - EXPECT_EQ(DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS, discardable(2)->Lock()); - EXPECT_EQ(DISCARDABLE_MEMORY_LOCK_STATUS_PURGED, discardable(1)->Lock()); + EXPECT_EQ(LOCK_STATUS_SUCCESS, Lock(allocation(2))); + EXPECT_EQ(LOCK_STATUS_PURGED, Lock(allocation(1))); // 0 should still be locked. - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable(0))); + EXPECT_TRUE(allocation(0)->is_locked()); + + UnlockAndUnregisterAllocations(); } -TEST_P(DiscardableMemoryManagerPermutationTest, - CriticalPressureFreesAllUnlocked) { - CreateAndUseDiscardableMemory(); +TEST_P(DiscardableMemoryManagerPermutationTest, PurgeFreesAllUnlocked) { + RegisterAndUseAllocations(); MemoryPressureListener::NotifyMemoryPressure( MemoryPressureListener::MEMORY_PRESSURE_CRITICAL); @@ -309,10 +330,12 @@ TEST_P(DiscardableMemoryManagerPermutationTest, for (int i = 0; i < 3; ++i) { if (i == 0) - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable(i))); + EXPECT_TRUE(allocation(i)->is_locked()); else - EXPECT_EQ(DISCARDABLE_MEMORY_LOCK_STATUS_PURGED, discardable(i)->Lock()); + EXPECT_EQ(LOCK_STATUS_PURGED, Lock(allocation(i))); } + + UnlockAndUnregisterAllocations(); } INSTANTIATE_TEST_CASE_P(DiscardableMemoryManagerPermutationTests, @@ -327,69 +350,61 @@ INSTANTIATE_TEST_CASE_P(DiscardableMemoryManagerPermutationTests, TEST_F(DiscardableMemoryManagerTest, NormalDestruction) { { size_t size = 1024; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_TRUE(IsRegistered(discardable.get())); - EXPECT_EQ(1024u, BytesAllocated()); + TestAllocationImpl allocation; + Register(&allocation, size); + Unregister(&allocation); } EXPECT_EQ(0u, BytesAllocated()); } -TEST_F(DiscardableMemoryManagerTest, DestructionWhileLocked) { +TEST_F(DiscardableMemoryManagerTest, DestructionAfterLocked) { { size_t size = 1024; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_TRUE(IsRegistered(discardable.get())); - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get())); + TestAllocationImpl allocation; + RegisterAndLock(&allocation, size); EXPECT_EQ(1024u, BytesAllocated()); - EXPECT_FALSE(CanBePurged(discardable.get())); + EXPECT_FALSE(CanBePurged(&allocation)); + Unlock(&allocation); + Unregister(&allocation); } - // Should have ignored the "locked" status and freed the discardable memory. EXPECT_EQ(0u, BytesAllocated()); } -#if !defined(NDEBUG) && !defined(OS_ANDROID) && !defined(OS_IOS) -// Death tests are not supported with Android APKs. -TEST_F(DiscardableMemoryManagerTest, UnlockedMemoryAccessCrashesInDebugMode) { - size_t size = 1024; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_TRUE(IsRegistered(discardable.get())); - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get())); - EXPECT_EQ(1024u, BytesAllocated()); - EXPECT_FALSE(CanBePurged(discardable.get())); - discardable->Unlock(); - EXPECT_TRUE(CanBePurged(discardable.get())); - // We *must* die if we are asked to vend a pointer to unlocked memory. - EXPECT_DEATH(discardable->Memory(), ".*Check failed.*"); +TEST_F(DiscardableMemoryManagerTest, DestructionAfterPurged) { + { + size_t size = 1024; + TestAllocationImpl allocation; + RegisterAndLock(&allocation, size); + EXPECT_EQ(1024u, BytesAllocated()); + Unlock(&allocation); + EXPECT_TRUE(CanBePurged(&allocation)); + SetMemoryLimit(0); + EXPECT_EQ(0u, BytesAllocated()); + Unregister(&allocation); + } + EXPECT_EQ(0u, BytesAllocated()); } -#endif class ThreadedDiscardableMemoryManagerTest : public DiscardableMemoryManagerTest { public: ThreadedDiscardableMemoryManagerTest() : memory_usage_thread_("memory_usage_thread"), - thread_sync_(true, false) { - } + thread_sync_(true, false) {} - virtual void SetUp() OVERRIDE { - memory_usage_thread_.Start(); - } + virtual void SetUp() OVERRIDE { memory_usage_thread_.Start(); } - virtual void TearDown() OVERRIDE { - memory_usage_thread_.Stop(); - } + virtual void TearDown() OVERRIDE { memory_usage_thread_.Stop(); } void UseMemoryHelper() { size_t size = 1024; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_TRUE(IsRegistered(discardable.get())); - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get())); - discardable->Unlock(); + TestAllocationImpl allocation; + RegisterAndLock(&allocation, size); + Unlock(&allocation); + Unregister(&allocation); } - void SignalHelper() { - thread_sync_.Signal(); - } + void SignalHelper() { thread_sync_.Signal(); } Thread memory_usage_thread_; WaitableEvent thread_sync_; @@ -407,4 +422,5 @@ TEST_F(ThreadedDiscardableMemoryManagerTest, UseMemoryOnThread) { thread_sync_.Wait(); } +} // namespace } // namespace base |