blob: 338adf2b4ca7ba114ac580986d81928571d3d502 [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 PersistentNode_h
#define PersistentNode_h
#include "platform/PlatformExport.h"
#include "platform/heap/ThreadState.h"
#include "wtf/Assertions.h"
#include "wtf/MainThread.h"
#include "wtf/ThreadingPrimitives.h"
namespace blink {
class PersistentNode final {
public:
PersistentNode()
: m_self(nullptr)
, m_trace(nullptr)
{
ASSERT(isUnused());
}
~PersistentNode()
{
// If you hit this assert, it means that the thread finished
// without clearing persistent handles that the thread created.
// We don't enable the assert for the main thread because the
// main thread finishes without clearing all persistent handles.
ASSERT(isMainThread() || isUnused());
}
// It is dangrous to copy the PersistentNode because it breaks the
// free list.
PersistentNode& operator=(const PersistentNode& otherref) = delete;
// Ideally the trace method should be virtual and automatically dispatch
// to the most specific implementation. However having a virtual method
// on PersistentNode leads to too eager template instantiation with MSVC
// which leads to include cycles.
// Instead we call the constructor with a TraceCallback which knows the
// type of the most specific child and calls trace directly. See
// TraceMethodDelegate in Visitor.h for how this is done.
void tracePersistentNode(Visitor* visitor)
{
ASSERT(!isUnused());
ASSERT(m_trace);
m_trace(visitor, m_self);
}
void initialize(void* self, TraceCallback trace)
{
ASSERT(isUnused());
m_self = self;
m_trace = trace;
}
void setFreeListNext(PersistentNode* node)
{
ASSERT(!node || node->isUnused());
m_self = node;
m_trace = nullptr;
ASSERT(isUnused());
}
PersistentNode* freeListNext()
{
ASSERT(isUnused());
PersistentNode* node = reinterpret_cast<PersistentNode*>(m_self);
ASSERT(!node || node->isUnused());
return node;
}
bool isUnused() const
{
return !m_trace;
}
void* self() const
{
return m_self;
}
private:
// If this PersistentNode is in use:
// - m_self points to the corresponding Persistent handle.
// - m_trace points to the trace method.
// If this PersistentNode is freed:
// - m_self points to the next freed PersistentNode.
// - m_trace is nullptr.
void* m_self;
TraceCallback m_trace;
};
struct PersistentNodeSlots final {
private:
static const int slotCount = 256;
PersistentNodeSlots* m_next;
PersistentNode m_slot[slotCount];
friend class PersistentRegion;
friend class CrossThreadPersistentRegion;
};
// PersistentRegion provides a region of PersistentNodes. PersistentRegion
// holds a linked list of PersistentNodeSlots, each of which stores
// a predefined number of PersistentNodes. You can call allocatePersistentNode/
// freePersistentNode to allocate/free a PersistentNode on the region.
class PLATFORM_EXPORT PersistentRegion final {
public:
PersistentRegion()
: m_freeListHead(nullptr)
, m_slots(nullptr)
#if ENABLE(ASSERT)
, m_persistentCount(0)
#endif
{
}
~PersistentRegion();
PersistentNode* allocatePersistentNode(void* self, TraceCallback trace)
{
#if ENABLE(ASSERT)
++m_persistentCount;
#endif
if (UNLIKELY(!m_freeListHead))
ensurePersistentNodeSlots(self, trace);
ASSERT(m_freeListHead);
PersistentNode* node = m_freeListHead;
m_freeListHead = m_freeListHead->freeListNext();
node->initialize(self, trace);
ASSERT(!node->isUnused());
return node;
}
void freePersistentNode(PersistentNode* persistentNode)
{
ASSERT(m_persistentCount > 0);
persistentNode->setFreeListNext(m_freeListHead);
m_freeListHead = persistentNode;
#if ENABLE(ASSERT)
--m_persistentCount;
#endif
}
void tracePersistentNodes(Visitor*);
int numberOfPersistents();
private:
friend CrossThreadPersistentRegion;
void ensurePersistentNodeSlots(void*, TraceCallback);
PersistentNode* m_freeListHead;
PersistentNodeSlots* m_slots;
#if ENABLE(ASSERT)
int m_persistentCount;
#endif
};
class CrossThreadPersistentRegion final {
public:
CrossThreadPersistentRegion() : m_persistentRegion(adoptPtr(new PersistentRegion)) { }
PersistentNode* allocatePersistentNode(void* self, TraceCallback trace)
{
MutexLocker lock(m_mutex);
return m_persistentRegion->allocatePersistentNode(self, trace);
}
void freePersistentNode(PersistentNode* persistentNode)
{
MutexLocker lock(m_mutex);
m_persistentRegion->freePersistentNode(persistentNode);
}
void tracePersistentNodes(Visitor* visitor)
{
MutexLocker lock(m_mutex);
m_persistentRegion->tracePersistentNodes(visitor);
}
void prepareForThreadStateTermination(ThreadState*);
private:
// We don't make CrossThreadPersistentRegion inherit from PersistentRegion
// because we don't want to virtualize performance-sensitive methods
// such as PersistentRegion::allocate/freePersistentNode.
OwnPtr<PersistentRegion> m_persistentRegion;
Mutex m_mutex;
};
} // namespace blink
#endif