Index: Source/platform/heap/Heap.cpp |
diff --git a/Source/platform/heap/Heap.cpp b/Source/platform/heap/Heap.cpp |
index 5f49a2714d92cc8900697c241c388e0da0cb8c47..5e3733112f92ee6b4e901bdfc1ad9cd6ecf5a916 100644 |
--- a/Source/platform/heap/Heap.cpp |
+++ b/Source/platform/heap/Heap.cpp |
@@ -582,12 +582,12 @@ static bool isUninitializedMemory(void* objectPointer, size_t objectSize) |
template<> |
void LargeObject<FinalizedHeapObjectHeader>::mark(Visitor* visitor) |
{ |
- if (heapObjectHeader()->hasVTable() && !vTableInitialized(payload())) { |
- FinalizedHeapObjectHeader* header = heapObjectHeader(); |
+ FinalizedHeapObjectHeader* header = heapObjectHeader(); |
+ if (header->hasVTable() && !vTableInitialized(payload())) { |
visitor->markNoTracing(header); |
ASSERT(isUninitializedMemory(header->payload(), header->payloadSize())); |
} else { |
- visitor->mark(heapObjectHeader(), heapObjectHeader()->traceCallback()); |
+ visitor->mark(header, header->traceCallback()); |
} |
} |
@@ -1963,6 +1963,12 @@ static void markNoTracingCallback(Visitor* visitor, void* object) |
visitor->markNoTracing(object); |
} |
+enum MarkingMode { |
+ GlobalMarking, |
+ ThreadLocalMarking, |
+}; |
+ |
+template<MarkingMode Mode> |
class MarkingVisitor final : public Visitor { |
public: |
#if ENABLE(GC_PROFILE_MARKING) |
@@ -1971,27 +1977,26 @@ public: |
typedef HashMap<uintptr_t, std::pair<uintptr_t, String> > ObjectGraph; |
#endif |
- MarkingVisitor(CallbackStack* markingStack) : m_markingStack(markingStack) |
+ explicit MarkingVisitor(CallbackStack* markingStack) |
+ : m_markingStack(markingStack) |
{ |
} |
inline void visitHeader(HeapObjectHeader* header, const void* objectPointer, TraceCallback callback) |
{ |
ASSERT(header); |
-#if ENABLE(ASSERT) |
- { |
- // Check that we are not marking objects that are outside |
- // the heap by calling Heap::contains. However we cannot |
- // call Heap::contains when outside a GC and we call mark |
- // when doing weakness for ephemerons. Hence we only check |
- // when called within. |
- ASSERT(!Heap::isInGC() || Heap::containedInHeapOrOrphanedPage(header)); |
- } |
-#endif |
ASSERT(objectPointer); |
+ // Check that we are not marking objects that are outside |
+ // the heap by calling Heap::contains. However we cannot |
+ // call Heap::contains when outside a GC and we call mark |
+ // when doing weakness for ephemerons. Hence we only check |
+ // when called within. |
+ ASSERT(!Heap::isInGC() || Heap::containedInHeapOrOrphanedPage(header)); |
+ |
if (header->isMarked()) |
return; |
header->mark(); |
+ |
#if ENABLE(GC_PROFILE_MARKING) |
MutexLocker locker(objectGraphMutex()); |
String className(classOf(objectPointer)); |
@@ -2003,21 +2008,30 @@ public: |
ASSERT(result.isNewEntry); |
// fprintf(stderr, "%s[%p] -> %s[%p]\n", m_hostName.ascii().data(), m_hostObject, className.ascii().data(), objectPointer); |
#endif |
+ // If you hit this ASSERT, it means that there is a dangling pointer |
+ // from a live thread heap to a dead thread heap. We must eliminate |
+ // the dangling pointer. |
+ // Release builds don't have the ASSERT, but it is OK because |
+ // release builds will crash upon invoking the trace callback |
+ // as all the entries of the orphaned heaps are zeroed out |
+ // (=> 'objectPointer' will not have a valid vtable.) |
+ ASSERT(!pageFromObject(objectPointer)->orphaned()); |
+ |
+ if (Mode == ThreadLocalMarking && !objectInTerminatingThreadHeap(objectPointer)) |
+ return; |
+ |
if (callback) |
Heap::pushTraceCallback(m_markingStack, const_cast<void*>(objectPointer), callback); |
} |
+ // We need both HeapObjectHeader and FinalizedHeapObjectHeader versions to correctly find the payload. |
virtual void mark(HeapObjectHeader* header, TraceCallback callback) override |
{ |
- // We need both the HeapObjectHeader and FinalizedHeapObjectHeader |
- // version to correctly find the payload. |
visitHeader(header, header->payload(), callback); |
} |
virtual void mark(FinalizedHeapObjectHeader* header, TraceCallback callback) override |
{ |
- // We need both the HeapObjectHeader and FinalizedHeapObjectHeader |
- // version to correctly find the payload. |
visitHeader(header, header->payload(), callback); |
} |
@@ -2056,6 +2070,60 @@ public: |
return FinalizedHeapObjectHeader::fromPayload(objectPointer)->isMarked(); |
} |
+ virtual bool ensureMarked(const void* objectPointer) override |
+ { |
+ if (!objectPointer) |
+ return false; |
+#if ENABLE(ASSERT) |
+ if (isMarked(objectPointer)) |
+ return false; |
+ |
+ markNoTracing(objectPointer); |
+#else |
+ // Inline what the above markNoTracing() call expands to, |
+ // so as to make sure that we do get all the benefits. |
+ FinalizedHeapObjectHeader* header = |
+ FinalizedHeapObjectHeader::fromPayload(objectPointer); |
+ if (header->isMarked()) |
+ return false; |
+ header->mark(); |
+#endif |
+ if (Mode == ThreadLocalMarking && !objectInTerminatingThreadHeap(objectPointer)) |
+ return false; |
+ return true; |
+ } |
+ |
+#if ENABLE(ASSERT) |
+#define DEFINE_ENSURE_MARKED_METHOD(Type) \ |
+ virtual bool ensureMarked(const Type* objectPointer) override \ |
+ { \ |
+ if (!objectPointer) \ |
+ return false; \ |
+ COMPILE_ASSERT(!NeedsAdjustAndMark<Type>::value, CanOnlyUseIsMarkedOnNonAdjustedTypes); \ |
+ if (isMarked(objectPointer)) \ |
+ return false; \ |
+ markNoTracing(objectPointer); \ |
+ if (Mode == ThreadLocalMarking && !objectInTerminatingThreadHeap(objectPointer)) \ |
+ return false; \ |
+ return true; \ |
+ } |
+#else |
+#define DEFINE_ENSURE_MARKED_METHOD(Type) \ |
+ virtual bool ensureMarked(const Type* objectPointer) override \ |
+ { \ |
+ if (!objectPointer) \ |
+ return false; \ |
+ HeapObjectHeader* header = \ |
+ HeapObjectHeader::fromPayload(objectPointer); \ |
+ if (header->isMarked()) \ |
+ return false; \ |
+ header->mark(); \ |
+ if (Mode == ThreadLocalMarking && !objectInTerminatingThreadHeap(objectPointer)) \ |
+ return false; \ |
+ return true; \ |
+ } |
+#endif |
+ |
// This macro defines the necessary visitor methods for typed heaps |
#define DEFINE_VISITOR_METHODS(Type) \ |
virtual void mark(const Type* objectPointer, TraceCallback callback) override \ |
@@ -2069,7 +2137,8 @@ public: |
virtual bool isMarked(const Type* objectPointer) override \ |
{ \ |
return HeapObjectHeader::fromPayload(objectPointer)->isMarked(); \ |
- } |
+ } \ |
+ DEFINE_ENSURE_MARKED_METHOD(Type) |
FOR_EACH_TYPED_HEAP(DEFINE_VISITOR_METHODS) |
#undef DEFINE_VISITOR_METHODS |
@@ -2170,6 +2239,16 @@ public: |
} |
#endif |
+ static inline bool objectInTerminatingThreadHeap(const void* objectPointer) |
+ { |
+ BaseHeapPage* page = pageFromObject(objectPointer); |
+ ASSERT(!page->orphaned()); |
+ // When doing a thread local GC, the marker checks if |
+ // the object resides in another thread's heap. The |
+ // object should not be traced, if it does. |
+ return page->terminating(); |
+ } |
+ |
protected: |
virtual void registerWeakCell(void** cell, WeakPointerCallback callback) override |
{ |
@@ -2188,7 +2267,7 @@ void Heap::init() |
s_weakCallbackStack = new CallbackStack(); |
s_ephemeronStack = new CallbackStack(); |
s_heapDoesNotContainCache = new HeapDoesNotContainCache(); |
- s_markingVisitor = new MarkingVisitor(s_markingStack); |
+ s_markingVisitor = new MarkingVisitor<GlobalMarking>(s_markingStack); |
s_freePagePool = new FreePagePool(); |
s_orphanedPagePool = new OrphanedPagePool(); |
s_allocatedObjectSize = 0; |
@@ -2330,42 +2409,16 @@ String Heap::createBacktraceString() |
void Heap::pushTraceCallback(CallbackStack* stack, void* object, TraceCallback callback) |
{ |
-#if ENABLE(ASSERT) |
- { |
- ASSERT(Heap::containedInHeapOrOrphanedPage(object)); |
- } |
-#endif |
+ ASSERT(Heap::containedInHeapOrOrphanedPage(object)); |
CallbackStack::Item* slot = stack->allocateEntry(); |
*slot = CallbackStack::Item(object, callback); |
} |
-template<CallbackInvocationMode Mode> |
bool Heap::popAndInvokeTraceCallback(CallbackStack* stack, Visitor* visitor) |
{ |
CallbackStack::Item* item = stack->pop(); |
if (!item) |
return false; |
-#if ENABLE(ASSERT) |
- if (Mode == GlobalMarking) { |
- BaseHeapPage* page = pageFromObject(item->object()); |
- // If you hit this ASSERT, it means that there is a dangling pointer |
- // from a live thread heap to a dead thread heap. We must eliminate |
- // the dangling pointer. |
- // Release builds don't have the ASSERT, but it is OK because |
- // release builds will crash at the following item->call |
- // because all the entries of the orphaned heaps are zeroed out and |
- // thus the item does not have a valid vtable. |
- ASSERT(!page->orphaned()); |
- } |
-#endif |
- if (Mode == ThreadLocalMarking) { |
- BaseHeapPage* page = pageFromObject(item->object()); |
- ASSERT(!page->orphaned()); |
- // When doing a thread local GC, don't trace an object located in |
- // a heap of another thread. |
- if (!page->terminating()) |
- return true; |
- } |
#if ENABLE(GC_PROFILE_MARKING) |
visitor->setHostInfo(item->object(), classOf(item->object())); |
@@ -2501,7 +2554,7 @@ void Heap::collectGarbage(ThreadState::StackState stackState, ThreadState::GCTyp |
ThreadState::visitPersistentRoots(s_markingVisitor); |
// 2. trace objects reachable from the persistent roots including ephemerons. |
- processMarkingStack<GlobalMarking>(); |
+ processMarkingStack(s_markingVisitor); |
// 3. trace objects reachable from the stack. We do this independent of the |
// given stackState since other threads might have a different stack state. |
@@ -2510,12 +2563,11 @@ void Heap::collectGarbage(ThreadState::StackState stackState, ThreadState::GCTyp |
// 4. trace objects reachable from the stack "roots" including ephemerons. |
// Only do the processing if we found a pointer to an object on one of the |
// thread stacks. |
- if (lastGCWasConservative()) { |
- processMarkingStack<GlobalMarking>(); |
- } |
+ if (lastGCWasConservative()) |
+ processMarkingStack(s_markingVisitor); |
- postMarkingProcessing(); |
- globalWeakProcessing(); |
+ postMarkingProcessing(s_markingVisitor); |
+ globalWeakProcessing(s_markingVisitor); |
// Now we can delete all orphaned pages because there are no dangling |
// pointers to the orphaned pages. (If we have such dangling pointers, |
@@ -2546,6 +2598,7 @@ void Heap::collectGarbageForTerminatingThread(ThreadState* state) |
// same time as a thread local GC. |
{ |
+ MarkingVisitor<ThreadLocalMarking> markingVisitor(s_markingStack); |
ThreadState::NoAllocationScope noAllocationScope(state); |
enterGC(); |
@@ -2561,14 +2614,14 @@ void Heap::collectGarbageForTerminatingThread(ThreadState* state) |
// global GC finds a "pointer" on the stack or due to a programming |
// error where an object has a dangling cross-thread pointer to an |
// object on this heap. |
- state->visitPersistents(s_markingVisitor); |
+ state->visitPersistents(&markingVisitor); |
// 2. trace objects reachable from the thread's persistent roots |
// including ephemerons. |
- processMarkingStack<ThreadLocalMarking>(); |
+ processMarkingStack(&markingVisitor); |
- postMarkingProcessing(); |
- globalWeakProcessing(); |
+ postMarkingProcessing(&markingVisitor); |
+ globalWeakProcessing(&markingVisitor); |
state->postGC(); |
leaveGC(); |
@@ -2576,32 +2629,29 @@ void Heap::collectGarbageForTerminatingThread(ThreadState* state) |
state->performPendingSweep(); |
} |
-template<CallbackInvocationMode Mode> |
-void Heap::processMarkingStack() |
+void Heap::processMarkingStack(Visitor* markingVisitor) |
{ |
// Ephemeron fixed point loop. |
do { |
{ |
// Iteratively mark all objects that are reachable from the objects |
- // currently pushed onto the marking stack. If Mode is ThreadLocalMarking |
- // don't continue tracing if the trace hits an object on another thread's |
- // heap. |
+ // currently pushed onto the marking stack. |
TRACE_EVENT0("blink_gc", "Heap::processMarkingStackSingleThreaded"); |
- while (popAndInvokeTraceCallback<Mode>(s_markingStack, s_markingVisitor)) { } |
+ while (popAndInvokeTraceCallback(s_markingStack, markingVisitor)) { } |
} |
{ |
// Mark any strong pointers that have now become reachable in ephemeron |
// maps. |
TRACE_EVENT0("blink_gc", "Heap::processEphemeronStack"); |
- s_ephemeronStack->invokeEphemeronCallbacks(s_markingVisitor); |
+ s_ephemeronStack->invokeEphemeronCallbacks(markingVisitor); |
} |
// Rerun loop if ephemeron processing queued more objects for tracing. |
} while (!s_markingStack->isEmpty()); |
} |
-void Heap::postMarkingProcessing() |
+void Heap::postMarkingProcessing(Visitor* markingVisitor) |
{ |
TRACE_EVENT0("blink_gc", "Heap::postMarkingProcessing"); |
// Call post-marking callbacks including: |
@@ -2609,7 +2659,7 @@ void Heap::postMarkingProcessing() |
// (specifically to clear the queued bits for weak hash tables), and |
// 2. the markNoTracing callbacks on collection backings to mark them |
// if they are only reachable from their front objects. |
- while (popAndInvokePostMarkingCallback(s_markingVisitor)) { } |
+ while (popAndInvokePostMarkingCallback(markingVisitor)) { } |
s_ephemeronStack->clear(); |
@@ -2619,12 +2669,12 @@ void Heap::postMarkingProcessing() |
ASSERT(s_markingStack->isEmpty()); |
} |
-void Heap::globalWeakProcessing() |
+void Heap::globalWeakProcessing(Visitor* markingVisitor) |
{ |
TRACE_EVENT0("blink_gc", "Heap::globalWeakProcessing"); |
// Call weak callbacks on objects that may now be pointing to dead |
// objects. |
- while (popAndInvokeWeakPointerCallback(s_markingVisitor)) { } |
+ while (popAndInvokeWeakPointerCallback(markingVisitor)) { } |
// It is not permitted to trace pointers of live objects in the weak |
// callback phase, so the marking stack should still be empty here. |