| Index: base/memory/discardable_memory_mac.cc
|
| diff --git a/base/memory/discardable_memory_mac.cc b/base/memory/discardable_memory_mac.cc
|
| index 8cd5905c904deb8392f6bef67399484e0dd78e75..b2b4b18a7061bf689907e7ef7df1b5c9f05ef1ac 100644
|
| --- a/base/memory/discardable_memory_mac.cc
|
| +++ b/base/memory/discardable_memory_mac.cc
|
| @@ -9,50 +9,91 @@
|
|
|
| #include "base/basictypes.h"
|
| #include "base/compiler_specific.h"
|
| +#include "base/lazy_instance.h"
|
| #include "base/logging.h"
|
| #include "base/memory/discardable_memory_emulated.h"
|
| #include "base/memory/discardable_memory_malloc.h"
|
| +#include "base/memory/discardable_memory_manager.h"
|
| #include "base/memory/scoped_ptr.h"
|
|
|
| namespace base {
|
| namespace {
|
|
|
| +// For Mac, have the DiscardableMemoryManager trigger userspace eviction when
|
| +// address space usage gets too high (e.g. 512 MBytes).
|
| +const size_t kMacMemoryLimit = 512 * 1024 * 1024;
|
| +
|
| +struct SharedState {
|
| + SharedState() : manager(kMacMemoryLimit, kMacMemoryLimit) {}
|
| +
|
| + internal::DiscardableMemoryManager manager;
|
| +};
|
| +LazyInstance<SharedState>::Leaky g_shared_state = LAZY_INSTANCE_INITIALIZER;
|
| +
|
| // The VM subsystem allows tagging of memory and 240-255 is reserved for
|
| // application use (see mach/vm_statistics.h). Pick 252 (after chromium's atomic
|
| // weight of ~52).
|
| const int kDiscardableMemoryTag = VM_MAKE_TAG(252);
|
|
|
| -class DiscardableMemoryMac : public DiscardableMemory {
|
| +class DiscardableMemoryMac
|
| + : public DiscardableMemory,
|
| + public internal::DiscardableMemoryManagerAllocation {
|
| public:
|
| - explicit DiscardableMemoryMac(size_t size)
|
| - : buffer_(0),
|
| - size_(size) {
|
| + explicit DiscardableMemoryMac(size_t bytes)
|
| + : buffer_(0), bytes_(bytes), is_locked_(false) {
|
| + g_shared_state.Pointer()->manager.Register(this, bytes);
|
| }
|
|
|
| - bool Initialize() {
|
| - kern_return_t ret = vm_allocate(mach_task_self(),
|
| - &buffer_,
|
| - size_,
|
| - VM_FLAGS_PURGABLE |
|
| - VM_FLAGS_ANYWHERE |
|
| - kDiscardableMemoryTag);
|
| - if (ret != KERN_SUCCESS) {
|
| - DLOG(ERROR) << "vm_allocate() failed";
|
| - return false;
|
| - }
|
| -
|
| - return true;
|
| - }
|
| + bool Initialize() { return Lock() == DISCARDABLE_MEMORY_LOCK_STATUS_PURGED; }
|
|
|
| virtual ~DiscardableMemoryMac() {
|
| + if (is_locked_)
|
| + Unlock();
|
| + g_shared_state.Pointer()->manager.Unregister(this);
|
| if (buffer_)
|
| - vm_deallocate(mach_task_self(), buffer_, size_);
|
| + vm_deallocate(mach_task_self(), buffer_, bytes_);
|
| }
|
|
|
| + // Overridden from DiscardableMemory:
|
| virtual DiscardableMemoryLockStatus Lock() OVERRIDE {
|
| - DCHECK_EQ(0, mprotect(reinterpret_cast<void*>(buffer_),
|
| - size_,
|
| - PROT_READ | PROT_WRITE));
|
| + DCHECK(!is_locked_);
|
| +
|
| + bool purged = false;
|
| + if (!g_shared_state.Pointer()->manager.AcquireLock(this, &purged))
|
| + return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED;
|
| +
|
| + is_locked_ = true;
|
| + return purged ? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED
|
| + : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS;
|
| + }
|
| + virtual void Unlock() OVERRIDE {
|
| + DCHECK(is_locked_);
|
| + g_shared_state.Pointer()->manager.ReleaseLock(this);
|
| + is_locked_ = false;
|
| + }
|
| + virtual void* Memory() const OVERRIDE {
|
| + DCHECK(is_locked_);
|
| + return reinterpret_cast<void*>(buffer_);
|
| + }
|
| +
|
| + // Overridden from internal::DiscardableMemoryManagerAllocation:
|
| + virtual bool AllocateAndAcquireLock() OVERRIDE {
|
| + if (!buffer_) {
|
| + kern_return_t ret = vm_allocate(
|
| + mach_task_self(),
|
| + &buffer_,
|
| + bytes_,
|
| + VM_FLAGS_PURGABLE | VM_FLAGS_ANYWHERE | kDiscardableMemoryTag);
|
| + if (ret != KERN_SUCCESS) {
|
| + DLOG(ERROR) << "vm_allocate() failed";
|
| + return false;
|
| + }
|
| + }
|
| +#if !defined(NDEBUG)
|
| + int status = mprotect(
|
| + reinterpret_cast<void*>(buffer_), bytes_, PROT_READ | PROT_WRITE);
|
| + DCHECK_EQ(0, status);
|
| +#endif
|
| int state = VM_PURGABLE_NONVOLATILE;
|
| kern_return_t ret = vm_purgable_control(mach_task_self(),
|
| buffer_,
|
| @@ -61,28 +102,32 @@ class DiscardableMemoryMac : public DiscardableMemory {
|
| if (ret != KERN_SUCCESS)
|
| return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED;
|
|
|
| - return state & VM_PURGABLE_EMPTY ? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED
|
| - : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS;
|
| + return state & VM_PURGABLE_EMPTY ? false : true;
|
| }
|
| -
|
| - virtual void Unlock() OVERRIDE {
|
| + virtual void ReleaseLock() OVERRIDE {
|
| int state = VM_PURGABLE_VOLATILE | VM_VOLATILE_GROUP_DEFAULT;
|
| kern_return_t ret = vm_purgable_control(mach_task_self(),
|
| buffer_,
|
| VM_PURGABLE_SET_STATE,
|
| &state);
|
| - DCHECK_EQ(0, mprotect(reinterpret_cast<void*>(buffer_), size_, PROT_NONE));
|
| if (ret != KERN_SUCCESS)
|
| DLOG(ERROR) << "Failed to unlock memory.";
|
| +#if !defined(NDEBUG)
|
| + int status = mprotect(reinterpret_cast<void*>(buffer_), bytes_, PROT_NONE);
|
| + DCHECK_EQ(0, status);
|
| +#endif
|
| }
|
| -
|
| - virtual void* Memory() const OVERRIDE {
|
| - return reinterpret_cast<void*>(buffer_);
|
| + virtual void Purge() OVERRIDE {
|
| + if (buffer_) {
|
| + vm_deallocate(mach_task_self(), buffer_, bytes_);
|
| + buffer_ = 0;
|
| + }
|
| }
|
|
|
| private:
|
| vm_address_t buffer_;
|
| - const size_t size_;
|
| + const size_t bytes_;
|
| + bool is_locked_;
|
|
|
| DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryMac);
|
| };
|
|
|