Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / base / memory / discardable_memory_mach.cc
1 // Copyright 2014 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_mach.h"
6
7 #include <mach/mach.h>
8
9 #include "base/basictypes.h"
10 #include "base/compiler_specific.h"
11 #include "base/lazy_instance.h"
12 #include "base/logging.h"
13 #include "base/mac/mach_logging.h"
14
15 namespace base {
16 namespace {
17
18 // For Mach, have the DiscardableMemoryManager trigger userspace eviction when
19 // address space usage gets too high (e.g. 512 MBytes).
20 const size_t kMachMemoryLimit = 512 * 1024 * 1024;
21
22 // internal::DiscardableMemoryManager has an explicit constructor that takes
23 // a number of memory limit parameters. The LeakyLazyInstanceTraits doesn't
24 // handle the case. Thus, we need our own class here.
25 struct DiscardableMemoryManagerLazyInstanceTraits {
26   // Leaky as discardable memory clients can use this after the exit handler
27   // has been called.
28   static const bool kRegisterOnExit = false;
29 #ifndef NDEBUG
30   static const bool kAllowedToAccessOnNonjoinableThread = true;
31 #endif
32
33   static internal::DiscardableMemoryManager* New(void* instance) {
34     return new (instance) internal::DiscardableMemoryManager(
35         kMachMemoryLimit, kMachMemoryLimit, TimeDelta::Max());
36   }
37   static void Delete(internal::DiscardableMemoryManager* instance) {
38     instance->~DiscardableMemoryManager();
39   }
40 };
41
42 LazyInstance<internal::DiscardableMemoryManager,
43              DiscardableMemoryManagerLazyInstanceTraits>
44     g_manager = LAZY_INSTANCE_INITIALIZER;
45
46 // The VM subsystem allows tagging of memory and 240-255 is reserved for
47 // application use (see mach/vm_statistics.h). Pick 252 (after chromium's atomic
48 // weight of ~52).
49 const int kDiscardableMemoryTag = VM_MAKE_TAG(252);
50
51 }  // namespace
52
53 namespace internal {
54
55 DiscardableMemoryMach::DiscardableMemoryMach(size_t bytes)
56     : memory_(0, 0), bytes_(mach_vm_round_page(bytes)), is_locked_(false) {
57   g_manager.Pointer()->Register(this, bytes);
58 }
59
60 DiscardableMemoryMach::~DiscardableMemoryMach() {
61   if (is_locked_)
62     Unlock();
63   g_manager.Pointer()->Unregister(this);
64 }
65
66 // static
67 void DiscardableMemoryMach::PurgeForTesting() {
68   int state = 0;
69   vm_purgable_control(mach_task_self(), 0, VM_PURGABLE_PURGE_ALL, &state);
70 }
71
72 bool DiscardableMemoryMach::Initialize() {
73   return Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_FAILED;
74 }
75
76 DiscardableMemoryLockStatus DiscardableMemoryMach::Lock() {
77   DCHECK(!is_locked_);
78
79   bool purged = false;
80   if (!g_manager.Pointer()->AcquireLock(this, &purged))
81     return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED;
82
83   is_locked_ = true;
84   return purged ? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED
85                 : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS;
86 }
87
88 void DiscardableMemoryMach::Unlock() {
89   DCHECK(is_locked_);
90   g_manager.Pointer()->ReleaseLock(this);
91   is_locked_ = false;
92 }
93
94 void* DiscardableMemoryMach::Memory() const {
95   DCHECK(is_locked_);
96   return reinterpret_cast<void*>(memory_.address());
97 }
98
99 bool DiscardableMemoryMach::AllocateAndAcquireLock() {
100   kern_return_t ret;
101   bool persistent;
102   if (!memory_.size()) {
103     vm_address_t address = 0;
104     ret = vm_allocate(
105         mach_task_self(),
106         &address,
107         bytes_,
108         VM_FLAGS_ANYWHERE | VM_FLAGS_PURGABLE | kDiscardableMemoryTag);
109     MACH_CHECK(ret == KERN_SUCCESS, ret) << "vm_allocate";
110     memory_.reset(address, bytes_);
111
112     // When making a fresh allocation, it's impossible for |persistent| to
113     // be true.
114     persistent = false;
115   } else {
116     // |persistent| will be reset to false below if appropriate, but when
117     // reusing an existing allocation, it's possible for it to be true.
118     persistent = true;
119
120 #if !defined(NDEBUG)
121     ret = vm_protect(mach_task_self(),
122                      memory_.address(),
123                      memory_.size(),
124                      FALSE,
125                      VM_PROT_DEFAULT);
126     MACH_DCHECK(ret == KERN_SUCCESS, ret) << "vm_protect";
127 #endif
128   }
129
130   int state = VM_PURGABLE_NONVOLATILE;
131   ret = vm_purgable_control(
132       mach_task_self(), memory_.address(), VM_PURGABLE_SET_STATE, &state);
133   MACH_CHECK(ret == KERN_SUCCESS, ret) << "vm_purgable_control";
134   if (state & VM_PURGABLE_EMPTY)
135     persistent = false;
136
137   return persistent;
138 }
139
140 void DiscardableMemoryMach::ReleaseLock() {
141   int state = VM_PURGABLE_VOLATILE | VM_VOLATILE_GROUP_DEFAULT;
142   kern_return_t ret = vm_purgable_control(
143       mach_task_self(), memory_.address(), VM_PURGABLE_SET_STATE, &state);
144   MACH_CHECK(ret == KERN_SUCCESS, ret) << "vm_purgable_control";
145
146 #if !defined(NDEBUG)
147   ret = vm_protect(
148       mach_task_self(), memory_.address(), memory_.size(), FALSE, VM_PROT_NONE);
149   MACH_DCHECK(ret == KERN_SUCCESS, ret) << "vm_protect";
150 #endif
151 }
152
153 void DiscardableMemoryMach::Purge() {
154   memory_.reset();
155 }
156
157 bool DiscardableMemoryMach::IsMemoryResident() const {
158   return true;
159 }
160
161 }  // namespace internal
162 }  // namespace base