0c0b2a60add086856647343be5b1998c40dc8359
[platform/framework/web/crosswalk.git] / src / base / memory / discardable_memory_mac.cc
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/discardable_memory_emulated.h"
14 #include "base/memory/scoped_ptr.h"
15
16 namespace base {
17 namespace {
18
19 // The VM subsystem allows tagging of memory and 240-255 is reserved for
20 // application use (see mach/vm_statistics.h). Pick 252 (after chromium's atomic
21 // weight of ~52).
22 const int kDiscardableMemoryTag = VM_MAKE_TAG(252);
23
24 class DiscardableMemoryMac : public DiscardableMemory {
25  public:
26   explicit DiscardableMemoryMac(size_t size)
27       : buffer_(0),
28         size_(size) {
29   }
30
31   bool Initialize() {
32     kern_return_t ret = vm_allocate(mach_task_self(),
33                                     &buffer_,
34                                     size_,
35                                     VM_FLAGS_PURGABLE |
36                                     VM_FLAGS_ANYWHERE |
37                                     kDiscardableMemoryTag);
38     if (ret != KERN_SUCCESS) {
39       DLOG(ERROR) << "vm_allocate() failed";
40       return false;
41     }
42
43     return true;
44   }
45
46   virtual ~DiscardableMemoryMac() {
47     if (buffer_)
48       vm_deallocate(mach_task_self(), buffer_, size_);
49   }
50
51   virtual DiscardableMemoryLockStatus Lock() OVERRIDE {
52     DCHECK_EQ(0, mprotect(reinterpret_cast<void*>(buffer_),
53                           size_,
54                           PROT_READ | PROT_WRITE));
55     int state = VM_PURGABLE_NONVOLATILE;
56     kern_return_t ret = vm_purgable_control(mach_task_self(),
57                                             buffer_,
58                                             VM_PURGABLE_SET_STATE,
59                                             &state);
60     if (ret != KERN_SUCCESS)
61       return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED;
62
63     return state & VM_PURGABLE_EMPTY ? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED
64                                      : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS;
65   }
66
67   virtual void Unlock() OVERRIDE {
68     int state = VM_PURGABLE_VOLATILE | VM_VOLATILE_GROUP_DEFAULT;
69     kern_return_t ret = vm_purgable_control(mach_task_self(),
70                                             buffer_,
71                                             VM_PURGABLE_SET_STATE,
72                                             &state);
73     DCHECK_EQ(0, mprotect(reinterpret_cast<void*>(buffer_), size_, PROT_NONE));
74     if (ret != KERN_SUCCESS)
75       DLOG(ERROR) << "Failed to unlock memory.";
76   }
77
78   virtual void* Memory() const OVERRIDE {
79     return reinterpret_cast<void*>(buffer_);
80   }
81
82  private:
83   vm_address_t buffer_;
84   const size_t size_;
85
86   DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryMac);
87 };
88
89 }  // namespace
90
91 // static
92 void DiscardableMemory::RegisterMemoryPressureListeners() {
93   internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners();
94 }
95
96 // static
97 void DiscardableMemory::UnregisterMemoryPressureListeners() {
98   internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners();
99 }
100
101 // static
102 void DiscardableMemory::GetSupportedTypes(
103     std::vector<DiscardableMemoryType>* types) {
104   const DiscardableMemoryType supported_types[] = {
105     DISCARDABLE_MEMORY_TYPE_MAC,
106     DISCARDABLE_MEMORY_TYPE_EMULATED
107   };
108   types->assign(supported_types, supported_types + arraysize(supported_types));
109 }
110
111 // static
112 scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemoryWithType(
113     DiscardableMemoryType type, size_t size) {
114   switch (type) {
115     case DISCARDABLE_MEMORY_TYPE_NONE:
116     case DISCARDABLE_MEMORY_TYPE_ANDROID:
117       return scoped_ptr<DiscardableMemory>();
118     case DISCARDABLE_MEMORY_TYPE_MAC: {
119       scoped_ptr<DiscardableMemoryMac> memory(new DiscardableMemoryMac(size));
120       if (!memory->Initialize())
121         return scoped_ptr<DiscardableMemory>();
122
123       return memory.PassAs<DiscardableMemory>();
124     }
125     case DISCARDABLE_MEMORY_TYPE_EMULATED: {
126       scoped_ptr<internal::DiscardableMemoryEmulated> memory(
127           new internal::DiscardableMemoryEmulated(size));
128       if (!memory->Initialize())
129         return scoped_ptr<DiscardableMemory>();
130
131       return memory.PassAs<DiscardableMemory>();
132     }
133   }
134
135   NOTREACHED();
136   return scoped_ptr<DiscardableMemory>();
137 }
138
139 // static
140 bool DiscardableMemory::PurgeForTestingSupported() {
141   return true;
142 }
143
144 // static
145 void DiscardableMemory::PurgeForTesting() {
146   int state = 0;
147   vm_purgable_control(mach_task_self(), 0, VM_PURGABLE_PURGE_ALL, &state);
148   internal::DiscardableMemoryEmulated::PurgeForTesting();
149 }
150
151 }  // namespace base