summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
Diffstat (limited to 'base')
-rw-r--r--base/android/sys_utils.h2
-rw-r--r--base/memory/discardable_memory_allocator_android.cc34
-rw-r--r--base/memory/discardable_memory_allocator_android.h12
-rw-r--r--base/memory/discardable_memory_allocator_android_unittest.cc26
-rw-r--r--base/memory/discardable_memory_android.cc17
5 files changed, 62 insertions, 29 deletions
diff --git a/base/android/sys_utils.h b/base/android/sys_utils.h
index 9b3152b..0841d0a 100644
--- a/base/android/sys_utils.h
+++ b/base/android/sys_utils.h
@@ -17,7 +17,7 @@ class BASE_EXPORT SysUtils {
// Returns true iff this is a low-end device.
static bool IsLowEndDevice();
- // Return the device's RAM size in kilo-bytes. Used for testing.
+ // Return the device's RAM size in kilo-bytes.
static size_t AmountOfPhysicalMemoryKB();
private:
diff --git a/base/memory/discardable_memory_allocator_android.cc b/base/memory/discardable_memory_allocator_android.cc
index 5e10817..a0a3aff 100644
--- a/base/memory/discardable_memory_allocator_android.cc
+++ b/base/memory/discardable_memory_allocator_android.cc
@@ -44,6 +44,8 @@ namespace {
// issues.
const size_t kMaxChunkFragmentationBytes = 4096 - 1;
+const size_t kMinAshmemRegionSize = 32 * 1024 * 1024;
+
} // namespace
namespace internal {
@@ -104,6 +106,7 @@ class DiscardableMemoryAllocator::AshmemRegion {
size_t size,
const std::string& name,
DiscardableMemoryAllocator* allocator) {
+ DCHECK_EQ(size, internal::AlignToNextPage(size));
int fd;
void* base;
if (!internal::CreateAshmemRegion(name.c_str(), size, &fd, &base))
@@ -364,8 +367,12 @@ DiscardableMemoryAllocator::DiscardableAshmemChunk::~DiscardableAshmemChunk() {
ashmem_region_->OnChunkDeletion(address_, size_);
}
-DiscardableMemoryAllocator::DiscardableMemoryAllocator(const std::string& name)
- : name_(name) {
+DiscardableMemoryAllocator::DiscardableMemoryAllocator(
+ const std::string& name,
+ size_t ashmem_region_size)
+ : name_(name),
+ ashmem_region_size_(std::max(kMinAshmemRegionSize, ashmem_region_size)) {
+ DCHECK_GE(ashmem_region_size_, kMinAshmemRegionSize);
}
DiscardableMemoryAllocator::~DiscardableMemoryAllocator() {
@@ -389,16 +396,21 @@ scoped_ptr<DiscardableMemory> DiscardableMemoryAllocator::Allocate(
if (memory)
return memory.Pass();
}
- scoped_ptr<AshmemRegion> new_region(
- AshmemRegion::Create(
- std::max(static_cast<size_t>(kMinAshmemRegionSize), aligned_size),
- name_.c_str(), this));
- if (!new_region) {
- // TODO(pliard): consider adding an histogram to see how often this happens.
- return scoped_ptr<DiscardableMemory>();
+ // The creation of the (large) ashmem region might fail if the address space
+ // is too fragmented. In case creation fails the allocator retries by
+ // repetitively dividing the size by 2.
+ const size_t min_region_size = std::max(kMinAshmemRegionSize, aligned_size);
+ for (size_t region_size = std::max(ashmem_region_size_, aligned_size);
+ region_size >= min_region_size; region_size /= 2) {
+ scoped_ptr<AshmemRegion> new_region(
+ AshmemRegion::Create(region_size, name_.c_str(), this));
+ if (!new_region)
+ continue;
+ ashmem_regions_.push_back(new_region.release());
+ return ashmem_regions_.back()->Allocate_Locked(size, aligned_size);
}
- ashmem_regions_.push_back(new_region.release());
- return ashmem_regions_.back()->Allocate_Locked(size, aligned_size);
+ // TODO(pliard): consider adding an histogram to see how often this happens.
+ return scoped_ptr<DiscardableMemory>();
}
void DiscardableMemoryAllocator::DeleteAshmemRegion_Locked(
diff --git a/base/memory/discardable_memory_allocator_android.h b/base/memory/discardable_memory_allocator_android.h
index 7991656..7ced405 100644
--- a/base/memory/discardable_memory_allocator_android.h
+++ b/base/memory/discardable_memory_allocator_android.h
@@ -32,13 +32,12 @@ namespace internal {
// discardable_memory.h for DiscardableMemory's threading guarantees.
class BASE_EXPORT_PRIVATE DiscardableMemoryAllocator {
public:
- // Exposed for testing.
- enum {
- kMinAshmemRegionSize = 32 * 1024 * 1024,
- };
-
// Note that |name| is only used for debugging/measurement purposes.
- explicit DiscardableMemoryAllocator(const std::string& name);
+ // |ashmem_region_size| is the size that will be used to create the underlying
+ // ashmem regions and is expected to be greater or equal than 32 MBytes.
+ DiscardableMemoryAllocator(const std::string& name,
+ size_t ashmem_region_size);
+
~DiscardableMemoryAllocator();
// Note that the allocator must outlive the returned DiscardableMemory
@@ -53,6 +52,7 @@ class BASE_EXPORT_PRIVATE DiscardableMemoryAllocator {
base::ThreadChecker thread_checker_;
const std::string name_;
+ const size_t ashmem_region_size_;
base::Lock lock_;
ScopedVector<AshmemRegion> ashmem_regions_;
diff --git a/base/memory/discardable_memory_allocator_android_unittest.cc b/base/memory/discardable_memory_allocator_android_unittest.cc
index 97cf5d4..ae95d5d 100644
--- a/base/memory/discardable_memory_allocator_android_unittest.cc
+++ b/base/memory/discardable_memory_allocator_android_unittest.cc
@@ -20,13 +20,14 @@ namespace internal {
const char kAllocatorName[] = "allocator-for-testing";
+const size_t kAshmemRegionSizeForTesting = 32 * 1024 * 1024;
const size_t kPageSize = 4096;
-const size_t kMinAshmemRegionSize =
- DiscardableMemoryAllocator::kMinAshmemRegionSize;
class DiscardableMemoryAllocatorTest : public testing::Test {
protected:
- DiscardableMemoryAllocatorTest() : allocator_(kAllocatorName) {}
+ DiscardableMemoryAllocatorTest()
+ : allocator_(kAllocatorName, kAshmemRegionSizeForTesting) {
+ }
DiscardableMemoryAllocator allocator_;
};
@@ -45,6 +46,19 @@ TEST_F(DiscardableMemoryAllocatorTest, Basic) {
WriteToDiscardableMemory(memory.get(), size);
}
+TEST_F(DiscardableMemoryAllocatorTest,
+ AshmemRegionsAreNotSmallerThanRequestedSize) {
+ const size_t size = std::numeric_limits<size_t>::max() - kPageSize + 1;
+ // The creation of the underlying ashmem region is expected to fail since
+ // there should not be enough room in the address space. When ashmem creation
+ // fails, the allocator repetitively retries by dividing the size by 2. This
+ // size should not be smaller than the size the user requested so the
+ // allocation here should just fail (and not succeed with the minimum ashmem
+ // region size).
+ scoped_ptr<DiscardableMemory> memory(allocator_.Allocate(size));
+ ASSERT_FALSE(memory);
+}
+
TEST_F(DiscardableMemoryAllocatorTest, LargeAllocation) {
// Note that large allocations should just use DiscardableMemoryAndroidSimple
// instead.
@@ -211,15 +225,15 @@ TEST_F(DiscardableMemoryAllocatorTest,
TEST_F(DiscardableMemoryAllocatorTest, UseMultipleAshmemRegions) {
// Leave one page untouched at the end of the ashmem region.
- const size_t size = kMinAshmemRegionSize - kPageSize;
+ const size_t size = kAshmemRegionSizeForTesting - kPageSize;
scoped_ptr<DiscardableMemory> memory1(allocator_.Allocate(size));
ASSERT_TRUE(memory1);
WriteToDiscardableMemory(memory1.get(), size);
scoped_ptr<DiscardableMemory> memory2(
- allocator_.Allocate(kMinAshmemRegionSize));
+ allocator_.Allocate(kAshmemRegionSizeForTesting));
ASSERT_TRUE(memory2);
- WriteToDiscardableMemory(memory2.get(), kMinAshmemRegionSize);
+ WriteToDiscardableMemory(memory2.get(), kAshmemRegionSizeForTesting);
// The last page of the first ashmem region should be used for this
// allocation.
scoped_ptr<DiscardableMemory> memory3(allocator_.Allocate(kPageSize));
diff --git a/base/memory/discardable_memory_android.cc b/base/memory/discardable_memory_android.cc
index 7e84967..519aa8d 100644
--- a/base/memory/discardable_memory_android.cc
+++ b/base/memory/discardable_memory_android.cc
@@ -11,6 +11,7 @@
#include <limits>
+#include "base/android/sys_utils.h"
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/file_util.h"
@@ -31,7 +32,8 @@ const char kAshmemAllocatorName[] = "DiscardableMemoryAllocator";
struct GlobalContext {
GlobalContext()
: ashmem_fd_limit(GetSoftFDLimit()),
- allocator(kAshmemAllocatorName),
+ allocator(kAshmemAllocatorName,
+ GetOptimalAshmemRegionSizeForAllocator()),
ashmem_fd_count_(0) {
}
@@ -63,6 +65,14 @@ struct GlobalContext {
return limit_info.rlim_cur / 4;
}
+ // Returns 64 MBytes for a 512 MBytes device, 128 MBytes for 1024 MBytes...
+ static size_t GetOptimalAshmemRegionSizeForAllocator() {
+ // Note that this may do some I/O (without hitting the disk though) so it
+ // should not be called on the critical path.
+ return internal::AlignToNextPage(
+ base::android::SysUtils::AmountOfPhysicalMemoryKB() * 1024 / 8);
+ }
+
int ashmem_fd_count_;
};
@@ -231,11 +241,8 @@ scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory(
// rather than DiscardableMemoryAndroidSimple). Moreover keeping the lock
// acquired for the whole allocation would cause a deadlock when the allocator
// tries to create an ashmem region.
- const size_t kAllocatorRegionSize =
- internal::DiscardableMemoryAllocator::kMinAshmemRegionSize;
GlobalContext* const global_context = g_context.Pointer();
- if (aligned_size >= kAllocatorRegionSize ||
- GetCurrentNumberOfAshmemFDs() < 0.9 * global_context->ashmem_fd_limit) {
+ if (GetCurrentNumberOfAshmemFDs() < 0.9 * global_context->ashmem_fd_limit) {
int fd;
void* address;
if (internal::CreateAshmemRegion("", aligned_size, &fd, &address)) {