OLD | NEW |
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.h" |
6 | 6 |
7 #include <sys/mman.h> | 7 #include <sys/mman.h> |
8 #include <unistd.h> | 8 #include <unistd.h> |
9 | 9 |
10 #include "base/lazy_instance.h" | 10 #include "base/lazy_instance.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/posix/eintr_wrapper.h" | 12 #include "base/posix/eintr_wrapper.h" |
13 #include "base/synchronization/lock.h" | 13 #include "base/synchronization/lock.h" |
14 #include "third_party/ashmem/ashmem.h" | 14 #include "third_party/ashmem/ashmem.h" |
15 | 15 |
| 16 namespace base { |
| 17 |
16 namespace { | 18 namespace { |
17 | 19 |
18 base::LazyInstance<base::Lock>::Leaky g_discardable_memory_lock = | 20 base::LazyInstance<base::Lock>::Leaky g_discardable_memory_lock = |
19 LAZY_INSTANCE_INITIALIZER; | 21 LAZY_INSTANCE_INITIALIZER; |
20 | 22 |
21 // Total number of discardable memory in the process. | 23 // Total number of discardable memory in the process. |
22 int g_num_discardable_memory = 0; | 24 int g_num_discardable_memory = 0; |
23 | 25 |
24 // Upper limit on the number of discardable memory to avoid hitting file | 26 // Upper limit on the number of discardable memory to avoid hitting file |
25 // descriptor limit. | 27 // descriptor limit. |
26 const int kDiscardableMemoryNumLimit = 128; | 28 const int kDiscardableMemoryNumLimit = 128; |
27 | 29 |
28 } | 30 class DiscardableMemoryAndroid : public DiscardableMemory { |
| 31 public: |
| 32 DiscardableMemoryAndroid() |
| 33 : memory_(NULL), |
| 34 size_(0), |
| 35 is_locked_(false), |
| 36 fd_(-1) { |
| 37 } |
29 | 38 |
30 namespace base { | 39 virtual ~DiscardableMemoryAndroid() { |
| 40 if (is_locked_) |
| 41 Unlock(); |
| 42 // If fd_ is smaller than 0, initialization must have failed and |
| 43 // g_num_discardable_memory is not incremented by the caller. |
| 44 if (fd_ < 0) |
| 45 return; |
| 46 HANDLE_EINTR(close(fd_)); |
| 47 fd_ = -1; |
| 48 ReleaseFileDescriptor(); |
| 49 } |
31 | 50 |
32 // static | 51 virtual bool InitializeAndLock(size_t size) OVERRIDE { |
33 bool DiscardableMemory::Supported() { | 52 // When this function returns true, fd_ should be larger or equal than 0 |
34 return true; | 53 // and g_num_discardable_memory is incremented by 1. Otherwise, fd_ |
35 } | 54 // is less than 0 and g_num_discardable_memory is not incremented by |
| 55 // the caller. |
| 56 DCHECK_EQ(fd_, -1); |
| 57 DCHECK(!memory_); |
| 58 if (!ReserveFileDescriptor()) |
| 59 return false; |
36 | 60 |
37 DiscardableMemory::~DiscardableMemory() { | 61 size_ = size; |
38 if (is_locked_) | 62 fd_ = ashmem_create_region("", size); |
39 Unlock(); | |
40 // If fd_ is smaller than 0, initialization must have failed and | |
41 // g_num_discardable_memory is not incremented by the caller. | |
42 if (fd_ < 0) | |
43 return; | |
44 HANDLE_EINTR(close(fd_)); | |
45 fd_ = -1; | |
46 ReleaseFileDescriptor(); | |
47 } | |
48 | 63 |
49 bool DiscardableMemory::ReserveFileDescriptor() { | 64 if (fd_ < 0) { |
50 base::AutoLock lock(g_discardable_memory_lock.Get()); | 65 DLOG(ERROR) << "ashmem_create_region() failed"; |
51 if (g_num_discardable_memory < kDiscardableMemoryNumLimit) { | 66 ReleaseFileDescriptor(); |
52 ++g_num_discardable_memory; | 67 return false; |
| 68 } |
| 69 |
| 70 int err = ashmem_set_prot_region(fd_, PROT_READ | PROT_WRITE); |
| 71 if (err < 0) { |
| 72 DLOG(ERROR) << "Error " << err << " when setting protection of ashmem"; |
| 73 HANDLE_EINTR(close(fd_)); |
| 74 fd_ = -1; |
| 75 ReleaseFileDescriptor(); |
| 76 return false; |
| 77 } |
| 78 |
| 79 if (!Map()) { |
| 80 // Close the file descriptor in case of any initialization errors. |
| 81 HANDLE_EINTR(close(fd_)); |
| 82 fd_ = -1; |
| 83 ReleaseFileDescriptor(); |
| 84 return false; |
| 85 } |
| 86 |
| 87 is_locked_ = true; |
53 return true; | 88 return true; |
54 } | 89 } |
55 return false; | |
56 } | |
57 | 90 |
58 void DiscardableMemory::ReleaseFileDescriptor() { | 91 virtual LockDiscardableMemoryStatus Lock() OVERRIDE { |
59 base::AutoLock lock(g_discardable_memory_lock.Get()); | 92 DCHECK_NE(fd_, -1); |
60 --g_num_discardable_memory; | 93 DCHECK(!is_locked_); |
61 DCHECK_LE(0, g_num_discardable_memory); | |
62 } | |
63 | 94 |
64 bool DiscardableMemory::InitializeAndLock(size_t size) { | 95 bool purged = false; |
65 // When this function returns true, fd_ should be larger or equal than 0 | 96 if (ashmem_pin_region(fd_, 0, 0) == ASHMEM_WAS_PURGED) |
66 // and g_num_discardable_memory is incremented by 1. Otherwise, fd_ | 97 purged = true; |
67 // is less than 0 and g_num_discardable_memory is not incremented by | |
68 // the caller. | |
69 DCHECK_EQ(fd_, -1); | |
70 DCHECK(!memory_); | |
71 if (!ReserveFileDescriptor()) | |
72 return false; | |
73 | 98 |
74 size_ = size; | 99 if (!Map()) |
75 fd_ = ashmem_create_region("", size); | 100 return DISCARDABLE_MEMORY_FAILED; |
76 | 101 |
77 if (fd_ < 0) { | 102 is_locked_ = true; |
78 DLOG(ERROR) << "ashmem_create_region() failed"; | 103 return purged ? DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS; |
79 ReleaseFileDescriptor(); | 104 } |
| 105 |
| 106 virtual void Unlock() OVERRIDE { |
| 107 DCHECK_GE(fd_, 0); |
| 108 DCHECK(is_locked_); |
| 109 |
| 110 Unmap(); |
| 111 if (ashmem_unpin_region(fd_, 0, 0)) |
| 112 DLOG(ERROR) << "Failed to unpin memory."; |
| 113 is_locked_ = false; |
| 114 } |
| 115 |
| 116 virtual void* Memory() OVERRIDE { |
| 117 return memory_; |
| 118 } |
| 119 |
| 120 bool ReserveFileDescriptor() { |
| 121 base::AutoLock lock(g_discardable_memory_lock.Get()); |
| 122 if (g_num_discardable_memory < kDiscardableMemoryNumLimit) { |
| 123 ++g_num_discardable_memory; |
| 124 return true; |
| 125 } |
80 return false; | 126 return false; |
81 } | 127 } |
82 | 128 |
83 int err = ashmem_set_prot_region(fd_, PROT_READ | PROT_WRITE); | 129 void ReleaseFileDescriptor() { |
84 if (err < 0) { | 130 base::AutoLock lock(g_discardable_memory_lock.Get()); |
85 DLOG(ERROR) << "Error " << err << " when setting protection of ashmem"; | 131 --g_num_discardable_memory; |
86 HANDLE_EINTR(close(fd_)); | 132 DCHECK_LE(0, g_num_discardable_memory); |
87 fd_ = -1; | 133 } |
88 ReleaseFileDescriptor(); | 134 |
| 135 bool Map() { |
| 136 DCHECK(!memory_); |
| 137 // There is a problem using MAP_PRIVATE here. As we are constantly calling |
| 138 // Lock() and Unlock(), data could get lost if they are not written to the |
| 139 // underlying file when Unlock() gets called. |
| 140 memory_ = mmap(NULL, size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0); |
| 141 if (memory_ == (void*)-1) { |
| 142 DPLOG(ERROR) << "Failed to map memory."; |
| 143 memory_ = NULL; |
| 144 if (ashmem_unpin_region(fd_, 0, 0)) |
| 145 DLOG(ERROR) << "Failed to unpin memory."; |
| 146 return false; |
| 147 } |
| 148 return true; |
| 149 } |
| 150 |
| 151 void Unmap() { |
| 152 DCHECK(memory_); |
| 153 |
| 154 if (-1 == munmap(memory_, size_)) |
| 155 DPLOG(ERROR) << "Failed to unmap memory."; |
| 156 |
| 157 memory_ = NULL; |
| 158 } |
| 159 |
| 160 private: |
| 161 void* memory_; |
| 162 size_t size_; |
| 163 bool is_locked_; |
| 164 int fd_; |
| 165 |
| 166 DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryAndroid); |
| 167 }; |
| 168 |
| 169 class BASE_EXPORT DiscardableMemoryProviderAndroid |
| 170 : public DiscardableMemoryProvider { |
| 171 public: |
| 172 virtual DiscardableMemory* CreateDiscardableMemory() OVERRIDE { |
| 173 return new DiscardableMemoryAndroid; |
| 174 } |
| 175 |
| 176 virtual bool PurgeForTestingSupported() const OVERRIDE { |
89 return false; | 177 return false; |
90 } | 178 } |
91 | 179 |
92 if (!Map()) { | 180 virtual void PurgeForTesting() OVERRIDE { |
93 // Close the file descriptor in case of any initialization errors. | 181 NOTIMPLEMENTED(); |
94 HANDLE_EINTR(close(fd_)); | |
95 fd_ = -1; | |
96 ReleaseFileDescriptor(); | |
97 return false; | |
98 } | 182 } |
| 183 }; |
99 | 184 |
100 is_locked_ = true; | 185 } // namespace |
101 return true; | |
102 } | |
103 | |
104 LockDiscardableMemoryStatus DiscardableMemory::Lock() { | |
105 DCHECK_NE(fd_, -1); | |
106 DCHECK(!is_locked_); | |
107 | |
108 bool purged = false; | |
109 if (ashmem_pin_region(fd_, 0, 0) == ASHMEM_WAS_PURGED) | |
110 purged = true; | |
111 | |
112 if (!Map()) | |
113 return DISCARDABLE_MEMORY_FAILED; | |
114 | |
115 is_locked_ = true; | |
116 return purged ? DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS; | |
117 } | |
118 | |
119 void DiscardableMemory::Unlock() { | |
120 DCHECK_GE(fd_, 0); | |
121 DCHECK(is_locked_); | |
122 | |
123 Unmap(); | |
124 if (ashmem_unpin_region(fd_, 0, 0)) | |
125 DLOG(ERROR) << "Failed to unpin memory."; | |
126 is_locked_ = false; | |
127 } | |
128 | |
129 bool DiscardableMemory::Map() { | |
130 DCHECK(!memory_); | |
131 // There is a problem using MAP_PRIVATE here. As we are constantly calling | |
132 // Lock() and Unlock(), data could get lost if they are not written to the | |
133 // underlying file when Unlock() gets called. | |
134 memory_ = mmap(NULL, size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0); | |
135 if (memory_ == (void*)-1) { | |
136 DPLOG(ERROR) << "Failed to map memory."; | |
137 memory_ = NULL; | |
138 if (ashmem_unpin_region(fd_, 0, 0)) | |
139 DLOG(ERROR) << "Failed to unpin memory."; | |
140 return false; | |
141 } | |
142 return true; | |
143 } | |
144 | |
145 void DiscardableMemory::Unmap() { | |
146 DCHECK(memory_); | |
147 | |
148 if (-1 == munmap(memory_, size_)) | |
149 DPLOG(ERROR) << "Failed to unmap memory."; | |
150 | |
151 memory_ = NULL; | |
152 } | |
153 | 186 |
154 // static | 187 // static |
155 bool DiscardableMemory::PurgeForTestingSupported() { | 188 DiscardableMemoryProvider* DiscardableMemoryProvider::GetInstance() { |
156 return false; | 189 base::Singleton<DiscardableMemoryProviderAndroid>::GetInstance(); |
157 } | |
158 | |
159 // static | |
160 void DiscardableMemory::PurgeForTesting() { | |
161 NOTIMPLEMENTED(); | |
162 } | 190 } |
163 | 191 |
164 } // namespace base | 192 } // namespace base |
OLD | NEW |