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