summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authorqinmin@chromium.org <qinmin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-22 01:24:26 +0000
committerqinmin@chromium.org <qinmin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-22 01:24:26 +0000
commitb8da8abc1a6f00c802c5774bd8c2b6e6cf03fff8 (patch)
tree8ebbca2f590f32c765218ef9bb2dabf7bb1d39d9 /base
parent4090144d876aba1977c1b0fb9a503a467dc3b62a (diff)
downloadchromium_src-b8da8abc1a6f00c802c5774bd8c2b6e6cf03fff8.zip
chromium_src-b8da8abc1a6f00c802c5774bd8c2b6e6cf03fff8.tar.gz
chromium_src-b8da8abc1a6f00c802c5774bd8c2b6e6cf03fff8.tar.bz2
Add base::DiscardableMemory class and implementation for android
This change adds the DiscardableMemory class to support features like ashmem. It should be mainly used for mobile devices where VM swap is not available, such as android. The DiscardableMemory is particularly helpful for caching large objects without worrying about OOM situation. It allows user to lock and unlock pages. When locked, the memory can be used as regular physical memory. When unlocked, the memory could be discarded by the kernel under memory pressure. Compared to relying on OOM signals to clean up the memory, DiscardableMemory is much simpler as the OS will taking care of the LRU algorithm and there is no need to implement a separate cleanup() call. However, there is no guarantee on which cached objects will be discarded. Android implementation is provided through ashmem. BUG=169597 Review URL: https://chromiumcodereview.appspot.com/12015002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@177964 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r--base/base.gyp1
-rw-r--r--base/base.gypi2
-rw-r--r--base/memory/discardable_memory.h91
-rw-r--r--base/memory/discardable_memory_android.cc115
-rw-r--r--base/memory/discardable_memory_unittest.cc44
5 files changed, 253 insertions, 0 deletions
diff --git a/base/base.gyp b/base/base.gyp
index 48e0f50..efb94ef 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -472,6 +472,7 @@
'mac/scoped_sending_event_unittest.mm',
'md5_unittest.cc',
'memory/aligned_memory_unittest.cc',
+ 'memory/discardable_memory_unittest.cc',
'memory/linked_ptr_unittest.cc',
'memory/ref_counted_memory_unittest.cc',
'memory/ref_counted_unittest.cc',
diff --git a/base/base.gypi b/base/base.gypi
index 06a4aca..d19581c 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -248,6 +248,8 @@
'mach_ipc_mac.mm',
'memory/aligned_memory.cc',
'memory/aligned_memory.h',
+ 'memory/discardable_memory_android.cc',
+ 'memory/discardable_memory.h',
'memory/linked_ptr.h',
'memory/manual_constructor.h',
'memory/raw_scoped_refptr_mismatch_checker.h',
diff --git a/base/memory/discardable_memory.h b/base/memory/discardable_memory.h
new file mode 100644
index 0000000..3cccb57
--- /dev/null
+++ b/base/memory/discardable_memory.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_H_
+#define BASE_MEMORY_DISCARDABLE_MEMORY_H_
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/synchronization/lock.h"
+
+namespace base {
+
+enum LockDiscardableMemoryStatus {
+ FAILED = -1,
+ PURGED = 0,
+ SUCCESS = 1
+};
+
+// Platform abstraction for discardable memory. The DiscardableMemory should
+// be mainly used for mobile devices where VM swap is not available, such as
+// android. It is particularly helpful for caching large objects without
+// worrying about OOM situation.
+// Discardable memory allows user to lock(pin) and unlock(unpin) pages so that
+// the allocated memory can be discarded by the kernel under memory pressure.
+// From http://lwn.net/Articles/452035/: "Pinned pages (the default) behave
+// like any anonymous memory. Unpinned pages are available to the kernel for
+// eviction during VM pressure. When repinning the pages, the return value
+// instructs user-space as to any eviction. In this manner, user-space processes
+// may implement caching and similar resource management that efficiently
+// integrates with kernel memory management."
+// Compared to relying on OOM signals to clean up the memory, DiscardableMemory
+// is much simpler as the OS will taking care of the LRU algorithm and there
+// is no need to implement a separate cleanup() call. However, there is no
+// guarantee which cached objects will be discarded.
+// Because of memory alignment, the actual locked discardable memory could be
+// larger than the requested memory size. It may not be very efficient for
+// small size allocations.
+class BASE_EXPORT DiscardableMemory {
+ public:
+ DiscardableMemory();
+
+ // If the discardable memory is locked, the destructor will unlock it.
+ // The opened file will also be closed after this.
+ ~DiscardableMemory();
+
+ // Check whether the system supports discardable memory.
+ static bool Supported() {
+#if defined(OS_ANDROID)
+ return true;
+#endif
+ return false;
+ }
+
+ // Initialize the DiscardableMemory object. On success, this function returns
+ // true and the memory is locked. This should only be called once.
+ bool InitializeAndLock(size_t size);
+
+ // Lock the memory so that it will not be purged by the system. Returns
+ // SUCCESS on success. Returns FAILED on error or PURGED if the memory is
+ // purged. Don't call this function in a nested fashion.
+ LockDiscardableMemoryStatus Lock() WARN_UNUSED_RESULT;
+
+ // Unlock the memory so that it can be purged by the system. Must be called
+ // after every successful lock call.
+ void Unlock();
+
+ // Return the memory address held by this object. When this object is locked,
+ // this call will return the address in caller's address space. Otherwise,
+ // this call returns NULL.
+ void* Memory() const;
+
+ private:
+ // Maps the discardable memory into the caller's address space.
+ // Returns true on success, false otherwise.
+ bool Map();
+
+ // Unmaps the discardable memory from the caller's address space.
+ void Unmap();
+
+ int fd_;
+ void* memory_;
+ size_t size_;
+ bool is_pinned_;
+
+ DISALLOW_COPY_AND_ASSIGN(DiscardableMemory);
+};
+
+} // namespace base
+
+#endif // BASE_MEMORY_DISCARDABLE_MEMORY_H_
diff --git a/base/memory/discardable_memory_android.cc b/base/memory/discardable_memory_android.cc
new file mode 100644
index 0000000..afc2a2c
--- /dev/null
+++ b/base/memory/discardable_memory_android.cc
@@ -0,0 +1,115 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// 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 <sys/mman.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "third_party/ashmem/ashmem.h"
+
+namespace base {
+
+DiscardableMemory::DiscardableMemory()
+ : fd_(-1),
+ memory_(NULL),
+ size_(0),
+ is_pinned_(false) {
+ DCHECK(Supported());
+}
+
+DiscardableMemory::~DiscardableMemory() {
+ if (is_pinned_)
+ Unlock();
+ if (fd_ < 0)
+ return;
+ HANDLE_EINTR(close(fd_));
+ fd_ = -1;
+}
+
+bool DiscardableMemory::InitializeAndLock(size_t size) {
+ DCHECK_EQ(fd_, -1);
+ DCHECK(!memory_);
+ size_ = size;
+ fd_ = ashmem_create_region("", size);
+
+ if (fd_ < 0) {
+ DLOG(ERROR) << "ashmem_create_region() failed";
+ return false;
+ }
+
+ 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;
+ return false;
+ }
+
+ if (!Map()) {
+ return false;
+ }
+
+ is_pinned_ = true;
+ return true;
+}
+
+LockDiscardableMemoryStatus DiscardableMemory::Lock() {
+ DCHECK_NE(fd_, -1);
+ DCHECK(!is_pinned_);
+
+ bool purged = false;
+ if (ashmem_pin_region(fd_, 0, 0) == ASHMEM_WAS_PURGED)
+ purged = true;
+
+ if (!Map())
+ return FAILED;
+
+ is_pinned_ = true;
+ return purged ? PURGED : SUCCESS;
+}
+
+void DiscardableMemory::Unlock() {
+ DCHECK_GE(fd_, 0);
+ DCHECK(is_pinned_);
+
+ Unmap();
+ if (ashmem_unpin_region(fd_, 0, 0))
+ DLOG(ERROR) << "Failed to unpin memory.";
+ is_pinned_ = false;
+}
+
+void* DiscardableMemory::Memory() const {
+ DCHECK(is_pinned_);
+ return memory_;
+}
+
+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;
+ }
+ return true;
+}
+
+void DiscardableMemory::Unmap() {
+ DCHECK(memory_);
+
+ if (-1 == munmap(memory_, size_))
+ DPLOG(ERROR) << "Failed to unmap memory.";
+
+ memory_ = NULL;
+}
+
+} // namespace skia
diff --git a/base/memory/discardable_memory_unittest.cc b/base/memory/discardable_memory_unittest.cc
new file mode 100644
index 0000000..66fdf0b
--- /dev/null
+++ b/base/memory/discardable_memory_unittest.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// 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 "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_ANDROID)
+#include "third_party/ashmem/ashmem.h"
+#endif
+
+namespace base {
+
+#if defined(OS_ANDROID)
+// Test Lock() and Unlock() functionalities.
+TEST(DiscardableMemoryTest, LockAndUnLock) {
+ ASSERT_TRUE(DiscardableMemory::Supported());
+
+ size_t size = 1024;
+
+ DiscardableMemory memory;
+ ASSERT_TRUE(memory.InitializeAndLock(size));
+ void* addr = memory.Memory();
+ ASSERT_NE(static_cast<void*>(NULL), addr);
+
+ memory.Unlock();
+ EXPECT_EQ(SUCCESS, memory.Lock());
+ addr = memory.Memory();
+ ASSERT_NE(static_cast<void*>(NULL), addr);
+
+ memory.Unlock();
+}
+
+// Test delete a discardable memory while it is locked.
+TEST(DiscardableMemoryTest, DeleteWhileLocked) {
+ ASSERT_TRUE(DiscardableMemory::Supported());
+
+ size_t size = 1024;
+ DiscardableMemory memory;
+ ASSERT_TRUE(memory.InitializeAndLock(size));
+}
+#endif
+
+}