| // 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_emulated.h" |
| |
| #include "base/lazy_instance.h" |
| #include "base/memory/discardable_memory_manager.h" |
| |
| namespace base { |
| namespace { |
| |
| // This is admittedly pretty magical. |
| const size_t kEmulatedMemoryLimit = 512 * 1024 * 1024; |
| const size_t kEmulatedSoftMemoryLimit = 32 * 1024 * 1024; |
| const size_t kEmulatedHardMemoryLimitExpirationTimeMs = 1000; |
| |
| // internal::DiscardableMemoryManager has an explicit constructor that takes |
| // a number of memory limit parameters. The LeakyLazyInstanceTraits doesn't |
| // handle the case. Thus, we need our own class here. |
| struct DiscardableMemoryManagerLazyInstanceTraits { |
| // Leaky as discardable memory clients can use this after the exit handler |
| // has been called. |
| static const bool kRegisterOnExit = false; |
| #ifndef NDEBUG |
| static const bool kAllowedToAccessOnNonjoinableThread = true; |
| #endif |
| |
| static internal::DiscardableMemoryManager* New(void* instance) { |
| return new (instance) internal::DiscardableMemoryManager( |
| kEmulatedMemoryLimit, |
| kEmulatedSoftMemoryLimit, |
| TimeDelta::FromMilliseconds(kEmulatedHardMemoryLimitExpirationTimeMs)); |
| } |
| static void Delete(internal::DiscardableMemoryManager* instance) { |
| instance->~DiscardableMemoryManager(); |
| } |
| }; |
| |
| LazyInstance<internal::DiscardableMemoryManager, |
| DiscardableMemoryManagerLazyInstanceTraits> |
| g_manager = LAZY_INSTANCE_INITIALIZER; |
| |
| } // namespace |
| |
| namespace internal { |
| |
| DiscardableMemoryEmulated::DiscardableMemoryEmulated(size_t bytes) |
| : bytes_(bytes), |
| is_locked_(false) { |
| g_manager.Pointer()->Register(this, bytes); |
| } |
| |
| DiscardableMemoryEmulated::~DiscardableMemoryEmulated() { |
| if (is_locked_) |
| Unlock(); |
| g_manager.Pointer()->Unregister(this); |
| } |
| |
| // static |
| bool DiscardableMemoryEmulated::ReduceMemoryUsage() { |
| return g_manager.Pointer()->ReduceMemoryUsage(); |
| } |
| |
| // static |
| void DiscardableMemoryEmulated::ReduceMemoryUsageUntilWithinLimit( |
| size_t bytes) { |
| g_manager.Pointer()->ReduceMemoryUsageUntilWithinLimit(bytes); |
| } |
| |
| // static |
| void DiscardableMemoryEmulated::PurgeForTesting() { |
| g_manager.Pointer()->PurgeAll(); |
| } |
| |
| bool DiscardableMemoryEmulated::Initialize() { |
| return Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; |
| } |
| |
| DiscardableMemoryLockStatus DiscardableMemoryEmulated::Lock() { |
| DCHECK(!is_locked_); |
| |
| bool purged = false; |
| if (!g_manager.Pointer()->AcquireLock(this, &purged)) |
| return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; |
| |
| is_locked_ = true; |
| return purged ? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED |
| : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS; |
| } |
| |
| void DiscardableMemoryEmulated::Unlock() { |
| DCHECK(is_locked_); |
| g_manager.Pointer()->ReleaseLock(this); |
| is_locked_ = false; |
| } |
| |
| void* DiscardableMemoryEmulated::Memory() const { |
| DCHECK(is_locked_); |
| DCHECK(memory_); |
| return memory_.get(); |
| } |
| |
| bool DiscardableMemoryEmulated::AllocateAndAcquireLock() { |
| if (memory_) |
| return true; |
| |
| memory_.reset(new uint8[bytes_]); |
| return false; |
| } |
| |
| void DiscardableMemoryEmulated::Purge() { |
| memory_.reset(); |
| } |
| |
| } // namespace internal |
| } // namespace base |