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"
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"
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
28 static const bool kRegisterOnExit
= false;
30 static const bool kAllowedToAccessOnNonjoinableThread
= true;
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
49 const int kDiscardableMemoryTag
= VM_MAKE_TAG(252);
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() {
63 g_manager
.Pointer()->Unregister(this);
67 void DiscardableMemoryMach::PurgeForTesting() {
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() {
80 if (!g_manager
.Pointer()->AcquireLock(this, &purged
))
81 return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED
;
84 return purged
? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED
85 : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS
;
88 void DiscardableMemoryMach::Unlock() {
90 g_manager
.Pointer()->ReleaseLock(this);
94 void* DiscardableMemoryMach::Memory() const {
96 return reinterpret_cast<void*>(memory_
.address());
99 bool DiscardableMemoryMach::AllocateAndAcquireLock() {
102 if (!memory_
.size()) {
103 vm_address_t address
= 0;
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
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.
121 ret
= vm_protect(mach_task_self(),
126 MACH_DCHECK(ret
== KERN_SUCCESS
, ret
) << "vm_protect";
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
)
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";
148 mach_task_self(), memory_
.address(), memory_
.size(), FALSE
, VM_PROT_NONE
);
149 MACH_DCHECK(ret
== KERN_SUCCESS
, ret
) << "vm_protect";
153 void DiscardableMemoryMach::Purge() {
157 bool DiscardableMemoryMach::IsMemoryResident() const {
161 } // namespace internal