blob: 8b43e8b53fa2e0ae2f4c68ca13c53f264aa13b2c [file] [log] [blame]
// Copyright 2015 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 HeapAllocator_h
#define HeapAllocator_h
#include "platform/heap/Heap.h"
#include "platform/heap/TraceTraits.h"
#include "wtf/Allocator.h"
#include "wtf/Assertions.h"
#include "wtf/Deque.h"
#include "wtf/HashCountedSet.h"
#include "wtf/HashMap.h"
#include "wtf/HashSet.h"
#include "wtf/HashTable.h"
#include "wtf/LinkedHashSet.h"
#include "wtf/ListHashSet.h"
#include "wtf/TypeTraits.h"
#include "wtf/Vector.h"
namespace blink {
template<typename T, typename Traits = WTF::VectorTraits<T>> class HeapVectorBacking {
DISALLOW_NEW();
public:
static void finalize(void* pointer);
void finalizeGarbageCollectedObject() { finalize(this); }
};
template<typename Table> class HeapHashTableBacking {
DISALLOW_NEW();
public:
static void finalize(void* pointer);
void finalizeGarbageCollectedObject() { finalize(this); }
};
// This is a static-only class used as a trait on collections to make them heap
// allocated. However see also HeapListHashSetAllocator.
class PLATFORM_EXPORT HeapAllocator {
STATIC_ONLY(HeapAllocator);
public:
using Visitor = blink::Visitor;
static const bool isGarbageCollected = true;
template<typename T>
static size_t quantizedSize(size_t count)
{
RELEASE_ASSERT(count <= maxHeapObjectSize / sizeof(T));
return ThreadHeap::allocationSizeFromSize(count * sizeof(T)) - sizeof(HeapObjectHeader);
}
template <typename T>
static T* allocateVectorBacking(size_t size)
{
ThreadState* state = ThreadStateFor<ThreadingTrait<T>::Affinity>::state();
ASSERT(state->isAllocationAllowed());
size_t gcInfoIndex = GCInfoTrait<HeapVectorBacking<T>>::index();
NormalPageArena* arena = static_cast<NormalPageArena*>(state->vectorBackingArena(gcInfoIndex));
return reinterpret_cast<T*>(arena->allocateObject(ThreadHeap::allocationSizeFromSize(size), gcInfoIndex));
}
template <typename T>
static T* allocateExpandedVectorBacking(size_t size)
{
ThreadState* state = ThreadStateFor<ThreadingTrait<T>::Affinity>::state();
ASSERT(state->isAllocationAllowed());
size_t gcInfoIndex = GCInfoTrait<HeapVectorBacking<T>>::index();
NormalPageArena* arena = static_cast<NormalPageArena*>(state->expandedVectorBackingArena(gcInfoIndex));
return reinterpret_cast<T*>(arena->allocateObject(ThreadHeap::allocationSizeFromSize(size), gcInfoIndex));
}
static void freeVectorBacking(void*);
static bool expandVectorBacking(void*, size_t);
static bool shrinkVectorBacking(void* address, size_t quantizedCurrentSize, size_t quantizedShrunkSize);
template <typename T>
static T* allocateInlineVectorBacking(size_t size)
{
size_t gcInfoIndex = GCInfoTrait<HeapVectorBacking<T>>::index();
ThreadState* state = ThreadStateFor<ThreadingTrait<T>::Affinity>::state();
const char* typeName = WTF_HEAP_PROFILER_TYPE_NAME(HeapVectorBacking<T>);
return reinterpret_cast<T*>(ThreadHeap::allocateOnArenaIndex(state, size, BlinkGC::InlineVectorArenaIndex, gcInfoIndex, typeName));
}
static void freeInlineVectorBacking(void*);
static bool expandInlineVectorBacking(void*, size_t);
static bool shrinkInlineVectorBacking(void* address, size_t quantizedCurrentSize, size_t quantizedShrunkSize);
template <typename T, typename HashTable>
static T* allocateHashTableBacking(size_t size)
{
size_t gcInfoIndex = GCInfoTrait<HeapHashTableBacking<HashTable>>::index();
ThreadState* state = ThreadStateFor<ThreadingTrait<T>::Affinity>::state();
const char* typeName = WTF_HEAP_PROFILER_TYPE_NAME(HeapHashTableBacking<HashTable>);
return reinterpret_cast<T*>(ThreadHeap::allocateOnArenaIndex(state, size, BlinkGC::HashTableArenaIndex, gcInfoIndex, typeName));
}
template <typename T, typename HashTable>
static T* allocateZeroedHashTableBacking(size_t size)
{
return allocateHashTableBacking<T, HashTable>(size);
}
static void freeHashTableBacking(void* address);
static bool expandHashTableBacking(void*, size_t);
template <typename Return, typename Metadata>
static Return malloc(size_t size, const char* typeName)
{
return reinterpret_cast<Return>(ThreadHeap::allocate<Metadata>(size, IsEagerlyFinalizedType<Metadata>::value));
}
static void free(void* address) { }
template<typename T>
static void* newArray(size_t bytes)
{
ASSERT_NOT_REACHED();
return 0;
}
static void deleteArray(void* ptr)
{
ASSERT_NOT_REACHED();
}
static bool isAllocationAllowed()
{
return ThreadState::current()->isAllocationAllowed();
}
template<typename T>
static bool isHeapObjectAlive(T* object)
{
return ThreadHeap::isHeapObjectAlive(object);
}
template<typename VisitorDispatcher>
static void markNoTracing(VisitorDispatcher visitor, const void* t) { visitor->markNoTracing(t); }
template<typename VisitorDispatcher, typename T, typename Traits>
static void trace(VisitorDispatcher visitor, T& t)
{
TraceCollectionIfEnabled<WTF::NeedsTracingTrait<Traits>::value, Traits::weakHandlingFlag, WTF::WeakPointersActWeak, T, Traits>::trace(visitor, t);
}
template<typename VisitorDispatcher>
static void registerDelayedMarkNoTracing(VisitorDispatcher visitor, const void* object)
{
visitor->registerDelayedMarkNoTracing(object);
}
template<typename VisitorDispatcher>
static void registerWeakMembers(VisitorDispatcher visitor, const void* closure, const void* object, WeakCallback callback)
{
visitor->registerWeakMembers(closure, object, callback);
}
template<typename VisitorDispatcher>
static void registerWeakTable(VisitorDispatcher visitor, const void* closure, EphemeronCallback iterationCallback, EphemeronCallback iterationDoneCallback)
{
visitor->registerWeakTable(closure, iterationCallback, iterationDoneCallback);
}
#if ENABLE(ASSERT)
template<typename VisitorDispatcher>
static bool weakTableRegistered(VisitorDispatcher visitor, const void* closure)
{
return visitor->weakTableRegistered(closure);
}
#endif
static void enterGCForbiddenScope()
{
ThreadState::current()->enterGCForbiddenScope();
}
static void leaveGCForbiddenScope()
{
ThreadState::current()->leaveGCForbiddenScope();
}
private:
static void backingFree(void*);
static bool backingExpand(void*, size_t);
static bool backingShrink(void*, size_t quantizedCurrentSize, size_t quantizedShrunkSize);
template<typename T, size_t u, typename V> friend class WTF::Vector;
template<typename T, typename U, typename V, typename W> friend class WTF::HashSet;
template<typename T, typename U, typename V, typename W, typename X, typename Y> friend class WTF::HashMap;
};
template<typename VisitorDispatcher, typename Value>
static void traceListHashSetValue(VisitorDispatcher visitor, Value& value)
{
// We use the default hash traits for the value in the node, because
// ListHashSet does not let you specify any specific ones.
// We don't allow ListHashSet of WeakMember, so we set that one false
// (there's an assert elsewhere), but we have to specify some value for the
// strongify template argument, so we specify WTF::WeakPointersActWeak,
// arbitrarily.
TraceCollectionIfEnabled<WTF::NeedsTracingTrait<WTF::HashTraits<Value>>::value, WTF::NoWeakHandlingInCollections, WTF::WeakPointersActWeak, Value, WTF::HashTraits<Value>>::trace(visitor, value);
}
// The inline capacity is just a dummy template argument to match the off-heap
// allocator.
// This inherits from the static-only HeapAllocator trait class, but we do
// declare pointers to instances. These pointers are always null, and no
// objects are instantiated.
template<typename ValueArg, size_t inlineCapacity>
class HeapListHashSetAllocator : public HeapAllocator {
DISALLOW_NEW();
public:
using TableAllocator = HeapAllocator;
using Node = WTF::ListHashSetNode<ValueArg, HeapListHashSetAllocator>;
class AllocatorProvider {
DISALLOW_NEW();
public:
// For the heap allocation we don't need an actual allocator object, so
// we just return null.
HeapListHashSetAllocator* get() const { return 0; }
// No allocator object is needed.
void createAllocatorIfNeeded() { }
void releaseAllocator() { }
// There is no allocator object in the HeapListHashSet (unlike in the
// regular ListHashSet) so there is nothing to swap.
void swap(AllocatorProvider& other) { }
};
void deallocate(void* dummy) { }
// This is not a static method even though it could be, because it needs to
// match the one that the (off-heap) ListHashSetAllocator has. The 'this'
// pointer will always be null.
void* allocateNode()
{
// Consider using a LinkedHashSet instead if this compile-time assert fails:
static_assert(!WTF::IsWeak<ValueArg>::value, "weak pointers in a ListHashSet will result in null entries in the set");
return malloc<void*, Node>(sizeof(Node), nullptr /* Oilpan does not use the heap profiler at the moment. */);
}
template<typename VisitorDispatcher>
static void traceValue(VisitorDispatcher visitor, Node* node)
{
traceListHashSetValue(visitor, node->m_value);
}
};
template<typename T, typename Traits>
void HeapVectorBacking<T, Traits>::finalize(void* pointer)
{
static_assert(Traits::needsDestruction, "Only vector buffers with items requiring destruction should be finalized");
// See the comment in HeapVectorBacking::trace.
static_assert(Traits::canClearUnusedSlotsWithMemset || std::is_polymorphic<T>::value, "HeapVectorBacking doesn't support objects that cannot be cleared as unused with memset or don't have a vtable");
ASSERT(!WTF::IsTriviallyDestructible<T>::value);
HeapObjectHeader* header = HeapObjectHeader::fromPayload(pointer);
ASSERT(header->checkHeader());
// Use the payload size as recorded by the heap to determine how many
// elements to finalize.
size_t length = header->payloadSize() / sizeof(T);
T* buffer = reinterpret_cast<T*>(pointer);
#ifdef ANNOTATE_CONTIGUOUS_CONTAINER
// As commented above, HeapVectorBacking calls finalizers for unused slots
// (which are already zeroed out).
ANNOTATE_CHANGE_SIZE(buffer, length, 0, length);
#endif
if (std::is_polymorphic<T>::value) {
for (unsigned i = 0; i < length; ++i) {
if (blink::vTableInitialized(&buffer[i]))
buffer[i].~T();
}
} else {
for (unsigned i = 0; i < length; ++i) {
buffer[i].~T();
}
}
}
template<typename Table>
void HeapHashTableBacking<Table>::finalize(void* pointer)
{
using Value = typename Table::ValueType;
ASSERT(!WTF::IsTriviallyDestructible<Value>::value);
HeapObjectHeader* header = HeapObjectHeader::fromPayload(pointer);
ASSERT(header->checkHeader());
// Use the payload size as recorded by the heap to determine how many
// elements to finalize.
size_t length = header->payloadSize() / sizeof(Value);
Value* table = reinterpret_cast<Value*>(pointer);
for (unsigned i = 0; i < length; ++i) {
if (!Table::isEmptyOrDeletedBucket(table[i]))
table[i].~Value();
}
}
template<
typename KeyArg,
typename MappedArg,
typename HashArg = typename DefaultHash<KeyArg>::Hash,
typename KeyTraitsArg = HashTraits<KeyArg>,
typename MappedTraitsArg = HashTraits<MappedArg>>
class HeapHashMap : public HashMap<KeyArg, MappedArg, HashArg, KeyTraitsArg, MappedTraitsArg, HeapAllocator> {
IS_GARBAGE_COLLECTED_TYPE();
static_assert(WTF::IsWeak<KeyArg>::value || WTF::IsWeak<MappedArg>::value || WTF::NeedsTracing<KeyArg>::value || WTF::NeedsTracing<MappedArg>::value, "For hash maps without traceable elements, use HashMap<> instead of HeapHashMap<>");
};
template<
typename ValueArg,
typename HashArg = typename DefaultHash<ValueArg>::Hash,
typename TraitsArg = HashTraits<ValueArg>>
class HeapHashSet : public HashSet<ValueArg, HashArg, TraitsArg, HeapAllocator> {
IS_GARBAGE_COLLECTED_TYPE();
static_assert(WTF::IsWeak<ValueArg>::value || WTF::NeedsTracing<ValueArg>::value, "For hash sets without traceable elements, use HashSet<> instead of HeapHashSet<>");
};
template<
typename ValueArg,
typename HashArg = typename DefaultHash<ValueArg>::Hash,
typename TraitsArg = HashTraits<ValueArg>>
class HeapLinkedHashSet : public LinkedHashSet<ValueArg, HashArg, TraitsArg, HeapAllocator> {
IS_GARBAGE_COLLECTED_TYPE();
static_assert(WTF::IsWeak<ValueArg>::value || WTF::NeedsTracing<ValueArg>::value, "For sets without traceable elements, use LinkedHashSet<> instead of HeapLinkedHashSet<>");
};
template<
typename ValueArg,
size_t inlineCapacity = 0, // The inlineCapacity is just a dummy to match ListHashSet (off-heap).
typename HashArg = typename DefaultHash<ValueArg>::Hash>
class HeapListHashSet : public ListHashSet<ValueArg, inlineCapacity, HashArg, HeapListHashSetAllocator<ValueArg, inlineCapacity>> {
IS_GARBAGE_COLLECTED_TYPE();
static_assert(WTF::IsWeak<ValueArg>::value || WTF::NeedsTracing<ValueArg>::value, "For sets without traceable elements, use ListHashSet<> instead of HeapListHashSet<>");
};
template<
typename Value,
typename HashFunctions = typename DefaultHash<Value>::Hash,
typename Traits = HashTraits<Value>>
class HeapHashCountedSet : public HashCountedSet<Value, HashFunctions, Traits, HeapAllocator> {
IS_GARBAGE_COLLECTED_TYPE();
static_assert(WTF::IsWeak<Value>::value || WTF::NeedsTracing<Value>::value, "For counted sets without traceable elements, use HashCountedSet<> instead of HeapHashCountedSet<>");
};
template<typename T, size_t inlineCapacity = 0>
class HeapVector : public Vector<T, inlineCapacity, HeapAllocator> {
IS_GARBAGE_COLLECTED_TYPE();
public:
HeapVector()
{
static_assert(WTF::NeedsTracing<T>::value, "For vectors without traceable elements, use Vector<> instead of HeapVector<>");
}
explicit HeapVector(size_t size) : Vector<T, inlineCapacity, HeapAllocator>(size)
{
}
HeapVector(size_t size, const T& val) : Vector<T, inlineCapacity, HeapAllocator>(size, val)
{
}
template<size_t otherCapacity>
HeapVector(const HeapVector<T, otherCapacity>& other)
: Vector<T, inlineCapacity, HeapAllocator>(other)
{
}
};
template<typename T, size_t inlineCapacity = 0>
class HeapDeque : public Deque<T, inlineCapacity, HeapAllocator> {
IS_GARBAGE_COLLECTED_TYPE();
public:
HeapDeque()
{
static_assert(WTF::NeedsTracing<T>::value, "For vectors without traceable elements, use Deque<> instead of HeapDeque<>");
}
explicit HeapDeque(size_t size) : Deque<T, inlineCapacity, HeapAllocator>(size)
{
}
HeapDeque(size_t size, const T& val) : Deque<T, inlineCapacity, HeapAllocator>(size, val)
{
}
HeapDeque& operator=(const HeapDeque& other)
{
HeapDeque<T> copy(other);
Deque<T, inlineCapacity, HeapAllocator>::swap(copy);
return *this;
}
template<size_t otherCapacity>
HeapDeque(const HeapDeque<T, otherCapacity>& other)
: Deque<T, inlineCapacity, HeapAllocator>(other)
{
}
};
} // namespace blink
namespace WTF {
template <typename T> struct VectorTraits<blink::Member<T>> : VectorTraitsBase<blink::Member<T>> {
STATIC_ONLY(VectorTraits);
static const bool needsDestruction = false;
static const bool canInitializeWithMemset = true;
static const bool canClearUnusedSlotsWithMemset = true;
static const bool canMoveWithMemcpy = true;
};
template <typename T> struct VectorTraits<blink::WeakMember<T>> : VectorTraitsBase<blink::WeakMember<T>> {
STATIC_ONLY(VectorTraits);
static const bool needsDestruction = false;
static const bool canInitializeWithMemset = true;
static const bool canClearUnusedSlotsWithMemset = true;
static const bool canMoveWithMemcpy = true;
};
template <typename T> struct VectorTraits<blink::UntracedMember<T>> : VectorTraitsBase<blink::UntracedMember<T>> {
STATIC_ONLY(VectorTraits);
static const bool needsDestruction = false;
static const bool canInitializeWithMemset = true;
static const bool canClearUnusedSlotsWithMemset = true;
static const bool canMoveWithMemcpy = true;
};
template <typename T> struct VectorTraits<blink::HeapVector<T, 0>> : VectorTraitsBase<blink::HeapVector<T, 0>> {
STATIC_ONLY(VectorTraits);
static const bool needsDestruction = false;
static const bool canInitializeWithMemset = true;
static const bool canClearUnusedSlotsWithMemset = true;
static const bool canMoveWithMemcpy = true;
};
template <typename T> struct VectorTraits<blink::HeapDeque<T, 0>> : VectorTraitsBase<blink::HeapDeque<T, 0>> {
STATIC_ONLY(VectorTraits);
static const bool needsDestruction = false;
static const bool canInitializeWithMemset = true;
static const bool canClearUnusedSlotsWithMemset = true;
static const bool canMoveWithMemcpy = true;
};
template <typename T, size_t inlineCapacity> struct VectorTraits<blink::HeapVector<T, inlineCapacity>> : VectorTraitsBase<blink::HeapVector<T, inlineCapacity>> {
STATIC_ONLY(VectorTraits);
static const bool needsDestruction = VectorTraits<T>::needsDestruction;
static const bool canInitializeWithMemset = VectorTraits<T>::canInitializeWithMemset;
static const bool canClearUnusedSlotsWithMemset = VectorTraits<T>::canClearUnusedSlotsWithMemset;
static const bool canMoveWithMemcpy = VectorTraits<T>::canMoveWithMemcpy;
};
template <typename T, size_t inlineCapacity> struct VectorTraits<blink::HeapDeque<T, inlineCapacity>> : VectorTraitsBase<blink::HeapDeque<T, inlineCapacity>> {
STATIC_ONLY(VectorTraits);
static const bool needsDestruction = VectorTraits<T>::needsDestruction;
static const bool canInitializeWithMemset = VectorTraits<T>::canInitializeWithMemset;
static const bool canClearUnusedSlotsWithMemset = VectorTraits<T>::canClearUnusedSlotsWithMemset;
static const bool canMoveWithMemcpy = VectorTraits<T>::canMoveWithMemcpy;
};
template<typename T> struct HashTraits<blink::Member<T>> : SimpleClassHashTraits<blink::Member<T>> {
STATIC_ONLY(HashTraits);
// FIXME: The distinction between PeekInType and PassInType is there for
// the sake of the reference counting handles. When they are gone the two
// types can be merged into PassInType.
// FIXME: Implement proper const'ness for iterator types. Requires support
// in the marking Visitor.
using PeekInType = T*;
using PassInType = T*;
using IteratorGetType = blink::Member<T>*;
using IteratorConstGetType = const blink::Member<T>*;
using IteratorReferenceType = blink::Member<T>&;
using IteratorConstReferenceType = const blink::Member<T>&;
static IteratorReferenceType getToReferenceConversion(IteratorGetType x) { return *x; }
static IteratorConstReferenceType getToReferenceConstConversion(IteratorConstGetType x) { return *x; }
using PeekOutType = T*;
template<typename U>
static void store(const U& value, blink::Member<T>& storage) { storage = value; }
static PeekOutType peek(const blink::Member<T>& value) { return value; }
};
template<typename T> struct HashTraits<blink::WeakMember<T>> : SimpleClassHashTraits<blink::WeakMember<T>> {
STATIC_ONLY(HashTraits);
static const bool needsDestruction = false;
// FIXME: The distinction between PeekInType and PassInType is there for
// the sake of the reference counting handles. When they are gone the two
// types can be merged into PassInType.
// FIXME: Implement proper const'ness for iterator types. Requires support
// in the marking Visitor.
using PeekInType = T*;
using PassInType = T*;
using IteratorGetType = blink::WeakMember<T>*;
using IteratorConstGetType = const blink::WeakMember<T>*;
using IteratorReferenceType = blink::WeakMember<T>&;
using IteratorConstReferenceType = const blink::WeakMember<T>&;
static IteratorReferenceType getToReferenceConversion(IteratorGetType x) { return *x; }
static IteratorConstReferenceType getToReferenceConstConversion(IteratorConstGetType x) { return *x; }
using PeekOutType = T*;
template<typename U>
static void store(const U& value, blink::WeakMember<T>& storage) { storage = value; }
static PeekOutType peek(const blink::WeakMember<T>& value) { return value; }
template<typename VisitorDispatcher>
static bool traceInCollection(VisitorDispatcher visitor, blink::WeakMember<T>& weakMember, ShouldWeakPointersBeMarkedStrongly strongify)
{
if (strongify == WeakPointersActStrong) {
visitor->trace(weakMember.get()); // Strongified visit.
return false;
}
return !blink::ThreadHeap::isHeapObjectAlive(weakMember);
}
};
template<typename T> struct HashTraits<blink::UntracedMember<T>> : SimpleClassHashTraits<blink::UntracedMember<T>> {
STATIC_ONLY(HashTraits);
static const bool needsDestruction = false;
// FIXME: The distinction between PeekInType and PassInType is there for
// the sake of the reference counting handles. When they are gone the two
// types can be merged into PassInType.
// FIXME: Implement proper const'ness for iterator types.
using PeekInType = T*;
using PassInType = T*;
using IteratorGetType = blink::UntracedMember<T>*;
using IteratorConstGetType = const blink::UntracedMember<T>*;
using IteratorReferenceType = blink::UntracedMember<T>&;
using IteratorConstReferenceType = const blink::UntracedMember<T>&;
static IteratorReferenceType getToReferenceConversion(IteratorGetType x) { return *x; }
static IteratorConstReferenceType getToReferenceConstConversion(IteratorConstGetType x) { return *x; }
using PeekOutType = T*;
template<typename U>
static void store(const U& value, blink::UntracedMember<T>& storage) { storage = value; }
static PeekOutType peek(const blink::UntracedMember<T>& value) { return value; }
};
template<typename T, size_t inlineCapacity>
struct NeedsTracing<ListHashSetNode<T, blink::HeapListHashSetAllocator<T, inlineCapacity>> *> {
STATIC_ONLY(NeedsTracing);
static_assert(sizeof(T), "T must be fully defined");
// All heap allocated node pointers need visiting to keep the nodes alive,
// regardless of whether they contain pointers to other heap allocated
// objects.
static const bool value = true;
};
} // namespace WTF
#endif