| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2014 Google Inc. All rights reserved. | |
| 3 * | |
| 4 * Redistribution and use in source and binary forms, with or without | |
| 5 * modification, are permitted provided that the following conditions are | |
| 6 * met: | |
| 7 * | |
| 8 * * Redistributions of source code must retain the above copyright | |
| 9 * notice, this list of conditions and the following disclaimer. | |
| 10 * * Redistributions in binary form must reproduce the above | |
| 11 * copyright notice, this list of conditions and the following disclaimer | |
| 12 * in the documentation and/or other materials provided with the | |
| 13 * distribution. | |
| 14 * * Neither the name of Google Inc. nor the names of its | |
| 15 * contributors may be used to endorse or promote products derived from | |
| 16 * this software without specific prior written permission. | |
| 17 * | |
| 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 29 */ | |
| 30 | |
| 31 #include "platform/PurgeableVector.h" | |
| 32 | |
| 33 #include "base/memory/discardable_memory.h" | |
| 34 #include "base/memory/discardable_memory_allocator.h" | |
| 35 #include "platform/web_process_memory_dump.h" | |
| 36 #include "wtf/Assertions.h" | |
| 37 #include "wtf/text/StringUTF8Adaptor.h" | |
| 38 #include "wtf/text/WTFString.h" | |
| 39 #include <cstring> | |
| 40 #include <utility> | |
| 41 | |
| 42 namespace blink { | |
| 43 | |
| 44 // DiscardableMemory allocations are expensive and page-grained. We only use | |
| 45 // them when there's a reasonable amount of memory to be saved by the OS | |
| 46 // discarding the memory. | |
| 47 static const size_t minimumDiscardableAllocationSize = 4 * 4096; | |
| 48 | |
| 49 PurgeableVector::PurgeableVector(PurgeableOption purgeable) | |
| 50 : m_discardableCapacity(0) | |
| 51 , m_discardableSize(0) | |
| 52 , m_isPurgeable(purgeable == Purgeable) | |
| 53 { | |
| 54 } | |
| 55 | |
| 56 PurgeableVector::~PurgeableVector() | |
| 57 { | |
| 58 } | |
| 59 | |
| 60 void PurgeableVector::reserveCapacity(size_t capacity) | |
| 61 { | |
| 62 if (m_isPurgeable) { | |
| 63 if (reservePurgeableCapacity(capacity, UseExactCapacity)) | |
| 64 return; | |
| 65 // Fallback to non-purgeable buffer allocation in case discardable memor
y allocation failed. | |
| 66 } | |
| 67 | |
| 68 if (!m_vector.capacity()) { | |
| 69 // Using reserveInitialCapacity() on the underlying vector ensures that
the vector uses the | |
| 70 // exact specified capacity to avoid consuming too much memory for small
resources. | |
| 71 m_vector.reserveInitialCapacity(capacity); | |
| 72 } else { | |
| 73 m_vector.reserveCapacity(capacity); | |
| 74 } | |
| 75 | |
| 76 moveDataFromDiscardableToVector(); | |
| 77 } | |
| 78 | |
| 79 void PurgeableVector::onMemoryDump(const String& dumpName, WebProcessMemoryDump*
memoryDump) const | |
| 80 { | |
| 81 ASSERT(!(m_discardable && m_vector.size())); | |
| 82 if (m_discardable) { | |
| 83 WebMemoryAllocatorDump* dump = memoryDump->createDiscardableMemoryAlloca
torDump( | |
| 84 StringUTF8Adaptor(dumpName).asStringPiece().as_string(), m_discardab
le.get()); | |
| 85 dump->addScalar("discardable_size", "bytes", m_discardableSize); | |
| 86 } else if (m_vector.size()) { | |
| 87 WebMemoryAllocatorDump* dump = memoryDump->createMemoryAllocatorDump(dum
pName); | |
| 88 dump->addScalar("size", "bytes", m_vector.size()); | |
| 89 memoryDump->addSuballocation(dump->guid(), String(WTF::Partitions::kAllo
catedObjectPoolName)); | |
| 90 } | |
| 91 } | |
| 92 | |
| 93 void PurgeableVector::moveDataFromDiscardableToVector() | |
| 94 { | |
| 95 if (m_discardable) { | |
| 96 m_vector.append(static_cast<const char*>(m_discardable->data()), m_disca
rdableSize); | |
| 97 clearDiscardable(); | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 void PurgeableVector::clearDiscardable() | |
| 102 { | |
| 103 m_discardable = nullptr; | |
| 104 m_discardableCapacity = 0; | |
| 105 m_discardableSize = 0; | |
| 106 } | |
| 107 | |
| 108 void PurgeableVector::append(const char* data, size_t length) | |
| 109 { | |
| 110 if (!m_isPurgeable) { | |
| 111 m_vector.append(data, length); | |
| 112 return; | |
| 113 } | |
| 114 | |
| 115 const size_t currentSize = m_discardable ? m_discardableSize : m_vector.size
(); | |
| 116 const size_t newBufferSize = currentSize + length; | |
| 117 | |
| 118 if (!reservePurgeableCapacity(newBufferSize, UseExponentialGrowth)) { | |
| 119 moveDataFromDiscardableToVector(); | |
| 120 m_vector.append(data, length); | |
| 121 return; | |
| 122 } | |
| 123 | |
| 124 ASSERT(m_discardableSize + length <= m_discardableCapacity); | |
| 125 memcpy(static_cast<char*>(m_discardable->data()) + m_discardableSize, data,
length); | |
| 126 m_discardableSize += length; | |
| 127 } | |
| 128 | |
| 129 void PurgeableVector::grow(size_t newSize) | |
| 130 { | |
| 131 ASSERT(newSize >= size()); | |
| 132 | |
| 133 if (m_isPurgeable) { | |
| 134 if (reservePurgeableCapacity(newSize, UseExponentialGrowth)) { | |
| 135 m_discardableSize = newSize; | |
| 136 return; | |
| 137 } | |
| 138 moveDataFromDiscardableToVector(); | |
| 139 } | |
| 140 | |
| 141 m_vector.resize(newSize); | |
| 142 } | |
| 143 | |
| 144 void PurgeableVector::clear() | |
| 145 { | |
| 146 clearDiscardable(); | |
| 147 m_vector.clear(); | |
| 148 } | |
| 149 | |
| 150 char* PurgeableVector::data() | |
| 151 { | |
| 152 return m_discardable ? static_cast<char*>(m_discardable->data()) : m_vector.
data(); | |
| 153 } | |
| 154 | |
| 155 size_t PurgeableVector::size() const | |
| 156 { | |
| 157 return m_discardable ? m_discardableSize : m_vector.size(); | |
| 158 } | |
| 159 | |
| 160 void PurgeableVector::adopt(Vector<char>& other) | |
| 161 { | |
| 162 if (size() > 0) | |
| 163 clear(); | |
| 164 | |
| 165 if (!m_isPurgeable) { | |
| 166 m_vector.swap(other); | |
| 167 return; | |
| 168 } | |
| 169 | |
| 170 if (other.isEmpty()) | |
| 171 return; | |
| 172 | |
| 173 append(other.data(), other.size()); | |
| 174 other.clear(); | |
| 175 } | |
| 176 | |
| 177 bool PurgeableVector::reservePurgeableCapacity(size_t capacity, PurgeableAllocat
ionStrategy allocationStrategy) | |
| 178 { | |
| 179 ASSERT(m_isPurgeable); | |
| 180 | |
| 181 if (m_discardable && m_discardableCapacity >= capacity) { | |
| 182 ASSERT(!m_vector.capacity()); | |
| 183 return true; | |
| 184 } | |
| 185 | |
| 186 if (capacity < minimumDiscardableAllocationSize) | |
| 187 return false; | |
| 188 | |
| 189 if (allocationStrategy == UseExponentialGrowth) | |
| 190 capacity = adjustPurgeableCapacity(capacity); | |
| 191 | |
| 192 std::unique_ptr<base::DiscardableMemory> discardable = | |
| 193 base::DiscardableMemoryAllocator::GetInstance()->AllocateLockedDiscardab
leMemory(capacity); | |
| 194 ASSERT(discardable); | |
| 195 | |
| 196 m_discardableCapacity = capacity; | |
| 197 // Copy the data that was either in the previous purgeable buffer or in the
vector to the new | |
| 198 // purgeable buffer. | |
| 199 if (m_discardable) { | |
| 200 memcpy(discardable->data(), m_discardable->data(), m_discardableSize); | |
| 201 } else { | |
| 202 memcpy(discardable->data(), m_vector.data(), m_vector.size()); | |
| 203 m_discardableSize = m_vector.size(); | |
| 204 m_vector.clear(); | |
| 205 } | |
| 206 | |
| 207 m_discardable = std::move(discardable); | |
| 208 ASSERT(!m_vector.capacity()); | |
| 209 return true; | |
| 210 } | |
| 211 | |
| 212 size_t PurgeableVector::adjustPurgeableCapacity(size_t capacity) const | |
| 213 { | |
| 214 ASSERT(capacity >= minimumDiscardableAllocationSize); | |
| 215 | |
| 216 const float growthFactor = 1.5; | |
| 217 size_t newCapacity = std::max(capacity, static_cast<size_t>(m_discardableCap
acity * growthFactor)); | |
| 218 | |
| 219 // Discardable memory has page-granularity so align to the next page here to
minimize | |
| 220 // fragmentation. | |
| 221 // Since the page size is only used below to minimize fragmentation it's sti
ll safe to use it | |
| 222 // even if it gets out of sync (e.g. due to the use of huge pages). | |
| 223 const size_t kPageSize = 4096; | |
| 224 newCapacity = (newCapacity + kPageSize - 1) & ~(kPageSize - 1); | |
| 225 | |
| 226 return std::max(capacity, newCapacity); // Overflow check. | |
| 227 } | |
| 228 | |
| 229 } // namespace blink | |
| OLD | NEW |