diff options
Diffstat (limited to 'base')
| -rw-r--r-- | base/android/sys_utils.h | 2 | ||||
| -rw-r--r-- | base/memory/discardable_memory_allocator_android.cc | 34 | ||||
| -rw-r--r-- | base/memory/discardable_memory_allocator_android.h | 12 | ||||
| -rw-r--r-- | base/memory/discardable_memory_allocator_android_unittest.cc | 26 | ||||
| -rw-r--r-- | base/memory/discardable_memory_android.cc | 17 |
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)) { |
