blob: 9e3f8bae80af8581745767108282954ab5f4e2a8 [file] [log] [blame]
/*
* Copyright (C) 2012, 2013 Apple 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:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 "MarkedAllocator.h"
#include "GCActivityCallback.h"
#include "Heap.h"
#include "IncrementalSweeper.h"
#include "JSCInlines.h"
#include "VM.h"
#include <wtf/CurrentTime.h>
namespace JSC {
static bool isListPagedOut(double deadline, DoublyLinkedList<MarkedBlock>& list)
{
unsigned itersSinceLastTimeCheck = 0;
MarkedBlock* block = list.head();
while (block) {
block = block->next();
++itersSinceLastTimeCheck;
if (itersSinceLastTimeCheck >= Heap::s_timeCheckResolution) {
double currentTime = WTF::monotonicallyIncreasingTime();
if (currentTime > deadline)
return true;
itersSinceLastTimeCheck = 0;
}
}
return false;
}
bool MarkedAllocator::isPagedOut(double deadline)
{
if (isListPagedOut(deadline, m_blockList))
return true;
return false;
}
inline void* MarkedAllocator::tryAllocateHelper(size_t bytes)
{
if (m_currentBlock) {
ASSERT(m_currentBlock == m_nextBlockToSweep);
m_currentBlock->didConsumeFreeList();
m_nextBlockToSweep = m_currentBlock->next();
}
MarkedBlock* next;
for (MarkedBlock*& block = m_nextBlockToSweep; block; block = next) {
next = block->next();
MarkedBlock::FreeList freeList = block->sweep(MarkedBlock::SweepToFreeList);
double utilization = ((double)MarkedBlock::blockSize - (double)freeList.bytes) / (double)MarkedBlock::blockSize;
if (utilization >= Options::minMarkedBlockUtilization()) {
ASSERT(freeList.bytes || !freeList.head);
m_blockList.remove(block);
m_retiredBlocks.push(block);
block->didRetireBlock(freeList);
continue;
}
if (bytes > block->cellSize()) {
block->stopAllocating(freeList);
continue;
}
m_currentBlock = block;
m_freeList = freeList;
break;
}
if (!m_freeList.head) {
m_currentBlock = 0;
return 0;
}
ASSERT(m_freeList.head);
void* head = tryPopFreeList(bytes);
ASSERT(head);
m_markedSpace->didAllocateInBlock(m_currentBlock);
return head;
}
inline void* MarkedAllocator::tryPopFreeList(size_t bytes)
{
ASSERT(m_currentBlock);
if (bytes > m_currentBlock->cellSize())
return 0;
MarkedBlock::FreeCell* head = m_freeList.head;
m_freeList.head = head->next;
return head;
}
inline void* MarkedAllocator::tryAllocate(size_t bytes)
{
ASSERT(!m_heap->isBusy());
m_heap->m_operationInProgress = Allocation;
void* result = tryAllocateHelper(bytes);
m_heap->m_operationInProgress = NoOperation;
ASSERT(result || !m_currentBlock);
return result;
}
ALWAYS_INLINE void MarkedAllocator::doTestCollectionsIfNeeded()
{
if (!Options::slowPathAllocsBetweenGCs())
return;
static unsigned allocationCount = 0;
if (!allocationCount) {
if (!m_heap->isDeferred())
m_heap->collectAllGarbage();
ASSERT(m_heap->m_operationInProgress == NoOperation);
}
if (++allocationCount >= Options::slowPathAllocsBetweenGCs())
allocationCount = 0;
}
void* MarkedAllocator::allocateSlowCase(size_t bytes)
{
ASSERT(m_heap->vm()->currentThreadIsHoldingAPILock());
doTestCollectionsIfNeeded();
ASSERT(!m_markedSpace->isIterating());
ASSERT(!m_freeList.head);
m_heap->didAllocate(m_freeList.bytes);
void* result = tryAllocate(bytes);
if (LIKELY(result != 0))
return result;
if (m_heap->collectIfNecessaryOrDefer()) {
result = tryAllocate(bytes);
if (result)
return result;
}
ASSERT(!m_heap->shouldCollect());
MarkedBlock* block = allocateBlock(bytes);
ASSERT(block);
addBlock(block);
result = tryAllocate(bytes);
ASSERT(result);
return result;
}
MarkedBlock* MarkedAllocator::allocateBlock(size_t bytes)
{
size_t minBlockSize = MarkedBlock::blockSize;
size_t minAllocationSize = WTF::roundUpToMultipleOf<MarkedBlock::atomSize>(sizeof(MarkedBlock)) + WTF::roundUpToMultipleOf<MarkedBlock::atomSize>(bytes);
minAllocationSize = WTF::roundUpToMultipleOf(WTF::pageSize(), minAllocationSize);
size_t blockSize = std::max(minBlockSize, minAllocationSize);
size_t cellSize = m_cellSize ? m_cellSize : WTF::roundUpToMultipleOf<MarkedBlock::atomSize>(bytes);
return MarkedBlock::create(*m_heap, this, blockSize, cellSize, m_needsDestruction);
}
void MarkedAllocator::addBlock(MarkedBlock* block)
{
ASSERT(!m_currentBlock);
ASSERT(!m_freeList.head);
m_blockList.append(block);
m_nextBlockToSweep = block;
m_markedSpace->didAddBlock(block);
}
void MarkedAllocator::removeBlock(MarkedBlock* block)
{
if (m_currentBlock == block) {
m_currentBlock = m_currentBlock->next();
m_freeList = MarkedBlock::FreeList();
}
if (m_nextBlockToSweep == block)
m_nextBlockToSweep = m_nextBlockToSweep->next();
block->willRemoveBlock();
m_blockList.remove(block);
}
void MarkedAllocator::reset()
{
m_lastActiveBlock = 0;
m_currentBlock = 0;
m_freeList = MarkedBlock::FreeList();
if (m_heap->operationInProgress() == FullCollection)
m_blockList.append(m_retiredBlocks);
m_nextBlockToSweep = m_blockList.head();
}
struct LastChanceToFinalize : MarkedBlock::VoidFunctor {
void operator()(MarkedBlock* block) { block->lastChanceToFinalize(); }
};
void MarkedAllocator::lastChanceToFinalize()
{
m_blockList.append(m_retiredBlocks);
LastChanceToFinalize functor;
forEachBlock(functor);
}
} // namespace JSC