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 42 namespace WebCore { 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. 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 , m_locksCount(1) // The buffer is locked at creation. 54 { 55 } 56 57 PurgeableVector::~PurgeableVector() 58 { 59 } 60 61 void 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 82 void 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 90 void PurgeableVector::clearDiscardable() 91 { 92 m_discardable.clear(); 93 m_discardableCapacity = 0; 94 m_discardableSize = 0; 95 } 96 97 void 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 120 void 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 135 void PurgeableVector::clear() 136 { 137 clearDiscardable(); 138 m_vector.clear(); 139 } 140 141 char* PurgeableVector::data() 142 { 143 ASSERT(isLocked()); 144 return m_discardable ? static_cast<char*>(m_discardable->data()) : m_vector.data(); 145 } 146 147 size_t PurgeableVector::size() const 148 { 149 return m_discardable ? m_discardableSize : m_vector.size(); 150 } 151 152 void 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 169 bool 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 182 void 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 200 bool PurgeableVector::isLocked() const 201 { 202 ASSERT(m_locksCount >= 0); 203 return m_locksCount > 0; 204 } 205 206 bool 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 245 size_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 WebCore 263