Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(446)

Side by Side Diff: base/memory/discardable_memory_android.cc

Issue 25293002: Add DiscardableMemoryAllocator to work around FD limit issue. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: s/actual_size/recycled_chunk_size Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/memory/discardable_memory.h" 5 #include "base/memory/discardable_memory_android.h"
6 6
7 #include <sys/mman.h> 7 #include <sys/mman.h>
8 #include <sys/resource.h>
9 #include <sys/time.h>
8 #include <unistd.h> 10 #include <unistd.h>
9 11
10 #include "base/basictypes.h" 12 #include "base/basictypes.h"
11 #include "base/compiler_specific.h" 13 #include "base/compiler_specific.h"
12 #include "base/file_util.h" 14 #include "base/file_util.h"
13 #include "base/lazy_instance.h" 15 #include "base/lazy_instance.h"
14 #include "base/logging.h" 16 #include "base/logging.h"
17 #include "base/memory/discardable_memory.h"
18 #include "base/memory/discardable_memory_allocator_android.h"
15 #include "base/posix/eintr_wrapper.h" 19 #include "base/posix/eintr_wrapper.h"
16 #include "base/synchronization/lock.h" 20 #include "base/synchronization/lock.h"
17 #include "third_party/ashmem/ashmem.h" 21 #include "third_party/ashmem/ashmem.h"
18 22
19 namespace base { 23 namespace base {
20 namespace { 24 namespace {
21 25
22 // Protects |g_num_discardable_memory| below. 26 const char kAshmemAllocatorName[] = "DiscardableMemoryAllocator";
23 base::LazyInstance<base::Lock>::Leaky g_discardable_memory_lock =
24 LAZY_INSTANCE_INITIALIZER;
25 27
26 // Total number of discardable memory in the process. 28 struct GlobalContext {
27 int g_num_discardable_memory = 0; 29 GlobalContext()
30 : ashmem_fd_limit(GetSoftFDLimit()),
31 allocator(kAshmemAllocatorName),
32 ashmem_fd_count_(0) {
33 }
28 34
29 // Upper limit on the number of discardable memory to avoid hitting file 35 const int ashmem_fd_limit;
30 // descriptor limit. 36 internal::DiscardableMemoryAllocator allocator;
31 const int kDiscardableMemoryNumLimit = 128; 37 Lock lock;
38
39 int ashmem_fd_count() const {
40 lock.AssertAcquired();
41 return ashmem_fd_count_;
42 }
43
44 void decrement_ahmem_fd_count() {
45 lock.AssertAcquired();
46 --ashmem_fd_count_;
47 }
48
49 void increment_ahmem_fd_count() {
50 lock.AssertAcquired();
51 ++ashmem_fd_count_;
52 }
53
54 private:
55 static int GetSoftFDLimit() {
56 struct rlimit limit_info;
57 if (getrlimit(RLIMIT_NOFILE, &limit_info) != 0)
58 return 128;
59 // Allow 25% of file descriptor capacity for ashmem.
60 return limit_info.rlim_cur / 4;
61 }
62
63 int ashmem_fd_count_;
64 };
65
66 LazyInstance<GlobalContext>::Leaky g_context = LAZY_INSTANCE_INITIALIZER;
67
68 class DiscardableMemoryAndroid : public DiscardableMemory {
69 public:
70 DiscardableMemoryAndroid(int fd, void* address, size_t size)
71 : fd_(fd),
72 memory_(address),
73 size_(size) {
74 DCHECK_GE(fd_, 0);
75 DCHECK(memory_);
76 }
77
78 virtual ~DiscardableMemoryAndroid() {
79 internal::DeleteAshmemRegion(fd_, size_, memory_);
80 }
81
82 // DiscardableMemory:
83 virtual size_t Size() const OVERRIDE {
84 return size_;
85 }
86
87 virtual LockDiscardableMemoryStatus Lock() OVERRIDE {
88 return internal::LockAshmemRegion(fd_, 0, size_, memory_);
89 }
90
91 virtual void Unlock() OVERRIDE {
92 internal::UnlockAshmemRegion(fd_, 0, size_, memory_);
93 }
94
95 virtual void* Memory() const OVERRIDE {
96 return memory_;
97 }
98
99 private:
100 const int fd_;
101 void* const memory_;
102 const size_t size_;
103
104 DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryAndroid);
105 };
106
107 int GetCurrentNumberOfAshmemFDs() {
108 AutoLock lock(g_context.Get().lock);
109 return g_context.Get().ashmem_fd_count();
110 }
111
112 } // namespace
113
114 namespace internal {
115
116 size_t AlignToNextPage(size_t size) {
117 const size_t kPageSize = 4096;
118 const size_t mask = ~(kPageSize - 1);
119 return (size + kPageSize - 1) & mask;
120 }
32 121
33 bool CreateAshmemRegion(const char* name, 122 bool CreateAshmemRegion(const char* name,
34 size_t size, 123 size_t size,
35 int* out_fd, 124 int* out_fd,
36 void** out_address) { 125 void** out_address) {
37 base::AutoLock lock(g_discardable_memory_lock.Get()); 126 AutoLock lock(g_context.Get().lock);
38 if (g_num_discardable_memory + 1 > kDiscardableMemoryNumLimit) 127 if (g_context.Get().ashmem_fd_count() + 1 > g_context.Get().ashmem_fd_limit)
39 return false; 128 return false;
40 int fd = ashmem_create_region(name, size); 129 int fd = ashmem_create_region(name, size);
41 if (fd < 0) { 130 if (fd < 0) {
42 DLOG(ERROR) << "ashmem_create_region() failed"; 131 DLOG(ERROR) << "ashmem_create_region() failed";
43 return false; 132 return false;
44 } 133 }
45 file_util::ScopedFD fd_closer(&fd); 134 file_util::ScopedFD fd_closer(&fd);
46 135
47 const int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE); 136 const int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
48 if (err < 0) { 137 if (err < 0) {
49 DLOG(ERROR) << "Error " << err << " when setting protection of ashmem"; 138 DLOG(ERROR) << "Error " << err << " when setting protection of ashmem";
50 return false; 139 return false;
51 } 140 }
52 141
53 // There is a problem using MAP_PRIVATE here. As we are constantly calling 142 // There is a problem using MAP_PRIVATE here. As we are constantly calling
54 // Lock() and Unlock(), data could get lost if they are not written to the 143 // Lock() and Unlock(), data could get lost if they are not written to the
55 // underlying file when Unlock() gets called. 144 // underlying file when Unlock() gets called.
56 void* const address = mmap( 145 void* const address = mmap(
57 NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 146 NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
58 if (address == MAP_FAILED) { 147 if (address == MAP_FAILED) {
59 DPLOG(ERROR) << "Failed to map memory."; 148 DPLOG(ERROR) << "Failed to map memory.";
60 return false; 149 return false;
61 } 150 }
62 151
63 ignore_result(fd_closer.release()); 152 ignore_result(fd_closer.release());
64 ++g_num_discardable_memory; 153 g_context.Get().increment_ahmem_fd_count();
65 *out_fd = fd; 154 *out_fd = fd;
66 *out_address = address; 155 *out_address = address;
67 return true; 156 return true;
68 } 157 }
69 158
70 bool DeleteAshmemRegion(int fd, size_t size, void* address) { 159 bool DeleteAshmemRegion(int fd, size_t size, void* address) {
71 base::AutoLock lock(g_discardable_memory_lock.Get()); 160 AutoLock lock(g_context.Get().lock);
72 --g_num_discardable_memory; 161 g_context.Get().decrement_ahmem_fd_count();
73 if (munmap(address, size) == -1) { 162 if (munmap(address, size) == -1) {
74 DPLOG(ERROR) << "Failed to unmap memory."; 163 DPLOG(ERROR) << "Failed to unmap memory.";
75 close(fd); 164 close(fd);
76 return false; 165 return false;
77 } 166 }
78 return close(fd) == 0; 167 return close(fd) == 0;
79 } 168 }
80 169
81 LockDiscardableMemoryStatus LockAshmemRegion(int fd, 170 LockDiscardableMemoryStatus LockAshmemRegion(int fd,
82 size_t off, 171 size_t off,
83 size_t size, 172 size_t size,
84 const void* address) { 173 const void* address) {
85 const int result = ashmem_pin_region(fd, off, size); 174 const int result = ashmem_pin_region(fd, off, size);
86 DCHECK_EQ(0, mprotect(address, size, PROT_READ | PROT_WRITE)); 175 DCHECK_EQ(0, mprotect(address, size, PROT_READ | PROT_WRITE));
87 return result == ASHMEM_WAS_PURGED ? 176 return result == ASHMEM_WAS_PURGED ?
88 DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS; 177 DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS;
89 } 178 }
90 179
91 bool UnlockAshmemRegion(int fd, size_t off, size_t size, const void* address) { 180 bool UnlockAshmemRegion(int fd, size_t off, size_t size, const void* address) {
92 const int failed = ashmem_unpin_region(fd, off, size); 181 const int failed = ashmem_unpin_region(fd, off, size);
93 if (failed) 182 if (failed)
94 DLOG(ERROR) << "Failed to unpin memory."; 183 DLOG(ERROR) << "Failed to unpin memory.";
95 // This allows us to catch accesses to unlocked memory. 184 // This allows us to catch accesses to unlocked memory.
96 DCHECK_EQ(0, mprotect(address, size, PROT_NONE)); 185 DCHECK_EQ(0, mprotect(address, size, PROT_NONE));
97 return !failed; 186 return !failed;
98 } 187 }
99 188
100 class DiscardableMemoryAndroid : public DiscardableMemory { 189 } // namespace internal
101 public:
102 DiscardableMemoryAndroid(int fd, void* address, size_t size)
103 : fd_(fd),
104 memory_(address),
105 size_(size) {
106 DCHECK_GE(fd_, 0);
107 DCHECK(memory_);
108 }
109
110 virtual ~DiscardableMemoryAndroid() {
111 DeleteAshmemRegion(fd_, size_, memory_);
112 }
113
114 // DiscardableMemory:
115 virtual LockDiscardableMemoryStatus Lock() OVERRIDE {
116 return LockAshmemRegion(fd_, 0, size_, memory_);
117 }
118
119 virtual void Unlock() OVERRIDE {
120 UnlockAshmemRegion(fd_, 0, size_, memory_);
121 }
122
123 virtual void* Memory() const OVERRIDE {
124 return memory_;
125 }
126
127 private:
128 const int fd_;
129 void* const memory_;
130 const size_t size_;
131
132 DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryAndroid);
133 };
134
135 } // namespace
136 190
137 // static 191 // static
138 bool DiscardableMemory::Supported() { 192 bool DiscardableMemory::Supported() {
139 return true; 193 return true;
140 } 194 }
141 195
142 // static 196 // static
143 scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory( 197 scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory(
144 size_t size) { 198 size_t size) {
199 GlobalContext* const global_context = g_context.Pointer();
200 // Allocation can happen in two ways:
201 // - Each client-requested allocation is backed by an individual ashmem
202 // region. This allows to delete ashmem regions individually by closing the
203 // ashmem file descriptor. This is the default path that is taken when file
204 // descriptor usage allows us to do so.
205 // - Allocations are performed by the global allocator when file descriptor
206 // usage gets too high. This still allows unpinning but does not allow
207 // deleting (i.e. releasing the physycal pages backing) individiual regions.
208 const bool use_allocator = GetCurrentNumberOfAshmemFDs() >
209 0.9 * global_context->ashmem_fd_limit;
210 if (use_allocator)
211 return global_context->allocator.Allocate(size);
145 // Pinning & unpinning works with page granularity therefore align the size 212 // Pinning & unpinning works with page granularity therefore align the size
146 // upfront. 213 // upfront.
147 const size_t kPageSize = 4096; 214 size = internal::AlignToNextPage(size);
148 const size_t mask = ~(kPageSize - 1);
149 size = (size + kPageSize - 1) & mask;
150 int fd; 215 int fd;
151 void* address; 216 void* address;
152 if (!CreateAshmemRegion("", size, &fd, &address)) 217 if (!internal::CreateAshmemRegion("", size, &fd, &address))
153 return scoped_ptr<DiscardableMemory>(); 218 return scoped_ptr<DiscardableMemory>();
154 return scoped_ptr<DiscardableMemory>( 219 return scoped_ptr<DiscardableMemory>(
155 new DiscardableMemoryAndroid(fd, address, size)); 220 new DiscardableMemoryAndroid(fd, address, size));
156 } 221 }
157 222
158 // static 223 // static
159 bool DiscardableMemory::PurgeForTestingSupported() { 224 bool DiscardableMemory::PurgeForTestingSupported() {
160 return false; 225 return false;
161 } 226 }
162 227
163 // static 228 // static
164 void DiscardableMemory::PurgeForTesting() { 229 void DiscardableMemory::PurgeForTesting() {
165 NOTIMPLEMENTED(); 230 NOTIMPLEMENTED();
166 } 231 }
167 232
168 } // namespace base 233 } // namespace base
OLDNEW
« base/memory/discardable_memory_android.h ('K') | « base/memory/discardable_memory_android.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698