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