• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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 #include "base/mac/scoped_mach_vm.h"
15 #include "base/memory/discardable_memory_emulated.h"
16 #include "base/memory/discardable_memory_malloc.h"
17 #include "base/memory/discardable_memory_manager.h"
18 #include "base/memory/scoped_ptr.h"
19 
20 namespace base {
21 namespace {
22 
23 // For Mac, have the DiscardableMemoryManager trigger userspace eviction when
24 // address space usage gets too high (e.g. 512 MBytes).
25 const size_t kMacMemoryLimit = 512 * 1024 * 1024;
26 
27 struct SharedState {
SharedStatebase::__anone936e73e0111::SharedState28   SharedState() : manager(kMacMemoryLimit, kMacMemoryLimit, TimeDelta::Max()) {}
29 
30   internal::DiscardableMemoryManager manager;
31 };
32 LazyInstance<SharedState>::Leaky g_shared_state = LAZY_INSTANCE_INITIALIZER;
33 
34 // The VM subsystem allows tagging of memory and 240-255 is reserved for
35 // application use (see mach/vm_statistics.h). Pick 252 (after chromium's atomic
36 // weight of ~52).
37 const int kDiscardableMemoryTag = VM_MAKE_TAG(252);
38 
39 class DiscardableMemoryMac
40     : public DiscardableMemory,
41       public internal::DiscardableMemoryManagerAllocation {
42  public:
DiscardableMemoryMac(size_t bytes)43   explicit DiscardableMemoryMac(size_t bytes)
44       : memory_(0, 0),
45         bytes_(mach_vm_round_page(bytes)),
46         is_locked_(false) {
47     g_shared_state.Pointer()->manager.Register(this, bytes);
48   }
49 
Initialize()50   bool Initialize() { return Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; }
51 
~DiscardableMemoryMac()52   virtual ~DiscardableMemoryMac() {
53     if (is_locked_)
54       Unlock();
55     g_shared_state.Pointer()->manager.Unregister(this);
56   }
57 
58   // Overridden from DiscardableMemory:
Lock()59   virtual DiscardableMemoryLockStatus Lock() OVERRIDE {
60     DCHECK(!is_locked_);
61 
62     bool purged = false;
63     if (!g_shared_state.Pointer()->manager.AcquireLock(this, &purged))
64       return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED;
65 
66     is_locked_ = true;
67     return purged ? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED
68                   : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS;
69   }
70 
Unlock()71   virtual void Unlock() OVERRIDE {
72     DCHECK(is_locked_);
73     g_shared_state.Pointer()->manager.ReleaseLock(this);
74     is_locked_ = false;
75   }
76 
Memory() const77   virtual void* Memory() const OVERRIDE {
78     DCHECK(is_locked_);
79     return reinterpret_cast<void*>(memory_.address());
80   }
81 
82   // Overridden from internal::DiscardableMemoryManagerAllocation:
AllocateAndAcquireLock()83   virtual bool AllocateAndAcquireLock() OVERRIDE {
84     kern_return_t ret;
85     bool persistent;
86     if (!memory_.size()) {
87       vm_address_t address = 0;
88       ret = vm_allocate(
89           mach_task_self(),
90           &address,
91           bytes_,
92           VM_FLAGS_ANYWHERE | VM_FLAGS_PURGABLE | kDiscardableMemoryTag);
93       MACH_CHECK(ret == KERN_SUCCESS, ret) << "vm_allocate";
94       memory_.reset(address, bytes_);
95 
96       // When making a fresh allocation, it's impossible for |persistent| to
97       // be true.
98       persistent = false;
99     } else {
100       // |persistent| will be reset to false below if appropriate, but when
101       // reusing an existing allocation, it's possible for it to be true.
102       persistent = true;
103 
104 #if !defined(NDEBUG)
105       ret = vm_protect(mach_task_self(),
106                        memory_.address(),
107                        memory_.size(),
108                        FALSE,
109                        VM_PROT_DEFAULT);
110       MACH_DCHECK(ret == KERN_SUCCESS, ret) << "vm_protect";
111 #endif
112     }
113 
114     int state = VM_PURGABLE_NONVOLATILE;
115     ret = vm_purgable_control(mach_task_self(),
116                               memory_.address(),
117                               VM_PURGABLE_SET_STATE,
118                               &state);
119     MACH_CHECK(ret == KERN_SUCCESS, ret) << "vm_purgable_control";
120     if (state & VM_PURGABLE_EMPTY)
121       persistent = false;
122 
123     return persistent;
124   }
125 
ReleaseLock()126   virtual void ReleaseLock() OVERRIDE {
127     int state = VM_PURGABLE_VOLATILE | VM_VOLATILE_GROUP_DEFAULT;
128     kern_return_t ret = vm_purgable_control(mach_task_self(),
129                                             memory_.address(),
130                                             VM_PURGABLE_SET_STATE,
131                                             &state);
132     MACH_CHECK(ret == KERN_SUCCESS, ret) << "vm_purgable_control";
133 
134 #if !defined(NDEBUG)
135     ret = vm_protect(mach_task_self(),
136                      memory_.address(),
137                      memory_.size(),
138                      FALSE,
139                      VM_PROT_NONE);
140     MACH_DCHECK(ret == KERN_SUCCESS, ret) << "vm_protect";
141 #endif
142   }
143 
Purge()144   virtual void Purge() OVERRIDE {
145     memory_.reset();
146   }
147 
148  private:
149   mac::ScopedMachVM memory_;
150   const size_t bytes_;
151   bool is_locked_;
152 
153   DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryMac);
154 };
155 
156 }  // namespace
157 
158 // static
ReduceMemoryUsage()159 bool DiscardableMemory::ReduceMemoryUsage() {
160   return internal::DiscardableMemoryEmulated::ReduceMemoryUsage();
161 }
162 
163 // static
GetSupportedTypes(std::vector<DiscardableMemoryType> * types)164 void DiscardableMemory::GetSupportedTypes(
165     std::vector<DiscardableMemoryType>* types) {
166   const DiscardableMemoryType supported_types[] = {
167     DISCARDABLE_MEMORY_TYPE_MAC,
168     DISCARDABLE_MEMORY_TYPE_EMULATED,
169     DISCARDABLE_MEMORY_TYPE_MALLOC
170   };
171   types->assign(supported_types, supported_types + arraysize(supported_types));
172 }
173 
174 // static
CreateLockedMemoryWithType(DiscardableMemoryType type,size_t size)175 scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemoryWithType(
176     DiscardableMemoryType type, size_t size) {
177   switch (type) {
178     case DISCARDABLE_MEMORY_TYPE_NONE:
179     case DISCARDABLE_MEMORY_TYPE_ASHMEM:
180       return scoped_ptr<DiscardableMemory>();
181     case DISCARDABLE_MEMORY_TYPE_MAC: {
182       scoped_ptr<DiscardableMemoryMac> memory(new DiscardableMemoryMac(size));
183       if (!memory->Initialize())
184         return scoped_ptr<DiscardableMemory>();
185 
186       return memory.PassAs<DiscardableMemory>();
187     }
188     case DISCARDABLE_MEMORY_TYPE_EMULATED: {
189       scoped_ptr<internal::DiscardableMemoryEmulated> memory(
190           new internal::DiscardableMemoryEmulated(size));
191       if (!memory->Initialize())
192         return scoped_ptr<DiscardableMemory>();
193 
194       return memory.PassAs<DiscardableMemory>();
195     }
196     case DISCARDABLE_MEMORY_TYPE_MALLOC: {
197       scoped_ptr<internal::DiscardableMemoryMalloc> memory(
198           new internal::DiscardableMemoryMalloc(size));
199       if (!memory->Initialize())
200         return scoped_ptr<DiscardableMemory>();
201 
202       return memory.PassAs<DiscardableMemory>();
203     }
204   }
205 
206   NOTREACHED();
207   return scoped_ptr<DiscardableMemory>();
208 }
209 
210 // static
PurgeForTesting()211 void DiscardableMemory::PurgeForTesting() {
212   int state = 0;
213   vm_purgable_control(mach_task_self(), 0, VM_PURGABLE_PURGE_ALL, &state);
214   internal::DiscardableMemoryEmulated::PurgeForTesting();
215 }
216 
217 }  // namespace base
218