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