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..b88a13611b9c7a0488a4fd6a76751201e4b1bdf5 100644 |
--- a/base/memory/discardable_memory_android.cc |
+++ b/base/memory/discardable_memory_android.cc |
@@ -7,14 +7,19 @@ |
#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,130 +30,129 @@ int g_num_discardable_memory = 0; |
// descriptor limit. |
const int kDiscardableMemoryNumLimit = 128; |
-} |
- |
-namespace base { |
- |
-// 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(); |
-} |
- |
-bool DiscardableMemory::ReserveFileDescriptor() { |
+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 < kDiscardableMemoryNumLimit) { |
- ++g_num_discardable_memory; |
- return true; |
- } |
- return false; |
-} |
- |
-void DiscardableMemory::ReleaseFileDescriptor() { |
- base::AutoLock lock(g_discardable_memory_lock.Get()); |
- --g_num_discardable_memory; |
- DCHECK_LE(0, g_num_discardable_memory); |
-} |
- |
-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()) |
+ if (g_num_discardable_memory + 1 > kDiscardableMemoryNumLimit) |
return false; |
- |
- size_ = size; |
- fd_ = ashmem_create_region("", size); |
- |
- if (fd_ < 0) { |
+ 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(); |
+ // 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. |
+ void* const address = mmap( |
+ NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); |
+ if (address == MAP_FAILED) { |
+ DPLOG(ERROR) << "Failed to map memory."; |
return false; |
} |
- is_locked_ = true; |
+ ignore_result(fd_closer.release()); |
+ ++g_num_discardable_memory; |
+ *out_fd = fd; |
+ *out_address = address; |
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; |
+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."; |
+ close(fd); |
+ return false; |
+ } |
+ return close(fd) == 0; |
} |
-void DiscardableMemory::Unlock() { |
- DCHECK_GE(fd_, 0); |
- DCHECK(is_locked_); |
+LockDiscardableMemoryStatus LockAshmemRegion(int fd, |
+ size_t off, |
+ size_t size, |
+ const void* address) { |
+ const int result = ashmem_pin_region(fd, off, size); |
+ DCHECK_EQ(0, mprotect(address, size, PROT_READ | PROT_WRITE)); |
+ return result == ASHMEM_WAS_PURGED ? |
+ DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS; |
+} |
- Unmap(); |
- if (ashmem_unpin_region(fd_, 0, 0)) |
+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."; |
- is_locked_ = false; |
+ // This allows us to catch accesses to unlocked memory. |
+ DCHECK_EQ(0, mprotect(address, size, PROT_NONE)); |
+ return !failed; |
} |
-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) { |
- DPLOG(ERROR) << "Failed to map memory."; |
- memory_ = NULL; |
- if (ashmem_unpin_region(fd_, 0, 0)) |
- DLOG(ERROR) << "Failed to unpin memory."; |
- return false; |
+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_); |
} |
- return true; |
-} |
-void DiscardableMemory::Unmap() { |
- DCHECK(memory_); |
+ virtual ~DiscardableMemoryAndroid() { |
+ DeleteAshmemRegion(fd_, size_, memory_); |
+ } |
- if (-1 == munmap(memory_, size_)) |
- DPLOG(ERROR) << "Failed to unmap memory."; |
+ // DiscardableMemory: |
+ virtual LockDiscardableMemoryStatus Lock() OVERRIDE { |
+ return LockAshmemRegion(fd_, 0, size_, memory_); |
+ } |
+ |
+ virtual void Unlock() OVERRIDE { |
+ UnlockAshmemRegion(fd_, 0, size_, memory_); |
+ } |
+ |
+ virtual void* Memory() const OVERRIDE { |
+ return memory_; |
+ } |
+ |
+ private: |
+ const int fd_; |
+ void* const memory_; |
+ const size_t size_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryAndroid); |
+}; |
- memory_ = NULL; |
+} // namespace |
+ |
+// static |
+bool DiscardableMemory::Supported() { |
+ return true; |
+} |
+ |
+// static |
+scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory( |
+ size_t size) { |
+ // Pinning & unpinning works with page granularity therefore align the size |
+ // upfront. |
+ const size_t kPageSize = 4096; |
+ const size_t mask = ~(kPageSize - 1); |
+ size = (size + kPageSize - 1) & mask; |
+ int fd; |
+ void* address; |
+ if (!CreateAshmemRegion("", size, &fd, &address)) |
+ return scoped_ptr<DiscardableMemory>(); |
+ return scoped_ptr<DiscardableMemory>( |
+ new DiscardableMemoryAndroid(fd, address, size)); |
} |
// static |