Home | History | Annotate | Download | only in platform
      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