Home | History | Annotate | Download | only in memory
      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