diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/platform/PurgeableVector.cpp')
-rw-r--r-- | chromium/third_party/WebKit/Source/platform/PurgeableVector.cpp | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/platform/PurgeableVector.cpp b/chromium/third_party/WebKit/Source/platform/PurgeableVector.cpp new file mode 100644 index 00000000000..d3efe15687d --- /dev/null +++ b/chromium/third_party/WebKit/Source/platform/PurgeableVector.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2014 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "platform/PurgeableVector.h" + +#include "public/platform/Platform.h" +#include "public/platform/WebDiscardableMemory.h" +#include "wtf/Assertions.h" +#include "wtf/OwnPtr.h" +#include "wtf/PassOwnPtr.h" + +#include <cstring> + +namespace WebCore { + +// WebDiscardableMemory allocations are expensive and page-grained. We only use +// them when there's a reasonable amount of memory to be saved by the OS +// discarding the memory. +static const size_t minimumDiscardableAllocationSize = 4 * 4096; + +PurgeableVector::PurgeableVector(PurgeableOption purgeable) + : m_discardableCapacity(0) + , m_discardableSize(0) + , m_isPurgeable(purgeable == Purgeable) + , m_locksCount(1) // The buffer is locked at creation. +{ +} + +PurgeableVector::~PurgeableVector() +{ +} + +void PurgeableVector::reserveCapacity(size_t capacity) +{ + ASSERT(isLocked()); + + if (m_isPurgeable) { + if (reservePurgeableCapacity(capacity, UseExactCapacity)) + return; + // Fallback to non-purgeable buffer allocation in case discardable memory allocation failed. + } + + if (!m_vector.capacity()) { + // Using reserveInitialCapacity() on the underlying vector ensures that the vector uses the + // exact specified capacity to avoid consuming too much memory for small resources. + m_vector.reserveInitialCapacity(capacity); + } else { + m_vector.reserveCapacity(capacity); + } + + moveDataFromDiscardableToVector(); +} + +void PurgeableVector::moveDataFromDiscardableToVector() +{ + if (m_discardable) { + m_vector.append(static_cast<const char*>(m_discardable->data()), m_discardableSize); + clearDiscardable(); + } +} + +void PurgeableVector::clearDiscardable() +{ + m_discardable.clear(); + m_discardableCapacity = 0; + m_discardableSize = 0; +} + +void PurgeableVector::append(const char* data, size_t length) +{ + ASSERT(isLocked()); + + if (!m_isPurgeable) { + m_vector.append(data, length); + return; + } + + const size_t currentSize = m_discardable ? m_discardableSize : m_vector.size(); + const size_t newBufferSize = currentSize + length; + + if (!reservePurgeableCapacity(newBufferSize, UseExponentialGrowth)) { + moveDataFromDiscardableToVector(); + m_vector.append(data, length); + return; + } + + ASSERT(m_discardableSize + length <= m_discardableCapacity); + memcpy(static_cast<char*>(m_discardable->data()) + m_discardableSize, data, length); + m_discardableSize += length; +} + +void PurgeableVector::grow(size_t newSize) +{ + ASSERT(newSize >= size()); + + if (m_isPurgeable) { + if (reservePurgeableCapacity(newSize, UseExponentialGrowth)) { + m_discardableSize = newSize; + return; + } + moveDataFromDiscardableToVector(); + } + + m_vector.resize(newSize); +} + +void PurgeableVector::clear() +{ + clearDiscardable(); + m_vector.clear(); +} + +char* PurgeableVector::data() +{ + ASSERT(isLocked()); + return m_discardable ? static_cast<char*>(m_discardable->data()) : m_vector.data(); +} + +size_t PurgeableVector::size() const +{ + return m_discardable ? m_discardableSize : m_vector.size(); +} + +void PurgeableVector::adopt(Vector<char>& other) +{ + if (size() > 0) + clear(); + + if (!m_isPurgeable) { + m_vector.swap(other); + return; + } + + if (other.isEmpty()) + return; + + append(other.data(), other.size()); + other.clear(); +} + +bool PurgeableVector::lock() +{ + ++m_locksCount; + if (m_locksCount > 1) + return true; + + ASSERT(m_locksCount == 1); + if (!m_discardable) + return true; + + return m_discardable->lock(); +} + +void PurgeableVector::unlock() +{ + ASSERT(isLocked()); + --m_locksCount; + if (m_locksCount > 0) + return; + + if (!m_vector.isEmpty()) { + ASSERT(!m_discardable); + m_isPurgeable = true; + if (!reservePurgeableCapacity(m_vector.size(), UseExactCapacity)) + return; + } + + if (m_discardable) + m_discardable->unlock(); +} + +bool PurgeableVector::isLocked() const +{ + ASSERT(m_locksCount >= 0); + return m_locksCount > 0; +} + +bool PurgeableVector::reservePurgeableCapacity(size_t capacity, PurgeableAllocationStrategy allocationStrategy) +{ + ASSERT(m_isPurgeable); + + if (m_discardable && m_discardableCapacity >= capacity) { + ASSERT(!m_vector.capacity()); + return true; + } + + if (capacity < minimumDiscardableAllocationSize) + return false; + + if (allocationStrategy == UseExponentialGrowth) + capacity = adjustPurgeableCapacity(capacity); + + OwnPtr<blink::WebDiscardableMemory> discardable = adoptPtr( + blink::Platform::current()->allocateAndLockDiscardableMemory(capacity)); + if (!discardable) { + // Discardable memory is not supported. + m_isPurgeable = false; + return false; + } + + m_discardableCapacity = capacity; + // Copy the data that was either in the previous purgeable buffer or in the vector to the new + // purgeable buffer. + if (m_discardable) { + memcpy(discardable->data(), m_discardable->data(), m_discardableSize); + } else { + memcpy(discardable->data(), m_vector.data(), m_vector.size()); + m_discardableSize = m_vector.size(); + m_vector.clear(); + } + + m_discardable.swap(discardable); + ASSERT(!m_vector.capacity()); + return true; +} + +size_t PurgeableVector::adjustPurgeableCapacity(size_t capacity) const +{ + ASSERT(capacity >= minimumDiscardableAllocationSize); + + const float growthFactor = 1.5; + size_t newCapacity = std::max(capacity, static_cast<size_t>(m_discardableCapacity * growthFactor)); + + // Discardable memory has page-granularity so align to the next page here to minimize + // fragmentation. + // Since the page size is only used below to minimize fragmentation it's still safe to use it + // even if it gets out of sync (e.g. due to the use of huge pages). + const size_t kPageSize = 4096; + newCapacity = (newCapacity + kPageSize - 1) & ~(kPageSize - 1); + + return std::max(capacity, newCapacity); // Overflow check. +} + +} // namespace WebCore |