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..fa18fa32cff10145a0f7fe1bd0dece9d240dd449 |
--- /dev/null |
+++ b/Source/platform/heap/PersistentNode.h |
@@ -0,0 +1,183 @@ |
+// 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; |
+ } |
+ |
+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; |
+}; |
+ |
+// 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: |
+ 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); |
+ } |
+ |
+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 |