blob: 853250cc3f1cda1d706474e5835d82082d0c4208 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "config.h"
#include "platform/heap/CallbackStack.h"
#include "platform/heap/Heap.h"
namespace blink {
class CallbackStack::Block {
public:
explicit Block(Block* next)
: m_limit(&(m_buffer[blockSize]))
, m_current(&(m_buffer[0]))
, m_next(next)
{
clearUnused();
}
~Block()
{
clearUnused();
}
void clear()
{
m_current = &m_buffer[0];
m_next = 0;
clearUnused();
}
Block* next() const { return m_next; }
void setNext(Block* next) { m_next = next; }
bool isEmptyBlock() const
{
return m_current == &(m_buffer[0]);
}
size_t size() const
{
return blockSize - (m_limit - m_current);
}
Item* allocateEntry()
{
if (m_current < m_limit)
return m_current++;
return 0;
}
Item* pop()
{
if (isEmptyBlock())
return 0;
return --m_current;
}
void invokeEphemeronCallbacks(Visitor* visitor)
{
// This loop can tolerate entries being added by the callbacks after
// iteration starts.
for (unsigned i = 0; m_buffer + i < m_current; i++) {
Item& item = m_buffer[i];
// We don't need to check for orphaned pages when popping an ephemeron
// callback since the callback is only pushed after the object containing
// it has been traced. There are basically three cases to consider:
// 1. Member<EphemeronCollection>
// 2. EphemeronCollection is part of a containing object
// 3. EphemeronCollection is a value object in a collection
//
// Ad. 1. In this case we push the start of the ephemeron on the
// marking stack and do the orphaned page check when popping it off
// the marking stack.
// Ad. 2. The containing object cannot be on an orphaned page since
// in that case we wouldn't have traced its parts. This also means
// the ephemeron collection is not on the orphaned page.
// Ad. 3. Is the same as 2. The collection containing the ephemeron
// collection as a value object cannot be on an orphaned page since
// it would not have traced its values in that case.
item.call(visitor);
}
}
#if ENABLE(ASSERT)
bool hasCallbackForObject(const void* object)
{
for (unsigned i = 0; m_buffer + i < m_current; i++) {
Item* item = &m_buffer[i];
if (item->object() == object)
return true;
}
return false;
}
#endif
private:
void clearUnused()
{
#if ENABLE(ASSERT)
for (size_t i = 0; i < blockSize; i++)
m_buffer[i] = Item(0, 0);
#endif
}
Item m_buffer[blockSize];
Item* m_limit;
Item* m_current;
Block* m_next;
};
CallbackStack::CallbackStack() : m_first(new Block(0)), m_last(m_first)
{
}
CallbackStack::~CallbackStack()
{
clear();
delete m_first;
m_first = 0;
m_last = 0;
}
void CallbackStack::clear()
{
Block* next;
for (Block* current = m_first->next(); current; current = next) {
next = current->next();
delete current;
}
m_first->clear();
m_last = m_first;
}
bool CallbackStack::isEmpty() const
{
return hasJustOneBlock() && m_first->isEmptyBlock();
}
void CallbackStack::takeBlockFrom(CallbackStack* other)
{
// We assume the stealing stack is empty.
ASSERT(isEmpty());
if (other->isEmpty())
return;
if (other->hasJustOneBlock()) {
swap(other);
return;
}
// Delete our block and steal the first one from other.
delete m_first;
m_first = other->m_first;
other->m_first = m_first->next();
m_first->setNext(0);
m_last = m_first;
}
CallbackStack::Item* CallbackStack::allocateEntry()
{
if (Item* item = m_first->allocateEntry())
return item;
m_first = new Block(m_first);
return m_first->allocateEntry();
}
CallbackStack::Item* CallbackStack::pop()
{
Item* item = m_first->pop();
while (!item) {
if (hasJustOneBlock()) {
#if ENABLE(ASSERT)
m_first->clear();
#endif
return 0;
}
Block* next = m_first->next();
delete m_first;
m_first = next;
item = m_first->pop();
}
return item;
}
void CallbackStack::invokeEphemeronCallbacks(Visitor* visitor)
{
// The first block is the only one where new ephemerons are added, so we
// call the callbacks on that last, to catch any new ephemerons discovered
// in the callbacks.
// However, if enough ephemerons were added, we may have a new block that
// has been prepended to the chain. This will be very rare, but we can
// handle the situation by starting again and calling all the callbacks
// on the prepended blocks.
Block* from = 0;
Block* upto = 0;
while (from != m_first) {
upto = from;
from = m_first;
invokeOldestCallbacks(from, upto, visitor);
}
}
void CallbackStack::invokeOldestCallbacks(Block* from, Block* upto, Visitor* visitor)
{
if (from == upto)
return;
ASSERT(from);
// Recurse first (blockSize at a time) so we get to the newly added entries last.
invokeOldestCallbacks(from->next(), upto, visitor);
from->invokeEphemeronCallbacks(visitor);
}
bool CallbackStack::sizeExceeds(size_t minSize) const
{
Block* current = m_first;
for (size_t size = m_first->size(); size < minSize; size += current->size()) {
if (!current->next())
return false;
current = current->next();
}
return true;
}
void CallbackStack::append(CallbackStack* other)
{
ASSERT(!m_last->next());
ASSERT(!other->m_last->next());
m_last->setNext(other->m_first);
m_last = other->m_last;
// After append, we mark |other| as ill-formed by clearing its pointers.
other->m_first = 0;
other->m_last = 0;
}
bool CallbackStack::hasJustOneBlock() const
{
return !m_first->next();
}
void CallbackStack::swap(CallbackStack* other)
{
Block* tmp = m_first;
m_first = other->m_first;
other->m_first = tmp;
tmp = m_last;
m_last = other->m_last;
other->m_last = tmp;
}
#if ENABLE(ASSERT)
bool CallbackStack::hasCallbackForObject(const void* object)
{
for (Block* current = m_first; current; current = current->next()) {
if (current->hasCallbackForObject(object))
return true;
}
return false;
}
#endif
}