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
9#include "base/logging.h"
10
11namespace base {
12
13namespace {
14
15// The VM subsystem allows tagging of memory and 240-255 is reserved for
16// application use (see mach/vm_statistics.h). Pick 252 (after chromium's atomic
17// weight of ~52).
18const int kDiscardableMemoryTag = VM_MAKE_TAG(252);
19
20}  // namespace
21
22// static
23bool DiscardableMemory::Supported() {
24  return true;
25}
26
27DiscardableMemory::~DiscardableMemory() {
28  if (memory_) {
29    vm_deallocate(mach_task_self(),
30                  reinterpret_cast<vm_address_t>(memory_),
31                  size_);
32  }
33}
34
35bool DiscardableMemory::InitializeAndLock(size_t size) {
36  DCHECK(!memory_);
37  size_ = size;
38
39  vm_address_t buffer = 0;
40  kern_return_t ret = vm_allocate(mach_task_self(),
41                                  &buffer,
42                                  size,
43                                  VM_FLAGS_PURGABLE |
44                                  VM_FLAGS_ANYWHERE |
45                                  kDiscardableMemoryTag);
46
47  if (ret != KERN_SUCCESS) {
48    DLOG(ERROR) << "vm_allocate() failed";
49    return false;
50  }
51
52  is_locked_ = true;
53  memory_ = reinterpret_cast<void*>(buffer);
54  return true;
55}
56
57LockDiscardableMemoryStatus DiscardableMemory::Lock() {
58  DCHECK(!is_locked_);
59
60  int state = VM_PURGABLE_NONVOLATILE;
61  kern_return_t ret = vm_purgable_control(
62      mach_task_self(),
63      reinterpret_cast<vm_address_t>(memory_),
64      VM_PURGABLE_SET_STATE,
65      &state);
66
67  if (ret != KERN_SUCCESS)
68    return DISCARDABLE_MEMORY_FAILED;
69
70  is_locked_ = true;
71  return state & VM_PURGABLE_EMPTY ? DISCARDABLE_MEMORY_PURGED
72                                   : DISCARDABLE_MEMORY_SUCCESS;
73}
74
75void DiscardableMemory::Unlock() {
76  DCHECK(is_locked_);
77
78  int state = VM_PURGABLE_VOLATILE | VM_VOLATILE_GROUP_DEFAULT;
79  kern_return_t ret = vm_purgable_control(
80      mach_task_self(),
81      reinterpret_cast<vm_address_t>(memory_),
82      VM_PURGABLE_SET_STATE,
83      &state);
84
85  if (ret != KERN_SUCCESS)
86    DLOG(ERROR) << "Failed to unlock memory.";
87
88  is_locked_ = false;
89}
90
91// static
92bool DiscardableMemory::PurgeForTestingSupported() {
93  return true;
94}
95
96// static
97void DiscardableMemory::PurgeForTesting() {
98  int state = 0;
99  vm_purgable_control(mach_task_self(), 0, VM_PURGABLE_PURGE_ALL, &state);
100}
101
102}  // namespace base
103