1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/memory/discardable_memory.h" 6 7 #include <sys/mman.h> 8 #include <unistd.h> 9 10 #include "base/lazy_instance.h" 11 #include "base/logging.h" 12 #include "base/posix/eintr_wrapper.h" 13 #include "base/synchronization/lock.h" 14 #include "third_party/ashmem/ashmem.h" 15 16 namespace { 17 18 base::LazyInstance<base::Lock>::Leaky g_discardable_memory_lock = 19 LAZY_INSTANCE_INITIALIZER; 20 21 // Total number of discardable memory in the process. 22 int g_num_discardable_memory = 0; 23 24 // Upper limit on the number of discardable memory to avoid hitting file 25 // descriptor limit. 26 const int kDiscardableMemoryNumLimit = 128; 27 28 } 29 30 namespace base { 31 32 // static 33 bool DiscardableMemory::Supported() { 34 return true; 35 } 36 37 DiscardableMemory::~DiscardableMemory() { 38 if (is_locked_) 39 Unlock(); 40 // If fd_ is smaller than 0, initialization must have failed and 41 // g_num_discardable_memory is not incremented by the caller. 42 if (fd_ < 0) 43 return; 44 HANDLE_EINTR(close(fd_)); 45 fd_ = -1; 46 ReleaseFileDescriptor(); 47 } 48 49 bool DiscardableMemory::ReserveFileDescriptor() { 50 base::AutoLock lock(g_discardable_memory_lock.Get()); 51 if (g_num_discardable_memory < kDiscardableMemoryNumLimit) { 52 ++g_num_discardable_memory; 53 return true; 54 } 55 return false; 56 } 57 58 void DiscardableMemory::ReleaseFileDescriptor() { 59 base::AutoLock lock(g_discardable_memory_lock.Get()); 60 --g_num_discardable_memory; 61 DCHECK_LE(0, g_num_discardable_memory); 62 } 63 64 bool DiscardableMemory::InitializeAndLock(size_t size) { 65 // When this function returns true, fd_ should be larger or equal than 0 66 // and g_num_discardable_memory is incremented by 1. Otherwise, fd_ 67 // is less than 0 and g_num_discardable_memory is not incremented by 68 // the caller. 69 DCHECK_EQ(fd_, -1); 70 DCHECK(!memory_); 71 if (!ReserveFileDescriptor()) 72 return false; 73 74 size_ = size; 75 fd_ = ashmem_create_region("", size); 76 77 if (fd_ < 0) { 78 DLOG(ERROR) << "ashmem_create_region() failed"; 79 ReleaseFileDescriptor(); 80 return false; 81 } 82 83 int err = ashmem_set_prot_region(fd_, PROT_READ | PROT_WRITE); 84 if (err < 0) { 85 DLOG(ERROR) << "Error " << err << " when setting protection of ashmem"; 86 HANDLE_EINTR(close(fd_)); 87 fd_ = -1; 88 ReleaseFileDescriptor(); 89 return false; 90 } 91 92 if (!Map()) { 93 // Close the file descriptor in case of any initialization errors. 94 HANDLE_EINTR(close(fd_)); 95 fd_ = -1; 96 ReleaseFileDescriptor(); 97 return false; 98 } 99 100 is_locked_ = true; 101 return true; 102 } 103 104 LockDiscardableMemoryStatus DiscardableMemory::Lock() { 105 DCHECK_NE(fd_, -1); 106 DCHECK(!is_locked_); 107 108 bool purged = false; 109 if (ashmem_pin_region(fd_, 0, 0) == ASHMEM_WAS_PURGED) 110 purged = true; 111 112 if (!Map()) 113 return DISCARDABLE_MEMORY_FAILED; 114 115 is_locked_ = true; 116 return purged ? DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS; 117 } 118 119 void DiscardableMemory::Unlock() { 120 DCHECK_GE(fd_, 0); 121 DCHECK(is_locked_); 122 123 Unmap(); 124 if (ashmem_unpin_region(fd_, 0, 0)) 125 DLOG(ERROR) << "Failed to unpin memory."; 126 is_locked_ = false; 127 } 128 129 bool DiscardableMemory::Map() { 130 DCHECK(!memory_); 131 // There is a problem using MAP_PRIVATE here. As we are constantly calling 132 // Lock() and Unlock(), data could get lost if they are not written to the 133 // underlying file when Unlock() gets called. 134 memory_ = mmap(NULL, size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0); 135 if (memory_ == (void*)-1) { 136 DPLOG(ERROR) << "Failed to map memory."; 137 memory_ = NULL; 138 if (ashmem_unpin_region(fd_, 0, 0)) 139 DLOG(ERROR) << "Failed to unpin memory."; 140 return false; 141 } 142 return true; 143 } 144 145 void DiscardableMemory::Unmap() { 146 DCHECK(memory_); 147 148 if (-1 == munmap(memory_, size_)) 149 DPLOG(ERROR) << "Failed to unmap memory."; 150 151 memory_ = NULL; 152 } 153 154 // static 155 bool DiscardableMemory::PurgeForTestingSupported() { 156 return false; 157 } 158 159 // static 160 void DiscardableMemory::PurgeForTesting() { 161 NOTIMPLEMENTED(); 162 } 163 164 } // namespace base 165