blob: 377c6214e3da00934985d4b6d45c775908f2e14b [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.
#ifndef CallbackStack_h
#define CallbackStack_h
#include "platform/heap/BlinkGC.h"
#include "wtf/Allocator.h"
#include "wtf/Assertions.h"
#include "wtf/Threading.h"
#include "wtf/ThreadingPrimitives.h"
namespace blink {
// The CallbackStack contains all the visitor callbacks used to trace and mark
// objects. A specific CallbackStack instance contains at most bufferSize
// elements.
// If more space is needed a new CallbackStack instance is created and chained
// together with the former instance. I.e. a logical CallbackStack can be made
// of multiple chained CallbackStack object instances.
class CallbackStack final {
USING_FAST_MALLOC(CallbackStack);
public:
class Item {
DISALLOW_NEW();
public:
Item() {}
Item(void* object, VisitorCallback callback)
: m_object(object), m_callback(callback) {}
void* object() { return m_object; }
VisitorCallback callback() { return m_callback; }
void call(Visitor* visitor) { m_callback(visitor, m_object); }
private:
void* m_object;
VisitorCallback m_callback;
};
static std::unique_ptr<CallbackStack> create();
~CallbackStack();
void commit();
void decommit();
Item* allocateEntry();
Item* pop();
bool isEmpty() const;
void invokeEphemeronCallbacks(Visitor*);
#if ENABLE(ASSERT)
bool hasCallbackForObject(const void*);
#endif
bool hasJustOneBlock() const;
static const size_t kMinimalBlockSize;
static const size_t kDefaultBlockSize = (1 << 13);
private:
class Block {
USING_FAST_MALLOC(Block);
public:
explicit Block(Block* next);
~Block();
#if ENABLE(ASSERT)
void clear();
#endif
Block* next() const { return m_next; }
void setNext(Block* next) { m_next = next; }
bool isEmptyBlock() const { return m_current == &(m_buffer[0]); }
size_t blockSize() const { return m_blockSize; }
Item* allocateEntry() {
if (LIKELY(m_current < m_limit))
return m_current++;
return nullptr;
}
Item* pop() {
if (UNLIKELY(isEmptyBlock()))
return nullptr;
return --m_current;
}
void invokeEphemeronCallbacks(Visitor*);
#if ENABLE(ASSERT)
bool hasCallbackForObject(const void*);
#endif
private:
size_t m_blockSize;
Item* m_buffer;
Item* m_limit;
Item* m_current;
Block* m_next;
};
CallbackStack();
Item* popSlow();
Item* allocateEntrySlow();
void invokeOldestCallbacks(Block*, Block*, Visitor*);
Block* m_first;
Block* m_last;
};
class CallbackStackMemoryPool final {
USING_FAST_MALLOC(CallbackStackMemoryPool);
public:
// 2048 * 8 * sizeof(Item) = 256 KB (64bit) is pre-allocated for the
// underlying buffer of CallbackStacks.
static const size_t kBlockSize = 2048;
static const size_t kPooledBlockCount = 8;
static const size_t kBlockBytes = kBlockSize * sizeof(CallbackStack::Item);
static CallbackStackMemoryPool& instance();
void initialize();
void shutdown();
CallbackStack::Item* allocate();
void free(CallbackStack::Item*);
private:
Mutex m_mutex;
int m_freeListFirst;
int m_freeListNext[kPooledBlockCount];
CallbackStack::Item* m_pooledMemory;
};
ALWAYS_INLINE CallbackStack::Item* CallbackStack::allocateEntry() {
DCHECK(m_first);
Item* item = m_first->allocateEntry();
if (LIKELY(!!item))
return item;
return allocateEntrySlow();
}
ALWAYS_INLINE CallbackStack::Item* CallbackStack::pop() {
Item* item = m_first->pop();
if (LIKELY(!!item))
return item;
return popSlow();
}
} // namespace blink
#endif // CallbackStack_h