Chromium Code Reviews| Index: base/memory/discardable_memory_android.cc |
| diff --git a/base/memory/discardable_memory_android.cc b/base/memory/discardable_memory_android.cc |
| index 73a25ae419e897b7bf2f3f903edd931a1599be27..ffead39307e7a8c9a2a071a1179b30ed8e61b910 100644 |
| --- a/base/memory/discardable_memory_android.cc |
| +++ b/base/memory/discardable_memory_android.cc |
| @@ -2,19 +2,24 @@ |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| -#include "base/memory/discardable_memory.h" |
| +#include "base/memory/discardable_memory_android.h" |
| #include <sys/mman.h> |
| #include <unistd.h> |
| +#include "base/basictypes.h" |
| +#include "base/compiler_specific.h" |
| +#include "base/file_util.h" |
| #include "base/lazy_instance.h" |
| #include "base/logging.h" |
| #include "base/posix/eintr_wrapper.h" |
| #include "base/synchronization/lock.h" |
| #include "third_party/ashmem/ashmem.h" |
| +namespace base { |
| namespace { |
| +// Protects |g_num_discardable_memory| below. |
| base::LazyInstance<base::Lock>::Leaky g_discardable_memory_lock = |
| LAZY_INSTANCE_INITIALIZER; |
| @@ -25,140 +30,141 @@ int g_num_discardable_memory = 0; |
| // descriptor limit. |
| const int kDiscardableMemoryNumLimit = 128; |
| -} |
| +class DiscardableMemoryAndroid : public DiscardableMemory { |
| + public: |
| + DiscardableMemoryAndroid(int fd, void* address, size_t size) |
| + : fd_(fd), |
| + memory_(address), |
| + size_(size) { |
| + DCHECK_GE(fd_, 0); |
| + DCHECK(memory_); |
| + } |
| -namespace base { |
| + virtual ~DiscardableMemoryAndroid() { |
| + internal::DeleteAshmemRegion(fd_, size_, memory_); |
| + } |
| + |
| + // DiscardableMemory: |
| + virtual LockDiscardableMemoryStatus Lock() OVERRIDE { |
| + return internal::LockAshmemRegion(fd_, 0, 0, memory_); |
| + } |
| + |
| + virtual void Unlock() OVERRIDE { |
| + internal::UnlockAshmemRegion(fd_, 0, 0, memory_); |
| + } |
| + |
| + virtual void* Memory() const OVERRIDE { |
| + return memory_; |
| + } |
| + |
| + private: |
| + const int fd_; |
| + void* const memory_; |
| + const size_t size_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryAndroid); |
| +}; |
| + |
| +} // namespace |
| // static |
| bool DiscardableMemory::Supported() { |
| return true; |
| } |
| -DiscardableMemory::~DiscardableMemory() { |
| - if (is_locked_) |
| - Unlock(); |
| - // If fd_ is smaller than 0, initialization must have failed and |
| - // g_num_discardable_memory is not incremented by the caller. |
| - if (fd_ < 0) |
| - return; |
| - HANDLE_EINTR(close(fd_)); |
| - fd_ = -1; |
| - ReleaseFileDescriptor(); |
| +// static |
| +scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory( |
| + size_t size) { |
| + int fd; |
| + void* address; |
| + if (!internal::CreateAshmemRegion("", size, &fd, &address)) |
| + return scoped_ptr<DiscardableMemory>(); |
| + return scoped_ptr<DiscardableMemory>( |
| + new DiscardableMemoryAndroid(fd, address, size)); |
| } |
| -bool DiscardableMemory::ReserveFileDescriptor() { |
| - base::AutoLock lock(g_discardable_memory_lock.Get()); |
| - if (g_num_discardable_memory < kDiscardableMemoryNumLimit) { |
| - ++g_num_discardable_memory; |
| - return true; |
| - } |
| +// static |
| +bool DiscardableMemory::PurgeForTestingSupported() { |
| return false; |
| } |
| -void DiscardableMemory::ReleaseFileDescriptor() { |
| - base::AutoLock lock(g_discardable_memory_lock.Get()); |
| - --g_num_discardable_memory; |
| - DCHECK_LE(0, g_num_discardable_memory); |
| +// static |
| +void DiscardableMemory::PurgeForTesting() { |
| + NOTIMPLEMENTED(); |
| } |
| -bool DiscardableMemory::InitializeAndLock(size_t size) { |
| - // When this function returns true, fd_ should be larger or equal than 0 |
| - // and g_num_discardable_memory is incremented by 1. Otherwise, fd_ |
| - // is less than 0 and g_num_discardable_memory is not incremented by |
| - // the caller. |
| - DCHECK_EQ(fd_, -1); |
| - DCHECK(!memory_); |
| - if (!ReserveFileDescriptor()) |
| - return false; |
| - |
| - size_ = size; |
| - fd_ = ashmem_create_region("", size); |
| +namespace internal { |
| - if (fd_ < 0) { |
| +bool CreateAshmemRegion(const char* name, |
| + size_t size, |
| + int* out_fd, |
| + void** out_address) { |
| + base::AutoLock lock(g_discardable_memory_lock.Get()); |
| + if (g_num_discardable_memory + 1 > kDiscardableMemoryNumLimit) |
| + return false; |
| + int fd = ashmem_create_region(name, size); |
| + if (fd < 0) { |
| DLOG(ERROR) << "ashmem_create_region() failed"; |
| - ReleaseFileDescriptor(); |
| return false; |
| } |
| + file_util::ScopedFD fd_closer(&fd); |
| - int err = ashmem_set_prot_region(fd_, PROT_READ | PROT_WRITE); |
| + const int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE); |
| if (err < 0) { |
| DLOG(ERROR) << "Error " << err << " when setting protection of ashmem"; |
| - HANDLE_EINTR(close(fd_)); |
| - fd_ = -1; |
| - ReleaseFileDescriptor(); |
| return false; |
| } |
| - if (!Map()) { |
| - // Close the file descriptor in case of any initialization errors. |
| - HANDLE_EINTR(close(fd_)); |
| - fd_ = -1; |
| - ReleaseFileDescriptor(); |
| - return false; |
| - } |
| - |
| - is_locked_ = true; |
| - return true; |
| -} |
| - |
| -LockDiscardableMemoryStatus DiscardableMemory::Lock() { |
| - DCHECK_NE(fd_, -1); |
| - DCHECK(!is_locked_); |
| - |
| - bool purged = false; |
| - if (ashmem_pin_region(fd_, 0, 0) == ASHMEM_WAS_PURGED) |
| - purged = true; |
| - |
| - if (!Map()) |
| - return DISCARDABLE_MEMORY_FAILED; |
| - |
| - is_locked_ = true; |
| - return purged ? DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS; |
| -} |
| - |
| -void DiscardableMemory::Unlock() { |
| - DCHECK_GE(fd_, 0); |
| - DCHECK(is_locked_); |
| - |
| - Unmap(); |
| - if (ashmem_unpin_region(fd_, 0, 0)) |
| - DLOG(ERROR) << "Failed to unpin memory."; |
| - is_locked_ = false; |
| -} |
| - |
| -bool DiscardableMemory::Map() { |
| - DCHECK(!memory_); |
| // There is a problem using MAP_PRIVATE here. As we are constantly calling |
| // Lock() and Unlock(), data could get lost if they are not written to the |
| // underlying file when Unlock() gets called. |
| - memory_ = mmap(NULL, size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0); |
| - if (memory_ == (void*)-1) { |
| + void* const address = mmap( |
| + NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); |
| + if (address == MAP_FAILED) { |
| DPLOG(ERROR) << "Failed to map memory."; |
| - memory_ = NULL; |
| - if (ashmem_unpin_region(fd_, 0, 0)) |
| - DLOG(ERROR) << "Failed to unpin memory."; |
| return false; |
| } |
| + |
| + ignore_result(fd_closer.release()); |
| + ++g_num_discardable_memory; |
| + *out_fd = fd; |
| + *out_address = address; |
| return true; |
| } |
| -void DiscardableMemory::Unmap() { |
| - DCHECK(memory_); |
| - |
| - if (-1 == munmap(memory_, size_)) |
| +bool DeleteAshmemRegion(int fd, size_t size, void* address) { |
| + base::AutoLock lock(g_discardable_memory_lock.Get()); |
| + --g_num_discardable_memory; |
| + if (munmap(address, size) == -1) { |
| DPLOG(ERROR) << "Failed to unmap memory."; |
| - |
| - memory_ = NULL; |
| + HANDLE_EINTR(close(fd)); |
|
willchan no longer on Chromium
2013/10/17 01:21:37
Please drop this HANDLE_EINTR: https://code.google
Philippe
2013/10/17 08:56:24
Thanks, I didn't know about this issue.
|
| + return false; |
| + } |
| + return HANDLE_EINTR(close(fd)) == 0; |
|
willchan no longer on Chromium
2013/10/17 01:21:37
Ditto
Philippe
2013/10/17 08:56:24
Done.
|
| } |
| -// static |
| -bool DiscardableMemory::PurgeForTestingSupported() { |
| - return false; |
| +LockDiscardableMemoryStatus LockAshmemRegion(int fd, |
| + size_t off, |
| + size_t size, |
| + const void* address) { |
| + const int result = ashmem_pin_region(fd, off, size); |
| +#if !defined(NDEBUG) |
|
willchan no longer on Chromium
2013/10/17 01:21:37
I don't get this. Why is this NDEBUG stuff needed?
Philippe
2013/10/17 08:56:24
Yeah, good point. I guess I added the DCHECK after
|
| + DCHECK_EQ(0, mprotect(address, size, PROT_READ | PROT_WRITE)); |
| +#endif |
| + return result == ASHMEM_WAS_PURGED ? |
| + DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS; |
| } |
| -// static |
| -void DiscardableMemory::PurgeForTesting() { |
| - NOTIMPLEMENTED(); |
| +bool UnlockAshmemRegion(int fd, size_t off, size_t size, const void* address) { |
| + const int failed = ashmem_unpin_region(fd, off, size); |
| + if (failed) |
| + DLOG(ERROR) << "Failed to unpin memory."; |
| +#if !defined(NDEBUG) |
| + // This allows us to catch accesses to unlocked memory. |
| + DCHECK_EQ(0, mprotect(address, size, PROT_NONE)); |
| +#endif |
| + return !failed; |
| } |
| +} // namespace internal |
| } // namespace base |