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 #include <sys/mman.h> 9 10 #include "base/basictypes.h" 11 #include "base/compiler_specific.h" 12 #include "base/logging.h" 13 #include "base/memory/scoped_ptr.h" 14 15 namespace base { 16 namespace { 17 18 // The VM subsystem allows tagging of memory and 240-255 is reserved for 19 // application use (see mach/vm_statistics.h). Pick 252 (after chromium's atomic 20 // weight of ~52). 21 const int kDiscardableMemoryTag = VM_MAKE_TAG(252); 22 23 class DiscardableMemoryMac : public DiscardableMemory { 24 public: 25 DiscardableMemoryMac(void* memory, size_t size) 26 : memory_(memory), 27 size_(size) { 28 DCHECK(memory_); 29 } 30 31 virtual ~DiscardableMemoryMac() { 32 vm_deallocate(mach_task_self(), 33 reinterpret_cast<vm_address_t>(memory_), 34 size_); 35 } 36 37 virtual LockDiscardableMemoryStatus Lock() OVERRIDE { 38 DCHECK_EQ(0, mprotect(memory_, size_, PROT_READ | PROT_WRITE)); 39 int state = VM_PURGABLE_NONVOLATILE; 40 kern_return_t ret = vm_purgable_control( 41 mach_task_self(), 42 reinterpret_cast<vm_address_t>(memory_), 43 VM_PURGABLE_SET_STATE, 44 &state); 45 if (ret != KERN_SUCCESS) 46 return DISCARDABLE_MEMORY_FAILED; 47 48 return state & VM_PURGABLE_EMPTY ? DISCARDABLE_MEMORY_PURGED 49 : DISCARDABLE_MEMORY_SUCCESS; 50 } 51 52 virtual void Unlock() OVERRIDE { 53 int state = VM_PURGABLE_VOLATILE | VM_VOLATILE_GROUP_DEFAULT; 54 kern_return_t ret = vm_purgable_control( 55 mach_task_self(), 56 reinterpret_cast<vm_address_t>(memory_), 57 VM_PURGABLE_SET_STATE, 58 &state); 59 DCHECK_EQ(0, mprotect(memory_, size_, PROT_NONE)); 60 if (ret != KERN_SUCCESS) 61 DLOG(ERROR) << "Failed to unlock memory."; 62 } 63 64 virtual void* Memory() const OVERRIDE { 65 return memory_; 66 } 67 68 private: 69 void* const memory_; 70 const size_t size_; 71 72 DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryMac); 73 }; 74 75 } // namespace 76 77 // static 78 bool DiscardableMemory::SupportedNatively() { 79 return true; 80 } 81 82 // static 83 scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory( 84 size_t size) { 85 vm_address_t buffer = 0; 86 kern_return_t ret = vm_allocate(mach_task_self(), 87 &buffer, 88 size, 89 VM_FLAGS_PURGABLE | 90 VM_FLAGS_ANYWHERE | 91 kDiscardableMemoryTag); 92 if (ret != KERN_SUCCESS) { 93 DLOG(ERROR) << "vm_allocate() failed"; 94 return scoped_ptr<DiscardableMemory>(); 95 } 96 return scoped_ptr<DiscardableMemory>( 97 new DiscardableMemoryMac(reinterpret_cast<void*>(buffer), size)); 98 } 99 100 // static 101 bool DiscardableMemory::PurgeForTestingSupported() { 102 return true; 103 } 104 105 // static 106 void DiscardableMemory::PurgeForTesting() { 107 int state = 0; 108 vm_purgable_control(mach_task_self(), 0, VM_PURGABLE_PURGE_ALL, &state); 109 } 110 111 } // namespace base 112