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 "config.h" 32#include "platform/PurgeableVector.h" 33 34#include "public/platform/Platform.h" 35#include "public/platform/WebDiscardableMemory.h" 36#include "wtf/Assertions.h" 37#include "wtf/OwnPtr.h" 38#include "wtf/PassOwnPtr.h" 39 40#include <cstring> 41 42namespace blink { 43 44// WebDiscardableMemory 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. 47static const size_t minimumDiscardableAllocationSize = 4 * 4096; 48 49PurgeableVector::PurgeableVector(PurgeableOption purgeable) 50 : m_discardableCapacity(0) 51 , m_discardableSize(0) 52 , m_isPurgeable(purgeable == Purgeable) 53 , m_locksCount(1) // The buffer is locked at creation. 54{ 55} 56 57PurgeableVector::~PurgeableVector() 58{ 59} 60 61void PurgeableVector::reserveCapacity(size_t capacity) 62{ 63 ASSERT(isLocked()); 64 65 if (m_isPurgeable) { 66 if (reservePurgeableCapacity(capacity, UseExactCapacity)) 67 return; 68 // Fallback to non-purgeable buffer allocation in case discardable memory allocation failed. 69 } 70 71 if (!m_vector.capacity()) { 72 // Using reserveInitialCapacity() on the underlying vector ensures that the vector uses the 73 // exact specified capacity to avoid consuming too much memory for small resources. 74 m_vector.reserveInitialCapacity(capacity); 75 } else { 76 m_vector.reserveCapacity(capacity); 77 } 78 79 moveDataFromDiscardableToVector(); 80} 81 82void PurgeableVector::moveDataFromDiscardableToVector() 83{ 84 if (m_discardable) { 85 m_vector.append(static_cast<const char*>(m_discardable->data()), m_discardableSize); 86 clearDiscardable(); 87 } 88} 89 90void PurgeableVector::clearDiscardable() 91{ 92 m_discardable.clear(); 93 m_discardableCapacity = 0; 94 m_discardableSize = 0; 95} 96 97void PurgeableVector::append(const char* data, size_t length) 98{ 99 ASSERT(isLocked()); 100 101 if (!m_isPurgeable) { 102 m_vector.append(data, length); 103 return; 104 } 105 106 const size_t currentSize = m_discardable ? m_discardableSize : m_vector.size(); 107 const size_t newBufferSize = currentSize + length; 108 109 if (!reservePurgeableCapacity(newBufferSize, UseExponentialGrowth)) { 110 moveDataFromDiscardableToVector(); 111 m_vector.append(data, length); 112 return; 113 } 114 115 ASSERT(m_discardableSize + length <= m_discardableCapacity); 116 memcpy(static_cast<char*>(m_discardable->data()) + m_discardableSize, data, length); 117 m_discardableSize += length; 118} 119 120void PurgeableVector::grow(size_t newSize) 121{ 122 ASSERT(newSize >= size()); 123 124 if (m_isPurgeable) { 125 if (reservePurgeableCapacity(newSize, UseExponentialGrowth)) { 126 m_discardableSize = newSize; 127 return; 128 } 129 moveDataFromDiscardableToVector(); 130 } 131 132 m_vector.resize(newSize); 133} 134 135void PurgeableVector::clear() 136{ 137 clearDiscardable(); 138 m_vector.clear(); 139} 140 141char* PurgeableVector::data() 142{ 143 ASSERT(isLocked()); 144 return m_discardable ? static_cast<char*>(m_discardable->data()) : m_vector.data(); 145} 146 147size_t PurgeableVector::size() const 148{ 149 return m_discardable ? m_discardableSize : m_vector.size(); 150} 151 152void PurgeableVector::adopt(Vector<char>& other) 153{ 154 if (size() > 0) 155 clear(); 156 157 if (!m_isPurgeable) { 158 m_vector.swap(other); 159 return; 160 } 161 162 if (other.isEmpty()) 163 return; 164 165 append(other.data(), other.size()); 166 other.clear(); 167} 168 169bool PurgeableVector::lock() 170{ 171 ++m_locksCount; 172 if (m_locksCount > 1) 173 return true; 174 175 ASSERT(m_locksCount == 1); 176 if (!m_discardable) 177 return true; 178 179 return m_discardable->lock(); 180} 181 182void PurgeableVector::unlock() 183{ 184 ASSERT(isLocked()); 185 --m_locksCount; 186 if (m_locksCount > 0) 187 return; 188 189 if (!m_vector.isEmpty()) { 190 ASSERT(!m_discardable); 191 m_isPurgeable = true; 192 if (!reservePurgeableCapacity(m_vector.size(), UseExactCapacity)) 193 return; 194 } 195 196 if (m_discardable) 197 m_discardable->unlock(); 198} 199 200bool PurgeableVector::isLocked() const 201{ 202 ASSERT(m_locksCount >= 0); 203 return m_locksCount > 0; 204} 205 206bool PurgeableVector::reservePurgeableCapacity(size_t capacity, PurgeableAllocationStrategy allocationStrategy) 207{ 208 ASSERT(m_isPurgeable); 209 210 if (m_discardable && m_discardableCapacity >= capacity) { 211 ASSERT(!m_vector.capacity()); 212 return true; 213 } 214 215 if (capacity < minimumDiscardableAllocationSize) 216 return false; 217 218 if (allocationStrategy == UseExponentialGrowth) 219 capacity = adjustPurgeableCapacity(capacity); 220 221 OwnPtr<blink::WebDiscardableMemory> discardable = adoptPtr( 222 blink::Platform::current()->allocateAndLockDiscardableMemory(capacity)); 223 if (!discardable) { 224 // Discardable memory is not supported. 225 m_isPurgeable = false; 226 return false; 227 } 228 229 m_discardableCapacity = capacity; 230 // Copy the data that was either in the previous purgeable buffer or in the vector to the new 231 // purgeable buffer. 232 if (m_discardable) { 233 memcpy(discardable->data(), m_discardable->data(), m_discardableSize); 234 } else { 235 memcpy(discardable->data(), m_vector.data(), m_vector.size()); 236 m_discardableSize = m_vector.size(); 237 m_vector.clear(); 238 } 239 240 m_discardable.swap(discardable); 241 ASSERT(!m_vector.capacity()); 242 return true; 243} 244 245size_t PurgeableVector::adjustPurgeableCapacity(size_t capacity) const 246{ 247 ASSERT(capacity >= minimumDiscardableAllocationSize); 248 249 const float growthFactor = 1.5; 250 size_t newCapacity = std::max(capacity, static_cast<size_t>(m_discardableCapacity * growthFactor)); 251 252 // Discardable memory has page-granularity so align to the next page here to minimize 253 // fragmentation. 254 // Since the page size is only used below to minimize fragmentation it's still safe to use it 255 // even if it gets out of sync (e.g. due to the use of huge pages). 256 const size_t kPageSize = 4096; 257 newCapacity = (newCapacity + kPageSize - 1) & ~(kPageSize - 1); 258 259 return std::max(capacity, newCapacity); // Overflow check. 260} 261 262} // namespace blink 263