1// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "base/memory/discardable_memory.h" 6 7#include <mach/mach.h> 8 9#include "base/basictypes.h" 10#include "base/compiler_specific.h" 11#include "base/lazy_instance.h" 12#include "base/logging.h" 13#include "base/mac/mach_logging.h" 14#include "base/mac/scoped_mach_vm.h" 15#include "base/memory/discardable_memory_emulated.h" 16#include "base/memory/discardable_memory_malloc.h" 17#include "base/memory/discardable_memory_manager.h" 18#include "base/memory/scoped_ptr.h" 19 20namespace base { 21namespace { 22 23// For Mac, have the DiscardableMemoryManager trigger userspace eviction when 24// address space usage gets too high (e.g. 512 MBytes). 25const size_t kMacMemoryLimit = 512 * 1024 * 1024; 26 27struct SharedState { 28 SharedState() : manager(kMacMemoryLimit, kMacMemoryLimit, TimeDelta::Max()) {} 29 30 internal::DiscardableMemoryManager manager; 31}; 32LazyInstance<SharedState>::Leaky g_shared_state = LAZY_INSTANCE_INITIALIZER; 33 34// The VM subsystem allows tagging of memory and 240-255 is reserved for 35// application use (see mach/vm_statistics.h). Pick 252 (after chromium's atomic 36// weight of ~52). 37const int kDiscardableMemoryTag = VM_MAKE_TAG(252); 38 39class DiscardableMemoryMac 40 : public DiscardableMemory, 41 public internal::DiscardableMemoryManagerAllocation { 42 public: 43 explicit DiscardableMemoryMac(size_t bytes) 44 : memory_(0, 0), 45 bytes_(mach_vm_round_page(bytes)), 46 is_locked_(false) { 47 g_shared_state.Pointer()->manager.Register(this, bytes); 48 } 49 50 bool Initialize() { return Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; } 51 52 virtual ~DiscardableMemoryMac() { 53 if (is_locked_) 54 Unlock(); 55 g_shared_state.Pointer()->manager.Unregister(this); 56 } 57 58 // Overridden from DiscardableMemory: 59 virtual DiscardableMemoryLockStatus Lock() OVERRIDE { 60 DCHECK(!is_locked_); 61 62 bool purged = false; 63 if (!g_shared_state.Pointer()->manager.AcquireLock(this, &purged)) 64 return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; 65 66 is_locked_ = true; 67 return purged ? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED 68 : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS; 69 } 70 71 virtual void Unlock() OVERRIDE { 72 DCHECK(is_locked_); 73 g_shared_state.Pointer()->manager.ReleaseLock(this); 74 is_locked_ = false; 75 } 76 77 virtual void* Memory() const OVERRIDE { 78 DCHECK(is_locked_); 79 return reinterpret_cast<void*>(memory_.address()); 80 } 81 82 // Overridden from internal::DiscardableMemoryManagerAllocation: 83 virtual bool AllocateAndAcquireLock() OVERRIDE { 84 kern_return_t ret; 85 bool persistent; 86 if (!memory_.size()) { 87 vm_address_t address = 0; 88 ret = vm_allocate( 89 mach_task_self(), 90 &address, 91 bytes_, 92 VM_FLAGS_ANYWHERE | VM_FLAGS_PURGABLE | kDiscardableMemoryTag); 93 MACH_CHECK(ret == KERN_SUCCESS, ret) << "vm_allocate"; 94 memory_.reset(address, bytes_); 95 96 // When making a fresh allocation, it's impossible for |persistent| to 97 // be true. 98 persistent = false; 99 } else { 100 // |persistent| will be reset to false below if appropriate, but when 101 // reusing an existing allocation, it's possible for it to be true. 102 persistent = true; 103 104#if !defined(NDEBUG) 105 ret = vm_protect(mach_task_self(), 106 memory_.address(), 107 memory_.size(), 108 FALSE, 109 VM_PROT_DEFAULT); 110 MACH_DCHECK(ret == KERN_SUCCESS, ret) << "vm_protect"; 111#endif 112 } 113 114 int state = VM_PURGABLE_NONVOLATILE; 115 ret = vm_purgable_control(mach_task_self(), 116 memory_.address(), 117 VM_PURGABLE_SET_STATE, 118 &state); 119 MACH_CHECK(ret == KERN_SUCCESS, ret) << "vm_purgable_control"; 120 if (state & VM_PURGABLE_EMPTY) 121 persistent = false; 122 123 return persistent; 124 } 125 126 virtual void ReleaseLock() OVERRIDE { 127 int state = VM_PURGABLE_VOLATILE | VM_VOLATILE_GROUP_DEFAULT; 128 kern_return_t ret = vm_purgable_control(mach_task_self(), 129 memory_.address(), 130 VM_PURGABLE_SET_STATE, 131 &state); 132 MACH_CHECK(ret == KERN_SUCCESS, ret) << "vm_purgable_control"; 133 134#if !defined(NDEBUG) 135 ret = vm_protect(mach_task_self(), 136 memory_.address(), 137 memory_.size(), 138 FALSE, 139 VM_PROT_NONE); 140 MACH_DCHECK(ret == KERN_SUCCESS, ret) << "vm_protect"; 141#endif 142 } 143 144 virtual void Purge() OVERRIDE { 145 memory_.reset(); 146 } 147 148 private: 149 mac::ScopedMachVM memory_; 150 const size_t bytes_; 151 bool is_locked_; 152 153 DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryMac); 154}; 155 156} // namespace 157 158// static 159bool DiscardableMemory::ReduceMemoryUsage() { 160 return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); 161} 162 163// static 164void DiscardableMemory::GetSupportedTypes( 165 std::vector<DiscardableMemoryType>* types) { 166 const DiscardableMemoryType supported_types[] = { 167 DISCARDABLE_MEMORY_TYPE_MAC, 168 DISCARDABLE_MEMORY_TYPE_EMULATED, 169 DISCARDABLE_MEMORY_TYPE_MALLOC 170 }; 171 types->assign(supported_types, supported_types + arraysize(supported_types)); 172} 173 174// static 175scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemoryWithType( 176 DiscardableMemoryType type, size_t size) { 177 switch (type) { 178 case DISCARDABLE_MEMORY_TYPE_NONE: 179 case DISCARDABLE_MEMORY_TYPE_ASHMEM: 180 return scoped_ptr<DiscardableMemory>(); 181 case DISCARDABLE_MEMORY_TYPE_MAC: { 182 scoped_ptr<DiscardableMemoryMac> memory(new DiscardableMemoryMac(size)); 183 if (!memory->Initialize()) 184 return scoped_ptr<DiscardableMemory>(); 185 186 return memory.PassAs<DiscardableMemory>(); 187 } 188 case DISCARDABLE_MEMORY_TYPE_EMULATED: { 189 scoped_ptr<internal::DiscardableMemoryEmulated> memory( 190 new internal::DiscardableMemoryEmulated(size)); 191 if (!memory->Initialize()) 192 return scoped_ptr<DiscardableMemory>(); 193 194 return memory.PassAs<DiscardableMemory>(); 195 } 196 case DISCARDABLE_MEMORY_TYPE_MALLOC: { 197 scoped_ptr<internal::DiscardableMemoryMalloc> memory( 198 new internal::DiscardableMemoryMalloc(size)); 199 if (!memory->Initialize()) 200 return scoped_ptr<DiscardableMemory>(); 201 202 return memory.PassAs<DiscardableMemory>(); 203 } 204 } 205 206 NOTREACHED(); 207 return scoped_ptr<DiscardableMemory>(); 208} 209 210// static 211void DiscardableMemory::PurgeForTesting() { 212 int state = 0; 213 vm_purgable_control(mach_task_self(), 0, VM_PURGABLE_PURGE_ALL, &state); 214 internal::DiscardableMemoryEmulated::PurgeForTesting(); 215} 216 217} // namespace base 218