#ifndef MarkStack_h
#define MarkStack_h
-#include "CopiedSpace.h"
-#include "HandleTypes.h"
-#include "Options.h"
-#include "JSValue.h"
-#include "Register.h"
-#include "UnconditionalFinalizer.h"
-#include "VTableSpectrum.h"
-#include "WeakReferenceHarvester.h"
-#include <wtf/HashMap.h>
-#include <wtf/HashSet.h>
-#include <wtf/Vector.h>
-#include <wtf/Noncopyable.h>
-#include <wtf/OSAllocator.h>
-#include <wtf/PageBlock.h>
-
-namespace JSC {
-
- class ConservativeRoots;
- class JSGlobalData;
- class MarkStack;
- class ParallelModeEnabler;
- class Register;
- class SlotVisitor;
- template<typename T> class WriteBarrierBase;
- template<typename T> class JITWriteBarrier;
-
- struct MarkStackSegment {
- MarkStackSegment* m_previous;
-#if !ASSERT_DISABLED
- size_t m_top;
-#endif
-
- const JSCell** data()
- {
- return bitwise_cast<const JSCell**>(this + 1);
- }
-
- static size_t capacityFromSize(size_t size)
- {
- return (size - sizeof(MarkStackSegment)) / sizeof(const JSCell*);
- }
-
- static size_t sizeFromCapacity(size_t capacity)
- {
- return sizeof(MarkStackSegment) + capacity * sizeof(const JSCell*);
- }
- };
-
- class MarkStackSegmentAllocator {
- public:
- MarkStackSegmentAllocator();
- ~MarkStackSegmentAllocator();
-
- MarkStackSegment* allocate();
- void release(MarkStackSegment*);
-
- void shrinkReserve();
-
- private:
- Mutex m_lock;
- MarkStackSegment* m_nextFreeSegment;
- };
-
- class MarkStackArray {
- public:
- MarkStackArray(MarkStackSegmentAllocator&);
- ~MarkStackArray();
-
- void append(const JSCell*);
-
- bool canRemoveLast();
- const JSCell* removeLast();
- bool refill();
-
- bool isEmpty();
-
- bool canDonateSomeCells(); // Returns false if you should definitely not call doanteSomeCellsTo().
- bool donateSomeCellsTo(MarkStackArray& other); // Returns true if some cells were donated.
-
- void stealSomeCellsFrom(MarkStackArray& other);
-
- size_t size();
-
- private:
- MarkStackSegment* m_topSegment;
-
- JS_EXPORT_PRIVATE void expand();
-
- MarkStackSegmentAllocator& m_allocator;
-
- size_t m_segmentCapacity;
- size_t m_top;
- size_t m_numberOfPreviousSegments;
-
- size_t postIncTop()
- {
- size_t result = m_top++;
- ASSERT(result == m_topSegment->m_top++);
- return result;
- }
-
- size_t preDecTop()
- {
- size_t result = --m_top;
- ASSERT(result == --m_topSegment->m_top);
- return result;
- }
-
- void setTopForFullSegment()
- {
- ASSERT(m_topSegment->m_top == m_segmentCapacity);
- m_top = m_segmentCapacity;
- }
-
- void setTopForEmptySegment()
- {
- ASSERT(!m_topSegment->m_top);
- m_top = 0;
- }
-
- size_t top()
- {
- ASSERT(m_top == m_topSegment->m_top);
- return m_top;
- }
-
-#if ASSERT_DISABLED
- void validatePrevious() { }
+#if ENABLE(OBJECT_MARK_LOGGING)
+#define MARK_LOG_MESSAGE0(message) dataLogF(message)
+#define MARK_LOG_MESSAGE1(message, arg1) dataLogF(message, arg1)
+#define MARK_LOG_MESSAGE2(message, arg1, arg2) dataLogF(message, arg1, arg2)
+#define MARK_LOG_ROOT(visitor, rootName) \
+ dataLogF("\n%s: ", rootName); \
+ (visitor).resetChildCount()
+#define MARK_LOG_PARENT(visitor, parent) \
+ dataLogF("\n%p (%s): ", parent, parent->className() ? parent->className() : "unknown"); \
+ (visitor).resetChildCount()
+#define MARK_LOG_CHILD(visitor, child) \
+ if ((visitor).childCount()) \
+ dataLogFString(", "); \
+ dataLogF("%p", child); \
+ (visitor).incrementChildCount()
#else
- void validatePrevious()
- {
- unsigned count = 0;
- for (MarkStackSegment* current = m_topSegment->m_previous; current; current = current->m_previous)
- count++;
- ASSERT(count == m_numberOfPreviousSegments);
- }
-#endif
- };
-
- class MarkStackThreadSharedData {
- public:
- MarkStackThreadSharedData(JSGlobalData*);
- ~MarkStackThreadSharedData();
-
- void reset();
-
- private:
- friend class MarkStack;
- friend class SlotVisitor;
-
-#if ENABLE(PARALLEL_GC)
- void markingThreadMain();
- static void markingThreadStartFunc(void* heap);
+#define MARK_LOG_MESSAGE0(message) do { } while (false)
+#define MARK_LOG_MESSAGE1(message, arg1) do { } while (false)
+#define MARK_LOG_MESSAGE2(message, arg1, arg2) do { } while (false)
+#define MARK_LOG_ROOT(visitor, rootName) do { } while (false)
+#define MARK_LOG_PARENT(visitor, parent) do { } while (false)
+#define MARK_LOG_CHILD(visitor, child) do { } while (false)
#endif
- JSGlobalData* m_globalData;
- CopiedSpace* m_copiedSpace;
-
- MarkStackSegmentAllocator m_segmentAllocator;
-
- Vector<ThreadIdentifier> m_markingThreads;
-
- Mutex m_markingLock;
- ThreadCondition m_markingCondition;
- MarkStackArray m_sharedMarkStack;
- unsigned m_numberOfActiveParallelMarkers;
- bool m_parallelMarkersShouldExit;
+#include "HeapBlock.h"
+#include <wtf/StdLibExtras.h>
- Mutex m_opaqueRootsLock;
- HashSet<void*> m_opaqueRoots;
-
- ListableHandler<WeakReferenceHarvester>::List m_weakReferenceHarvesters;
- ListableHandler<UnconditionalFinalizer>::List m_unconditionalFinalizers;
- };
-
- class MarkStack {
- WTF_MAKE_NONCOPYABLE(MarkStack);
- friend class HeapRootVisitor; // Allowed to mark a JSValue* or JSCell** directly.
-
- public:
- MarkStack(MarkStackThreadSharedData&);
- ~MarkStack();
-
- void append(ConservativeRoots&);
-
- template<typename T> void append(JITWriteBarrier<T>*);
- template<typename T> void append(WriteBarrierBase<T>*);
- void appendValues(WriteBarrierBase<Unknown>*, size_t count);
-
- template<typename T>
- void appendUnbarrieredPointer(T**);
-
- void addOpaqueRoot(void*);
- bool containsOpaqueRoot(void*);
- int opaqueRootCount();
-
- bool isEmpty() { return m_stack.isEmpty(); }
-
- void reset();
-
- size_t visitCount() const { return m_visitCount; }
-
-#if ENABLE(SIMPLE_HEAP_PROFILING)
- VTableSpectrum m_visitedTypeCounts;
-#endif
-
- void addWeakReferenceHarvester(WeakReferenceHarvester* weakReferenceHarvester)
- {
- m_shared.m_weakReferenceHarvesters.addThreadSafe(weakReferenceHarvester);
- }
-
- void addUnconditionalFinalizer(UnconditionalFinalizer* unconditionalFinalizer)
- {
- m_shared.m_unconditionalFinalizers.addThreadSafe(unconditionalFinalizer);
- }
-
- protected:
- JS_EXPORT_PRIVATE static void validate(JSCell*);
+namespace JSC {
- void append(JSValue*);
- void append(JSValue*, size_t count);
- void append(JSCell**);
+class BlockAllocator;
+class DeadBlock;
+class JSCell;
- void internalAppend(JSCell*);
- void internalAppend(JSValue);
-
- JS_EXPORT_PRIVATE void mergeOpaqueRoots();
-
- void mergeOpaqueRootsIfNecessary()
- {
- if (m_opaqueRoots.isEmpty())
- return;
- mergeOpaqueRoots();
- }
-
- void mergeOpaqueRootsIfProfitable()
- {
- if (static_cast<unsigned>(m_opaqueRoots.size()) < Options::opaqueRootMergeThreshold)
- return;
- mergeOpaqueRoots();
- }
-
- MarkStackArray m_stack;
- HashSet<void*> m_opaqueRoots; // Handle-owning data structures not visible to the garbage collector.
-
+class MarkStackSegment : public HeapBlock<MarkStackSegment> {
+public:
+ MarkStackSegment(Region* region)
+ : HeapBlock<MarkStackSegment>(region)
#if !ASSERT_DISABLED
- public:
- bool m_isCheckingForDefaultMarkViolation;
- bool m_isDraining;
+ , m_top(0)
#endif
- protected:
- friend class ParallelModeEnabler;
-
- size_t m_visitCount;
- bool m_isInParallelMode;
-
- MarkStackThreadSharedData& m_shared;
- };
-
- inline MarkStack::MarkStack(MarkStackThreadSharedData& shared)
- : m_stack(shared.m_segmentAllocator)
-#if !ASSERT_DISABLED
- , m_isCheckingForDefaultMarkViolation(false)
- , m_isDraining(false)
-#endif
- , m_visitCount(0)
- , m_isInParallelMode(false)
- , m_shared(shared)
{
}
- inline MarkStack::~MarkStack()
- {
- ASSERT(m_stack.isEmpty());
- }
+ static MarkStackSegment* create(DeadBlock*);
- inline void MarkStack::addOpaqueRoot(void* root)
+ const JSCell** data()
{
-#if ENABLE(PARALLEL_GC)
- if (Options::numberOfGCMarkers == 1) {
- // Put directly into the shared HashSet.
- m_shared.m_opaqueRoots.add(root);
- return;
- }
- // Put into the local set, but merge with the shared one every once in
- // a while to make sure that the local sets don't grow too large.
- mergeOpaqueRootsIfProfitable();
- m_opaqueRoots.add(root);
-#else
- m_opaqueRoots.add(root);
-#endif
+ return bitwise_cast<const JSCell**>(this + 1);
}
- inline bool MarkStack::containsOpaqueRoot(void* root)
- {
- ASSERT(!m_isInParallelMode);
-#if ENABLE(PARALLEL_GC)
- ASSERT(m_opaqueRoots.isEmpty());
- return m_shared.m_opaqueRoots.contains(root);
-#else
- return m_opaqueRoots.contains(root);
-#endif
- }
+ static const size_t blockSize = 4 * KB;
- inline int MarkStack::opaqueRootCount()
- {
- ASSERT(!m_isInParallelMode);
-#if ENABLE(PARALLEL_GC)
- ASSERT(m_opaqueRoots.isEmpty());
- return m_shared.m_opaqueRoots.size();
-#else
- return m_opaqueRoots.size();
+#if !ASSERT_DISABLED
+ size_t m_top;
#endif
- }
+};
- inline void MarkStackArray::append(const JSCell* cell)
- {
- if (m_top == m_segmentCapacity)
- expand();
- m_topSegment->data()[postIncTop()] = cell;
- }
+class MarkStackArray {
+public:
+ MarkStackArray(BlockAllocator&);
+ ~MarkStackArray();
- inline bool MarkStackArray::canRemoveLast()
- {
- return !!m_top;
- }
-
- inline const JSCell* MarkStackArray::removeLast()
- {
- return m_topSegment->data()[preDecTop()];
- }
-
- inline bool MarkStackArray::isEmpty()
- {
- if (m_top)
- return false;
- if (m_topSegment->m_previous) {
- ASSERT(m_topSegment->m_previous->m_top == m_segmentCapacity);
- return false;
- }
- return true;
- }
+ void append(const JSCell*);
- inline bool MarkStackArray::canDonateSomeCells()
- {
- size_t numberOfCellsToKeep = Options::minimumNumberOfCellsToKeep;
- // Another check: see if we have enough cells to warrant donation.
- if (m_top <= numberOfCellsToKeep) {
- // This indicates that we might not want to donate anything; check if we have
- // another full segment. If not, then don't donate.
- if (!m_topSegment->m_previous)
- return false;
-
- ASSERT(m_topSegment->m_previous->m_top == m_segmentCapacity);
- }
-
- return true;
- }
+ bool canRemoveLast();
+ const JSCell* removeLast();
+ bool refill();
+
+ void donateSomeCellsTo(MarkStackArray& other);
+ void stealSomeCellsFrom(MarkStackArray& other, size_t idleThreadCount);
- inline size_t MarkStackArray::size()
- {
- return m_top + m_segmentCapacity * m_numberOfPreviousSegments;
- }
+ size_t size();
+ bool isEmpty();
- ALWAYS_INLINE void MarkStack::append(JSValue* slot, size_t count)
- {
- for (size_t i = 0; i < count; ++i) {
- JSValue& value = slot[i];
- if (!value)
- continue;
- internalAppend(value);
- }
- }
+private:
+ template <size_t size> struct CapacityFromSize {
+ static const size_t value = (size - sizeof(MarkStackSegment)) / sizeof(const JSCell*);
+ };
- template<typename T>
- inline void MarkStack::appendUnbarrieredPointer(T** slot)
- {
- ASSERT(slot);
- JSCell* cell = *slot;
- if (cell)
- internalAppend(cell);
- }
+ JS_EXPORT_PRIVATE void expand();
- ALWAYS_INLINE void MarkStack::append(JSValue* slot)
- {
- ASSERT(slot);
- internalAppend(*slot);
- }
-
- ALWAYS_INLINE void MarkStack::append(JSCell** slot)
- {
- ASSERT(slot);
- internalAppend(*slot);
- }
+ size_t postIncTop();
+ size_t preDecTop();
+ void setTopForFullSegment();
+ void setTopForEmptySegment();
+ size_t top();
+
+ void validatePrevious();
- ALWAYS_INLINE void MarkStack::internalAppend(JSValue value)
- {
- ASSERT(value);
- if (!value.isCell())
- return;
- internalAppend(value.asCell());
- }
+ DoublyLinkedList<MarkStackSegment> m_segments;
+ BlockAllocator& m_blockAllocator;
- class ParallelModeEnabler {
- public:
- ParallelModeEnabler(MarkStack& stack)
- : m_stack(stack)
- {
- ASSERT(!m_stack.m_isInParallelMode);
- m_stack.m_isInParallelMode = true;
- }
-
- ~ParallelModeEnabler()
- {
- ASSERT(m_stack.m_isInParallelMode);
- m_stack.m_isInParallelMode = false;
- }
-
- private:
- MarkStack& m_stack;
- };
+ JS_EXPORT_PRIVATE static const size_t s_segmentCapacity = CapacityFromSize<MarkStackSegment::blockSize>::value;
+ size_t m_top;
+ size_t m_numberOfSegments;
+
+};
} // namespace JSC