• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
DiscardableMemoryMac(void * memory,size_t size)25   DiscardableMemoryMac(void* memory, size_t size)
26       : memory_(memory),
27         size_(size) {
28     DCHECK(memory_);
29   }
30 
~DiscardableMemoryMac()31   virtual ~DiscardableMemoryMac() {
32     vm_deallocate(mach_task_self(),
33                   reinterpret_cast<vm_address_t>(memory_),
34                   size_);
35   }
36 
Lock()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 
Unlock()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 
Memory() const64   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
SupportedNatively()78 bool DiscardableMemory::SupportedNatively() {
79   return true;
80 }
81 
82 // static
CreateLockedMemory(size_t size)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
PurgeForTestingSupported()101 bool DiscardableMemory::PurgeForTestingSupported() {
102   return true;
103 }
104 
105 // static
PurgeForTesting()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