Add status to server credit cards.
[chromium-blink-merge.git] / base / memory / discardable_memory_mach.cc
blob5fc43f2a7d850e487cae1f339adb89211a6281f4
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.
5 #include "base/memory/discardable_memory_mach.h"
7 #include <mach/mach.h>
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"
15 namespace base {
16 namespace {
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;
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
33 static internal::DiscardableMemoryManager* New(void* instance) {
34 return new (instance) internal::DiscardableMemoryManager(
35 kMachMemoryLimit, kMachMemoryLimit, TimeDelta::Max());
37 static void Delete(internal::DiscardableMemoryManager* instance) {
38 instance->~DiscardableMemoryManager();
42 LazyInstance<internal::DiscardableMemoryManager,
43 DiscardableMemoryManagerLazyInstanceTraits>
44 g_manager = LAZY_INSTANCE_INITIALIZER;
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);
51 } // namespace
53 namespace internal {
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);
60 DiscardableMemoryMach::~DiscardableMemoryMach() {
61 if (is_locked_)
62 Unlock();
63 g_manager.Pointer()->Unregister(this);
66 // static
67 void DiscardableMemoryMach::PurgeForTesting() {
68 int state = 0;
69 vm_purgable_control(mach_task_self(), 0, VM_PURGABLE_PURGE_ALL, &state);
72 bool DiscardableMemoryMach::Initialize() {
73 return Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_FAILED;
76 DiscardableMemoryLockStatus DiscardableMemoryMach::Lock() {
77 DCHECK(!is_locked_);
79 bool purged = false;
80 if (!g_manager.Pointer()->AcquireLock(this, &purged))
81 return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED;
83 is_locked_ = true;
84 return purged ? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED
85 : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS;
88 void DiscardableMemoryMach::Unlock() {
89 DCHECK(is_locked_);
90 g_manager.Pointer()->ReleaseLock(this);
91 is_locked_ = false;
94 void* DiscardableMemoryMach::Memory() const {
95 DCHECK(is_locked_);
96 return reinterpret_cast<void*>(memory_.address());
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_);
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;
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
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;
137 return persistent;
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";
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
153 void DiscardableMemoryMach::Purge() {
154 memory_.reset();
157 bool DiscardableMemoryMach::IsMemoryResident() const {
158 return true;
161 } // namespace internal
162 } // namespace base