| 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 |