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

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: Address reviewers' comments 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"
15 #include "base/posix/eintr_wrapper.h" 18 #include "base/posix/eintr_wrapper.h"
16 #include "base/synchronization/lock.h" 19 #include "base/synchronization/lock.h"
17 #include "third_party/ashmem/ashmem.h" 20 #include "third_party/ashmem/ashmem.h"
18 21
19 namespace base { 22 namespace base {
20 namespace { 23 namespace {
21 24
22 // Protects |g_num_discardable_memory| below. 25 struct GlobalContext {
23 base::LazyInstance<base::Lock>::Leaky g_discardable_memory_lock = 26 GlobalContext() : ashmem_fd_limit(GetSoftFDLimit()), ashmem_fd_count_(0) {}
24 LAZY_INSTANCE_INITIALIZER;
25 27
26 // Total number of discardable memory in the process. 28 const int ashmem_fd_limit;
27 int g_num_discardable_memory = 0; 29 base::Lock lock;
28 30
29 // Upper limit on the number of discardable memory to avoid hitting file 31 int ashmem_fd_count() const {
30 // descriptor limit. 32 lock.AssertAcquired();
31 const int kDiscardableMemoryNumLimit = 128; 33 return ashmem_fd_count_;
34 }
35
36 void decrement_ahmem_fd_count() {
37 lock.AssertAcquired();
38 --ashmem_fd_count_;
39 }
40
41 void increment_ahmem_fd_count() {
42 lock.AssertAcquired();
43 ++ashmem_fd_count_;
44 }
45
46 private:
47 static int GetSoftFDLimit() {
48 struct rlimit limit_info;
49 if (getrlimit(RLIMIT_NOFILE, &limit_info) != 0)
50 return 128;
51 // Allow 25% of file descriptor capacity for ashmem.
52 return limit_info.rlim_cur / 4;
53 }
54
55 int ashmem_fd_count_;
56 };
57
58 base::LazyInstance<GlobalContext>::Leaky g_context = LAZY_INSTANCE_INITIALIZER;
59
60 class DiscardableMemoryAndroid : public DiscardableMemory {
61 public:
62 DiscardableMemoryAndroid(int fd, void* address, size_t size)
63 : fd_(fd),
64 memory_(address),
65 size_(size) {
66 DCHECK_GE(fd_, 0);
67 DCHECK(memory_);
68 }
69
70 virtual ~DiscardableMemoryAndroid() {
71 internal::DeleteAshmemRegion(fd_, size_, memory_);
72 }
73
74 // DiscardableMemory:
75 virtual size_t Size() const OVERRIDE {
76 return size_;
77 }
78
79 virtual LockDiscardableMemoryStatus Lock() OVERRIDE {
80 return internal::LockAshmemRegion(fd_, 0, size_, memory_);
81 }
82
83 virtual void Unlock() OVERRIDE {
84 internal::UnlockAshmemRegion(fd_, 0, size_, memory_);
85 }
86
87 virtual void* Memory() const OVERRIDE {
88 return memory_;
89 }
90
91 private:
92 const int fd_;
93 void* const memory_;
94 const size_t size_;
95
96 DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryAndroid);
97 };
98
99 } // namespace
100
101 namespace internal {
102
103 size_t PageAlign(size_t size) {
pasko 2013/10/22 20:14:29 nit: naming: this is not just aligning to page bou
104 const size_t kPageSize = 4096;
105 const size_t mask = ~(kPageSize - 1);
106 return (size + kPageSize - 1) & mask;
107 }
32 108
33 bool CreateAshmemRegion(const char* name, 109 bool CreateAshmemRegion(const char* name,
34 size_t size, 110 size_t size,
35 int* out_fd, 111 int* out_fd,
36 void** out_address) { 112 void** out_address) {
37 base::AutoLock lock(g_discardable_memory_lock.Get()); 113 base::AutoLock lock(g_context.Get().lock);
38 if (g_num_discardable_memory + 1 > kDiscardableMemoryNumLimit) 114 if (g_context.Get().ashmem_fd_count() + 1 > g_context.Get().ashmem_fd_limit)
39 return false; 115 return false;
40 int fd = ashmem_create_region(name, size); 116 int fd = ashmem_create_region(name, size);
41 if (fd < 0) { 117 if (fd < 0) {
42 DLOG(ERROR) << "ashmem_create_region() failed"; 118 DLOG(ERROR) << "ashmem_create_region() failed";
43 return false; 119 return false;
44 } 120 }
45 file_util::ScopedFD fd_closer(&fd); 121 file_util::ScopedFD fd_closer(&fd);
46 122
47 const int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE); 123 const int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
48 if (err < 0) { 124 if (err < 0) {
49 DLOG(ERROR) << "Error " << err << " when setting protection of ashmem"; 125 DLOG(ERROR) << "Error " << err << " when setting protection of ashmem";
50 return false; 126 return false;
51 } 127 }
52 128
53 // There is a problem using MAP_PRIVATE here. As we are constantly calling 129 // 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 130 // Lock() and Unlock(), data could get lost if they are not written to the
55 // underlying file when Unlock() gets called. 131 // underlying file when Unlock() gets called.
56 void* const address = mmap( 132 void* const address = mmap(
57 NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 133 NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
58 if (address == MAP_FAILED) { 134 if (address == MAP_FAILED) {
59 DPLOG(ERROR) << "Failed to map memory."; 135 DPLOG(ERROR) << "Failed to map memory.";
60 return false; 136 return false;
61 } 137 }
62 138
63 ignore_result(fd_closer.release()); 139 ignore_result(fd_closer.release());
64 ++g_num_discardable_memory; 140 g_context.Get().increment_ahmem_fd_count();
65 *out_fd = fd; 141 *out_fd = fd;
66 *out_address = address; 142 *out_address = address;
67 return true; 143 return true;
68 } 144 }
69 145
70 bool DeleteAshmemRegion(int fd, size_t size, void* address) { 146 bool DeleteAshmemRegion(int fd, size_t size, void* address) {
71 base::AutoLock lock(g_discardable_memory_lock.Get()); 147 base::AutoLock lock(g_context.Get().lock);
72 --g_num_discardable_memory; 148 g_context.Get().decrement_ahmem_fd_count();
73 if (munmap(address, size) == -1) { 149 if (munmap(address, size) == -1) {
74 DPLOG(ERROR) << "Failed to unmap memory."; 150 DPLOG(ERROR) << "Failed to unmap memory.";
75 close(fd); 151 close(fd);
76 return false; 152 return false;
77 } 153 }
78 return close(fd) == 0; 154 return close(fd) == 0;
79 } 155 }
80 156
81 LockDiscardableMemoryStatus LockAshmemRegion(int fd, 157 LockDiscardableMemoryStatus LockAshmemRegion(int fd,
82 size_t off, 158 size_t off,
83 size_t size, 159 size_t size,
84 const void* address) { 160 const void* address) {
85 const int result = ashmem_pin_region(fd, off, size); 161 const int result = ashmem_pin_region(fd, off, size);
86 DCHECK_EQ(0, mprotect(address, size, PROT_READ | PROT_WRITE)); 162 DCHECK_EQ(0, mprotect(address, size, PROT_READ | PROT_WRITE));
87 return result == ASHMEM_WAS_PURGED ? 163 return result == ASHMEM_WAS_PURGED ?
88 DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS; 164 DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS;
89 } 165 }
90 166
91 bool UnlockAshmemRegion(int fd, size_t off, size_t size, const void* address) { 167 bool UnlockAshmemRegion(int fd, size_t off, size_t size, const void* address) {
92 const int failed = ashmem_unpin_region(fd, off, size); 168 const int failed = ashmem_unpin_region(fd, off, size);
93 if (failed) 169 if (failed)
94 DLOG(ERROR) << "Failed to unpin memory."; 170 DLOG(ERROR) << "Failed to unpin memory.";
95 // This allows us to catch accesses to unlocked memory. 171 // This allows us to catch accesses to unlocked memory.
96 DCHECK_EQ(0, mprotect(address, size, PROT_NONE)); 172 DCHECK_EQ(0, mprotect(address, size, PROT_NONE));
97 return !failed; 173 return !failed;
98 } 174 }
99 175
100 class DiscardableMemoryAndroid : public DiscardableMemory { 176 int GetAshmemFDLimit() {
101 public: 177 return g_context.Get().ashmem_fd_limit;
102 DiscardableMemoryAndroid(int fd, void* address, size_t size) 178 }
103 : fd_(fd),
104 memory_(address),
105 size_(size) {
106 DCHECK_GE(fd_, 0);
107 DCHECK(memory_);
108 }
109 179
110 virtual ~DiscardableMemoryAndroid() { 180 int GetCurrentNumberOfAshmemFDs() {
111 DeleteAshmemRegion(fd_, size_, memory_); 181 base::AutoLock lock(g_context.Get().lock);
112 } 182 return g_context.Get().ashmem_fd_count();
183 }
113 184
114 // DiscardableMemory: 185 } // namespace internal
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 186
137 // static 187 // static
138 bool DiscardableMemory::Supported() { 188 bool DiscardableMemory::Supported() {
139 return true; 189 return true;
140 } 190 }
141 191
142 // static 192 // static
143 scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory( 193 scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory(
144 size_t size) { 194 size_t size) {
145 // Pinning & unpinning works with page granularity therefore align the size 195 // Pinning & unpinning works with page granularity therefore align the size
146 // upfront. 196 // upfront.
147 const size_t kPageSize = 4096; 197 size = internal::PageAlign(size);
148 const size_t mask = ~(kPageSize - 1);
149 size = (size + kPageSize - 1) & mask;
150 int fd; 198 int fd;
151 void* address; 199 void* address;
152 if (!CreateAshmemRegion("", size, &fd, &address)) 200 if (!internal::CreateAshmemRegion("", size, &fd, &address))
153 return scoped_ptr<DiscardableMemory>(); 201 return scoped_ptr<DiscardableMemory>();
154 return scoped_ptr<DiscardableMemory>( 202 return scoped_ptr<DiscardableMemory>(
155 new DiscardableMemoryAndroid(fd, address, size)); 203 new DiscardableMemoryAndroid(fd, address, size));
156 } 204 }
157 205
158 // static 206 // static
159 bool DiscardableMemory::PurgeForTestingSupported() { 207 bool DiscardableMemory::PurgeForTestingSupported() {
160 return false; 208 return false;
161 } 209 }
162 210
163 // static 211 // static
164 void DiscardableMemory::PurgeForTesting() { 212 void DiscardableMemory::PurgeForTesting() {
165 NOTIMPLEMENTED(); 213 NOTIMPLEMENTED();
166 } 214 }
167 215
168 } // namespace base 216 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698