Jelajahi Sumber

Prevent redeferral of FunctionBody's that are being processed when GC happens. Fix a class of bugs involving redeferral of functions that are being accessed by some runtime method that allocates memory, triggering GC and possibly redeferral. When a GC that will result in redeferral scans the stack, mark any FunctionBody's we find as having active references. Do this by for now adding a method to FinalizableObject and overriding it in FunctionBody. We'll replace this with a new object info bit if and when bits become available.

Paul Leathers 9 tahun lalu
induk
melakukan
5458771696

+ 3 - 0
lib/Common/Core/FinalizableObject.h

@@ -20,4 +20,7 @@ public:
 
     // Used only by TrackableObjects (created with TrackedBit on by RecyclerNew*Tracked)
     virtual void Mark(Recycler * recycler) = 0;
+
+    // Special behavior on certain GC's
+    virtual void OnMark() {}
 };

+ 1 - 1
lib/Common/Memory/HeapBlock.h

@@ -282,7 +282,7 @@ protected:
 #endif
 
 public:
-    template <typename Fn>
+    template <bool doSpecialMark, typename Fn>
     bool UpdateAttributesOfMarkedObjects(MarkContext * markContext, void * objectAddress, size_t objectSize, unsigned char attributes, Fn fn);
     void SetNeedOOMRescan(Recycler * recycler);
 public:

+ 11 - 1
lib/Common/Memory/HeapBlock.inl

@@ -153,7 +153,7 @@ SmallHeapBlockT<TBlockAttributes>::FindImplicitRootObject(void* candidate, Recyc
     return true;
 }
 
-template <typename Fn>
+template <bool doSpecialMark, typename Fn>
 bool
 HeapBlock::UpdateAttributesOfMarkedObjects(MarkContext * markContext, void * objectAddress, size_t objectSize, unsigned char attributes, Fn fn)
 {
@@ -206,6 +206,16 @@ HeapBlock::UpdateAttributesOfMarkedObjects(MarkContext * markContext, void * obj
         }
     }
 
+    // Special mark-time behavior for finalizable objects on certain GC's
+    if (doSpecialMark)
+    {
+        if (attributes & FinalizeBit)
+        {
+            FinalizableObject * trackedObject = (FinalizableObject *)objectAddress;
+            trackedObject->OnMark();
+        }
+    }        
+
 #ifdef RECYCLER_STATS
     RECYCLER_STATS_INTERLOCKED_INC(markContext->GetRecycler(), markData.markCount);
     RECYCLER_STATS_INTERLOCKED_ADD(markContext->GetRecycler(), markData.markBytes, objectSize);

+ 2 - 2
lib/Common/Memory/HeapBlockMap.h

@@ -58,7 +58,7 @@ public:
     BVStatic<BitCount>* GetMarkBitVectorForPages(void * address);
 
     uint GetMarkCount(void* address, uint pageCount);
-    template <bool interlocked>
+    template <bool interlocked, bool doSpecialMark>
     void Mark(void * candidate, MarkContext * markContext);
     template <bool interlocked>
     void MarkInterior(void * candidate, MarkContext * markContext);
@@ -246,7 +246,7 @@ public:
     BVStatic<BitCount>* GetMarkBitVectorForPages(void * address);
 
     uint GetMarkCount(void* address, uint pageCount);
-    template <bool interlocked>
+    template <bool interlocked, bool doSpecialMark>
     void Mark(void * candidate, MarkContext * markContext);
     template <bool interlocked>
     void MarkInterior(void * candidate, MarkContext * markContext);

+ 10 - 10
lib/Common/Memory/HeapBlockMap.inl

@@ -52,7 +52,7 @@ HeapBlockMap32::MarkInternal(L2MapChunk * chunk, void * candidate)
 // If the object is newly marked, then the out param heapBlock is written to, and false is returned
 //
 
-template <bool interlocked>
+template <bool interlocked, bool doSpecialMark>
 inline
 void
 HeapBlockMap32::Mark(void * candidate, MarkContext * markContext)
@@ -129,17 +129,17 @@ HeapBlockMap32::Mark(void * candidate, MarkContext * markContext)
 #ifdef RECYCLER_WRITE_BARRIER
     case HeapBlock::HeapBlockType::SmallFinalizableBlockWithBarrierType:
 #endif
-        ((SmallFinalizableHeapBlock*)chunk->map[id2])->ProcessMarkedObject(candidate, markContext);
+        ((SmallFinalizableHeapBlock*)chunk->map[id2])->ProcessMarkedObject<doSpecialMark>(candidate, markContext);
         break;
     case HeapBlock::HeapBlockType::MediumFinalizableBlockType:
 #ifdef RECYCLER_WRITE_BARRIER
     case HeapBlock::HeapBlockType::MediumFinalizableBlockWithBarrierType:
 #endif
-        ((MediumFinalizableHeapBlock*)chunk->map[id2])->ProcessMarkedObject(candidate, markContext);
+        ((MediumFinalizableHeapBlock*)chunk->map[id2])->ProcessMarkedObject<doSpecialMark>(candidate, markContext);
         break;
 
     case HeapBlock::HeapBlockType::LargeBlockType:
-        ((LargeHeapBlock*)chunk->map[id2])->Mark(candidate, markContext);
+        ((LargeHeapBlock*)chunk->map[id2])->Mark<doSpecialMark>(candidate, markContext);
         break;
 
     case HeapBlock::HeapBlockType::BlockTypeCount:
@@ -184,7 +184,7 @@ HeapBlockMap32::MarkInteriorInternal(MarkContext * markContext, L2MapChunk *& ch
         {
             // We crossed a node boundary (very rare) so we should just re-start from the real candidate.
             // In this case we are no longer marking an interior reference.
-            markContext->GetRecycler()->heapBlockMap.Mark<interlocked>(realCandidate, markContext);
+            markContext->GetRecycler()->heapBlockMap.Mark<interlocked, false>(realCandidate, markContext);
 
             // This mark code therefore has nothing to do (it has already happened).
             return true;
@@ -286,7 +286,7 @@ HeapBlockMap32::MarkInterior(void * candidate, MarkContext * markContext)
                 break;
             }
 
-            ((SmallFinalizableHeapBlock*)chunk->map[id2])->ProcessMarkedObject(realCandidate, markContext);
+            ((SmallFinalizableHeapBlock*)chunk->map[id2])->ProcessMarkedObject<false>(realCandidate, markContext);
         }
         break;
     case HeapBlock::HeapBlockType::MediumFinalizableBlockType:
@@ -300,7 +300,7 @@ HeapBlockMap32::MarkInterior(void * candidate, MarkContext * markContext)
                 break;
             }
 
-            ((MediumFinalizableHeapBlock*)chunk->map[id2])->ProcessMarkedObject(realCandidate, markContext);
+            ((MediumFinalizableHeapBlock*)chunk->map[id2])->ProcessMarkedObject<false>(realCandidate, markContext);
         }
         break;
 
@@ -312,7 +312,7 @@ HeapBlockMap32::MarkInterior(void * candidate, MarkContext * markContext)
                 break;
             }
 
-            ((LargeHeapBlock*)chunk->map[GetLevel2Id(realCandidate)])->Mark(realCandidate, markContext);
+            ((LargeHeapBlock*)chunk->map[GetLevel2Id(realCandidate)])->Mark<false>(realCandidate, markContext);
         }
         break;
 
@@ -334,7 +334,7 @@ HeapBlockMap32::MarkInterior(void * candidate, MarkContext * markContext)
 // See HeapBlockMap32::Mark for explanation of return values
 //
 
-template <bool interlocked>
+template <bool interlocked, bool doSpecialMark>
 inline
 void
 HeapBlockMap64::Mark(void * candidate, MarkContext * markContext)
@@ -348,7 +348,7 @@ HeapBlockMap64::Mark(void * candidate, MarkContext * markContext)
         {
             // Found the correct Node.
             // Process the mark and return.
-            node->map.Mark<interlocked>(candidate, markContext);
+            node->map.Mark<interlocked, doSpecialMark>(candidate, markContext);
             return;
         }
 

+ 5 - 2
lib/Common/Memory/LargeHeapBlock.cpp

@@ -556,6 +556,7 @@ LargeHeapBlock::Alloc(size_t size, ObjectInfoBits attributes)
     return allocObject;
 }
 
+template <bool doSpecialMark>
 _NOINLINE
 void
 LargeHeapBlock::Mark(void* objectAddress, MarkContext * markContext)
@@ -602,7 +603,7 @@ LargeHeapBlock::Mark(void* objectAddress, MarkContext * markContext)
         }
     }
 
-    if (!UpdateAttributesOfMarkedObjects(markContext, objectAddress, objectSize, attributes,
+    if (!UpdateAttributesOfMarkedObjects<doSpecialMark>(markContext, objectAddress, objectSize, attributes,
         [&](unsigned char attributes) { header->SetAttributes(this->heapInfo->recycler->Cookie, attributes); }))
     {
         // Couldn't mark children- bail out and come back later
@@ -618,6 +619,9 @@ LargeHeapBlock::Mark(void* objectAddress, MarkContext * markContext)
     }
 }
 
+template void LargeHeapBlock::Mark<true>(void* objectAddress, MarkContext * markContext);
+template void LargeHeapBlock::Mark<false>(void* objectAddress, MarkContext * markContext);
+
 bool
 LargeHeapBlock::TestObjectMarkedBit(void* objectAddress)
 {
@@ -911,7 +915,6 @@ LargeHeapBlock::ScanInitialImplicitRoots(Recycler * recycler)
     }
 }
 
-
 void
 LargeHeapBlock::ScanNewImplicitRoots(Recycler * recycler)
 {

+ 1 - 0
lib/Common/Memory/LargeHeapBlock.h

@@ -98,6 +98,7 @@ public:
 #endif
     virtual BOOL IsValidObject(void* objectAddress) override;
 
+    template <bool doSpecialMark>
     void Mark(void* objectAddress, MarkContext * markContext);
     virtual byte* GetRealAddressFromInterior(void* interiorAddress) override;
     bool TestObjectMarkedBit(void* objectAddress) override;

+ 2 - 2
lib/Common/Memory/MarkContext.h

@@ -33,13 +33,13 @@ public:
     bool AddTrackedObject(FinalizableObject * obj);
 #endif
 
-    template <bool parallel, bool interior>
+    template <bool parallel, bool interior, bool doSpecialMark>
     void Mark(void * candidate, void * parentReference);
     template <bool parallel>
     void MarkInterior(void * candidate);
     template <bool parallel, bool interior>
     void ScanObject(void ** obj, size_t byteCount);
-    template <bool parallel, bool interior>
+    template <bool parallel, bool interior, bool doSpecialMark>
     void ScanMemory(void ** obj, size_t byteCount);
     template <bool parallel, bool interior>
     void ProcessMark();

+ 5 - 5
lib/Common/Memory/MarkContext.inl

@@ -45,7 +45,7 @@ bool MarkContext::AddTrackedObject(FinalizableObject * obj)
 }
 #endif
 
-template <bool parallel, bool interior>
+template <bool parallel, bool interior, bool doSpecialMark>
 inline
 void MarkContext::ScanMemory(void ** obj, size_t byteCount)
 {
@@ -74,7 +74,7 @@ void MarkContext::ScanMemory(void ** obj, size_t byteCount)
 #else
         void * candidate = *(static_cast<void * volatile *>(obj));
 #endif
-        Mark<parallel, interior>(candidate, parentObject);
+        Mark<parallel, interior, doSpecialMark>(candidate, parentObject);
         obj++;
     } while (obj != objEnd);
 
@@ -94,13 +94,13 @@ void MarkContext::ScanObject(void ** obj, size_t byteCount)
 {
     BEGIN_DUMP_OBJECT(recycler, obj);
 
-    ScanMemory<parallel, interior>(obj, byteCount);
+    ScanMemory<parallel, interior, false>(obj, byteCount);
 
     END_DUMP_OBJECT(recycler);
 }
 
 
-template <bool parallel, bool interior>
+template <bool parallel, bool interior, bool doSpecialMark>
 inline
 void MarkContext::Mark(void * candidate, void * parentReference)
 {
@@ -132,7 +132,7 @@ void MarkContext::Mark(void * candidate, void * parentReference)
         return;
     }
 
-    recycler->heapBlockMap.Mark<parallel>(candidate, this);
+    recycler->heapBlockMap.Mark<parallel, doSpecialMark>(candidate, this);
 
 #ifdef RECYCLER_MARK_TRACK
     this->OnObjectMarked(candidate, parentReference);

+ 34 - 11
lib/Common/Memory/Recycler.cpp

@@ -1536,12 +1536,28 @@ Recycler::ScanStack()
     }
 #endif
 
+    bool doSpecialMark = collectionWrapper->DoSpecialMarkOnScanStack();
+
     BEGIN_DUMP_OBJECT(this, _u("Registers"));
-    ScanMemoryInline(this->savedThreadContext.GetRegisters(), sizeof(void*) * SavedRegisterState::NumRegistersToSave);
+    if (doSpecialMark)
+    {
+        ScanMemoryInline<true>(this->savedThreadContext.GetRegisters(), sizeof(void*) * SavedRegisterState::NumRegistersToSave);
+    }
+    else
+    {
+        ScanMemoryInline<false>(this->savedThreadContext.GetRegisters(), sizeof(void*) * SavedRegisterState::NumRegistersToSave);
+    }
     END_DUMP_OBJECT(this);
 
     BEGIN_DUMP_OBJECT(this, _u("Stack"));
-    ScanMemoryInline((void**) stackTop, stackScanned);
+    if (doSpecialMark)
+    {
+        ScanMemoryInline<true>((void**) stackTop, stackScanned);
+    }
+    else
+    {
+        ScanMemoryInline<false>((void**) stackTop, stackScanned);
+    }
     END_DUMP_OBJECT(this);
 
 #if DBG_DUMP
@@ -1611,7 +1627,7 @@ size_t Recycler::ScanPinnedObjects()
 void
 RecyclerScanMemoryCallback::operator()(void** obj, size_t byteCount)
 {
-    this->recycler->ScanMemoryInline(obj, byteCount);
+    this->recycler->ScanMemoryInline<false>(obj, byteCount);
 }
 
 size_t
@@ -1766,7 +1782,7 @@ Recycler::TryMarkArenaMemoryBlockList(ArenaMemoryBlock * memoryBlocks)
         void** base=(void**)blockp->GetBytes();
         size_t byteCount = blockp->nbytes;
         scanRootBytes += byteCount;
-        this->ScanMemory(base, byteCount);
+        this->ScanMemory<false>(base, byteCount);
         blockp = blockp->next;
     }
     return scanRootBytes;
@@ -1796,7 +1812,7 @@ Recycler::TryMarkBigBlockListWithWriteWatch(BigBlock * memoryBlocks)
                 char * currentEnd = min(currentPageStart + pageSize, endAddress);
                 size_t byteCount = (size_t)(currentEnd - currentAddress);
                 scanRootBytes += byteCount;
-                this->ScanMemory((void **)currentAddress, byteCount);
+                this->ScanMemory<false>((void **)currentAddress, byteCount);
             }
 
             currentPageStart += pageSize;
@@ -1818,7 +1834,7 @@ Recycler::TryMarkBigBlockList(BigBlock * memoryBlocks)
         void** base = (void**)blockp->GetBytes();
         size_t byteCount = blockp->currentByte;
         scanRootBytes +=  byteCount;
-        this->ScanMemory(base, byteCount);
+        this->ScanMemory<false>(base, byteCount);
         blockp = blockp->nextBigBlock;
     }
     return scanRootBytes;
@@ -1907,10 +1923,9 @@ Recycler::TryMarkNonInterior(void* candidate, void* parentReference)
     Assert(!isHeapEnumInProgress);
 #endif
     Assert(this->collectionState != CollectionStateParallelMark);
-    markContext.Mark</*parallel */ false, /* interior */ false>(candidate, parentReference);
+    markContext.Mark</*parallel */ false, /* interior */ false, /* doSpecialMark */ false>(candidate, parentReference);
 }
 
-
 void
 Recycler::TryMarkInterior(void* candidate, void* parentReference)
 {
@@ -1920,7 +1935,7 @@ Recycler::TryMarkInterior(void* candidate, void* parentReference)
     Assert(!isHeapEnumInProgress);
 #endif
     Assert(this->collectionState != CollectionStateParallelMark);
-    markContext.Mark</*parallel */ false, /* interior */ true>(candidate, parentReference);
+    markContext.Mark</*parallel */ false, /* interior */ true, /* doSpecialMark */ false>(candidate, parentReference);
 }
 
 template <bool parallel, bool interior>
@@ -4959,7 +4974,15 @@ Recycler::BackgroundScanStack()
     if (stackTop != nullptr)
     {
         size_t size = (char *)stackBase - stackTop;
-        ScanMemoryInline((void **)stackTop, size);
+        bool doSpecialMark = collectionWrapper->DoSpecialMarkOnScanStack();
+        if (doSpecialMark)
+        {
+            ScanMemoryInline<true>((void **)stackTop, size);
+        }
+        else
+        {
+            ScanMemoryInline<false>((void **)stackTop, size);
+        }
         return size;
     }
 
@@ -8023,7 +8046,7 @@ void Recycler::SetObjectBeforeCollectCallback(void* object,
 
     if (callback != nullptr && this->IsInObjectBeforeCollectCallback()) // revive
     {
-        this->ScanMemory(&object, sizeof(object));
+        this->ScanMemory<false>(&object, sizeof(object));
         this->ProcessMark(/*background*/false);
     }
 }

+ 5 - 1
lib/Common/Memory/Recycler.h

@@ -329,6 +329,7 @@ public:
     virtual void PostCollectionCallBack() = 0;
     virtual BOOL ExecuteRecyclerCollectionFunction(Recycler * recycler, CollectionFunction function, CollectionFlags flags) = 0;
     virtual uint GetRandomNumber() = 0;
+    virtual bool DoSpecialMarkOnScanStack() = 0;
 
 #ifdef FAULT_INJECTION
     virtual void DisposeScriptContextByFaultInjectionCallBack() = 0;
@@ -376,6 +377,7 @@ public:
     virtual void PostCollectionCallBack() override {}
     virtual BOOL ExecuteRecyclerCollectionFunction(Recycler * recycler, CollectionFunction function, CollectionFlags flags) override;
     virtual uint GetRandomNumber() override { return 0; }
+    virtual bool DoSpecialMarkOnScanStack() override { return false; }
 #ifdef FAULT_INJECTION
     virtual void DisposeScriptContextByFaultInjectionCallBack() override {};
 #endif
@@ -1637,8 +1639,10 @@ private:
 
     inline void ScanObjectInline(void ** obj, size_t byteCount);
     inline void ScanObjectInlineInterior(void ** obj, size_t byteCount);
+    template <bool doSpecialMark>
     inline void ScanMemoryInline(void ** obj, size_t byteCount);
-    void ScanMemory(void ** obj, size_t byteCount) { if (byteCount != 0) { ScanMemoryInline(obj, byteCount); } }
+    template <bool doSpecialMark>
+    void ScanMemory(void ** obj, size_t byteCount) { if (byteCount != 0) { ScanMemoryInline<doSpecialMark>(obj, byteCount); } }
     bool AddMark(void * candidate, size_t byteCount);
 
     // Sweep

+ 3 - 2
lib/Common/Memory/Recycler.inl

@@ -494,6 +494,7 @@ Recycler::ScanObjectInlineInterior(void ** obj, size_t byteCount)
     markContext.ScanObject<false, true>(obj, byteCount);
 }
 
+template <bool doSpecialMark>
 inline void
 Recycler::ScanMemoryInline(void ** obj, size_t byteCount)
 {
@@ -501,11 +502,11 @@ Recycler::ScanMemoryInline(void ** obj, size_t byteCount)
     Assert(this->collectionState != CollectionStateParallelMark);
     if (this->enableScanInteriorPointers)
     {
-        markContext.ScanMemory<false, true>(obj, byteCount);
+        markContext.ScanMemory<false, true, doSpecialMark>(obj, byteCount);
     }
     else
     {
-        markContext.ScanMemory<false, false>(obj, byteCount);
+        markContext.ScanMemory<false, false, doSpecialMark>(obj, byteCount);
     }
 }
 

+ 6 - 1
lib/Common/Memory/SmallFinalizableHeapBlock.cpp

@@ -104,6 +104,7 @@ SmallFinalizableHeapBlockT<TBlockAttributes>::SetAttributes(void * address, unsi
 }
 
 template <class TBlockAttributes>
+template <bool doSpecialMark>
 _NOINLINE
 void
 SmallFinalizableHeapBlockT<TBlockAttributes>::ProcessMarkedObject(void* objectAddress, MarkContext * markContext)
@@ -118,7 +119,7 @@ SmallFinalizableHeapBlockT<TBlockAttributes>::ProcessMarkedObject(void* objectAd
 
     unsigned char * attributes = &this->ObjectInfo(objectIndex);
 
-    if (!this->UpdateAttributesOfMarkedObjects(markContext, objectAddress, this->objectSize, *attributes,
+    if (!this->template UpdateAttributesOfMarkedObjects<doSpecialMark>(markContext, objectAddress, this->objectSize, *attributes,
         [&](unsigned char _attributes) { *attributes = _attributes; }))
     {
         // Couldn't mark children- bail out and come back later
@@ -445,7 +446,11 @@ SmallFinalizableHeapBlockT<TBlockAttributes>::GetFreeObjectListOnAllocator(FreeO
 namespace Memory
 {
     template class SmallFinalizableHeapBlockT<SmallAllocationBlockAttributes>;
+    template void SmallFinalizableHeapBlockT<SmallAllocationBlockAttributes>::ProcessMarkedObject<true>(void* objectAddress, MarkContext * markContext);
+    template void SmallFinalizableHeapBlockT<SmallAllocationBlockAttributes>::ProcessMarkedObject<false>(void* objectAddress, MarkContext * markContext);
     template class SmallFinalizableHeapBlockT<MediumAllocationBlockAttributes>;
+    template void SmallFinalizableHeapBlockT<MediumAllocationBlockAttributes>::ProcessMarkedObject<true>(void* objectAddress, MarkContext * markContext);;
+    template void SmallFinalizableHeapBlockT<MediumAllocationBlockAttributes>::ProcessMarkedObject<false>(void* objectAddress, MarkContext * markContext);;
 
 #ifdef RECYCLER_WRITE_BARRIER
     template class SmallFinalizableWithBarrierHeapBlockT<SmallAllocationBlockAttributes>;

+ 1 - 0
lib/Common/Memory/SmallFinalizableHeapBlock.h

@@ -30,6 +30,7 @@ public:
     }
     void SetNextBlock(SmallFinalizableHeapBlockT * next) { Base::SetNextBlock(next); }
 
+    template <bool doSpecialMark>
     void ProcessMarkedObject(void* candidate, MarkContext * markContext);
 
     void SetAttributes(void * address, unsigned char attributes);

+ 7 - 0
lib/Runtime/Base/FunctionBody.cpp

@@ -496,6 +496,7 @@ namespace Js
         m_hasFirstInnerScopeRegister(false),
         m_hasFuncExprScopeRegister(false),
         m_hasFirstTmpRegister(false),
+        m_hasActiveReference(false),
         m_tag(TRUE),
         m_nativeEntryPointUsed(FALSE),
         bailOnMisingProfileCount(0),
@@ -634,6 +635,7 @@ namespace Js
         m_hasFirstInnerScopeRegister(false),
         m_hasFuncExprScopeRegister(false),
         m_hasFirstTmpRegister(false),
+        m_hasActiveReference(false),
         m_tag(TRUE),
         m_nativeEntryPointUsed(FALSE),
         bailOnMisingProfileCount(0),
@@ -7470,6 +7472,11 @@ namespace Js
         this->CleanupFunctionProxyCounters();
     }
 
+    void FunctionBody::OnMark()
+    {
+        this->m_hasActiveReference = true;
+    }
+
     void FunctionBody::CleanupSourceInfo(bool isScriptContextClosing)
     {
         Assert(this->cleanedUp);

+ 4 - 1
lib/Runtime/Base/FunctionBody.h

@@ -2457,6 +2457,7 @@ namespace Js
         bool m_hasFirstInnerScopeRegister : 1;
         bool m_hasFuncExprScopeRegister : 1;
         bool m_hasFirstTmpRegister : 1;
+        bool m_hasActiveReference : 1;
 #if DBG
         bool m_isSerialized : 1;
 #endif
@@ -2587,6 +2588,7 @@ namespace Js
         void IncrInactiveCount(uint increment);
         bool InterpretedSinceCallCountCollection() const;
         void CollectInterpretedCounts();
+        void ResetRedeferralAttributes() { this->m_hasActiveReference = false; }
 
         Js::RootObjectBase * LoadRootObject() const;
         Js::RootObjectBase * GetRootObject() const;
@@ -2826,6 +2828,7 @@ namespace Js
         int GetProfileSession() { return m_iProfileSession; }
 #endif
         virtual void Finalize(bool isShutdown) override;
+        virtual void OnMark() override;
 
         void Cleanup(bool isScriptContextClosing);
         void CleanupSourceInfo(bool isScriptContextClosing);
@@ -2955,7 +2958,7 @@ namespace Js
         // Field accessors
         bool GetHasBailoutInstrInJittedCode() const { return this->m_hasBailoutInstrInJittedCode; }
         void SetHasBailoutInstrInJittedCode(bool hasBailout) { this->m_hasBailoutInstrInJittedCode = hasBailout; }
-        bool GetCanDefer() const { return this->functionInfo->CanBeDeferred() && this->m_depth == 0; }
+        bool GetCanDefer() const { return this->functionInfo->CanBeDeferred() && this->m_depth == 0 && !this->m_hasActiveReference; }
         bool GetCanReleaseLoopHeaders() const { return (this->m_depth == 0); }
         void SetPendingLoopHeaderRelease(bool pendingLoopHeaderRelease) { this->m_pendingLoopHeaderRelease = pendingLoopHeaderRelease; }
 

+ 4 - 0
lib/Runtime/Base/ScriptContext.cpp

@@ -1107,6 +1107,10 @@ namespace Js
                 Assert(functionBody->DoRedeferFunction(inactiveThreshold));
                 functionBody->RedeferFunction();
             }
+            else
+            {
+                functionBody->ResetRedeferralAttributes();
+            }
         };
 
         this->MapFunction(fnRedefer);

+ 0 - 10
lib/Runtime/Base/ThreadContext.cpp

@@ -1710,16 +1710,6 @@ ThreadContext::ProbeStackNoDispose(size_t size, Js::ScriptContext *scriptContext
             this->CheckInterruptPoll();
         }
     }
-
-#if DBG
-    if (PHASE_STRESS1(Js::RedeferralPhase))
-    {
-        if (JsUtil::ExternalApi::IsScriptActiveOnCurrentThreadContext())
-        {
-            this->TryRedeferral();
-        }
-    }
-#endif
 }
 
 void

+ 1 - 0
lib/Runtime/Base/ThreadContext.h

@@ -1546,6 +1546,7 @@ public:
     void ClearDisableImplicitFlags() { disableImplicitFlags = DisableImplicitNoFlag; }
 
     virtual uint GetRandomNumber() override;
+    virtual bool DoSpecialMarkOnScanStack() override { return this->DoRedeferFunctionBodies(); }
 
     // DefaultCollectWrapper
     virtual void PreCollectionCallBack(CollectionFlags flags) override;