Chromium Code Reviews| Index: Source/platform/heap/PersistentNode.h |
| diff --git a/Source/platform/heap/PersistentNode.h b/Source/platform/heap/PersistentNode.h |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..4628e7919f9816701dd5bba433d3ac3ffbb26f7a |
| --- /dev/null |
| +++ b/Source/platform/heap/PersistentNode.h |
| @@ -0,0 +1,177 @@ |
| +// 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/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(isFree()); |
| + } |
| + |
| + ~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() || isFree()); |
| + } |
| + |
| + // 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(!isFree()); |
| + ASSERT(m_trace); |
| + m_trace(visitor, m_self); |
| + } |
| + |
| + void initialize(void* self, TraceCallback trace) |
| + { |
| + ASSERT(isFree()); |
| + m_self = self; |
| + m_trace = trace; |
| + } |
| + |
| + void setFreeNext(PersistentNode* node) |
| + { |
| + ASSERT(!node || node->isFree()); |
| + m_self = node; |
| + m_trace = nullptr; |
| + ASSERT(isFree()); |
| + } |
| + |
| + PersistentNode* freeNext() |
| + { |
| + ASSERT(isFree()); |
| + PersistentNode* node = reinterpret_cast<PersistentNode*>(m_self); |
| + ASSERT(!node || node->isFree()); |
| + return node; |
| + } |
| + |
| + bool isFree() const |
|
sof
2015/06/30 09:19:39
"free" feels a bit overloaded in this context - "u
haraken
2015/06/30 09:51:32
Done.
|
| + { |
| + return !m_trace; |
| + } |
| + |
| +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; |
| +}; |
| + |
| +class PersistentRegion final { |
|
sof
2015/06/30 09:19:39
Could you add a comment outlining what this provid
|
| +public: |
| + PersistentRegion() |
| + : m_freeHead(nullptr) |
| + , m_slots(nullptr) |
| +#if ENABLE(ASSERT) |
| + , m_numberOfPersistents(0) |
| +#endif |
| + { |
| + } |
| + ~PersistentRegion(); |
| + |
| + PersistentNode* allocatePersistentNode(void* self, TraceCallback trace) |
| + { |
| +#if ENABLE(ASSERT) |
| + ++m_numberOfPersistents; |
| +#endif |
| + if (UNLIKELY(!m_freeHead)) |
| + ensurePersistentNodeSlots(self, trace); |
| + ASSERT(m_freeHead); |
| + PersistentNode* node = m_freeHead; |
| + m_freeHead = m_freeHead->freeNext(); |
| + node->initialize(self, trace); |
| + ASSERT(!node->isFree()); |
| + return node; |
| + } |
| + void ensurePersistentNodeSlots(void*, TraceCallback); |
|
sof
2015/06/30 09:19:39
make this private?
haraken
2015/06/30 09:51:32
Done.
|
| + void freePersistentNode(PersistentNode* persistentNode) |
| + { |
| + ASSERT(m_numberOfPersistents > 0); |
| + persistentNode->setFreeNext(m_freeHead); |
| + m_freeHead = persistentNode; |
| +#if ENABLE(ASSERT) |
| + --m_numberOfPersistents; |
| +#endif |
| + } |
| + void tracePersistentNodes(Visitor*); |
| + int numberOfPersistents(); |
| + |
| +private: |
| + PersistentNode* m_freeHead; |
| + PersistentNodeSlots* m_slots; |
| +#if ENABLE(ASSERT) |
| + int m_numberOfPersistents; |
|
sof
2015/06/30 09:19:39
Did you consider caching this count outside of ENA
haraken
2015/06/30 09:51:32
At first I did this, but it added extra overhead t
|
| +#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); |
| + } |
| + |
| +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 |