diff --git a/src/coreclr/nativeaot/Runtime/AsmOffsets.h b/src/coreclr/nativeaot/Runtime/AsmOffsets.h index 30540742974b45..e2fb384248f171 100644 --- a/src/coreclr/nativeaot/Runtime/AsmOffsets.h +++ b/src/coreclr/nativeaot/Runtime/AsmOffsets.h @@ -49,14 +49,15 @@ ASM_OFFSET( 14, 18, MethodTable, m_VTable) ASM_OFFSET( 0, 0, Thread, m_rgbAllocContextBuffer) ASM_OFFSET( 28, 38, Thread, m_ThreadStateFlags) ASM_OFFSET( 2c, 40, Thread, m_pTransitionFrame) -ASM_OFFSET( 30, 48, Thread, m_pDeferredTransitionFrame) -ASM_OFFSET( 40, 68, Thread, m_ppvHijackedReturnAddressLocation) -ASM_OFFSET( 44, 70, Thread, m_pvHijackedReturnAddress) +ASM_OFFSET( 30, 48, Thread, m_generation) +ASM_OFFSET( 34, 50, Thread, m_pDeferredTransitionFrame) +ASM_OFFSET( 44, 70, Thread, m_ppvHijackedReturnAddressLocation) +ASM_OFFSET( 48, 78, Thread, m_pvHijackedReturnAddress) #ifdef HOST_64BIT -ASM_OFFSET( 0, 78, Thread, m_uHijackedReturnValueFlags) +ASM_OFFSET( 0, 80, Thread, m_uHijackedReturnValueFlags) #endif -ASM_OFFSET( 48, 80, Thread, m_pExInfoStackHead) -ASM_OFFSET( 4c, 88, Thread, m_threadAbortException) +ASM_OFFSET( 4c, 88, Thread, m_pExInfoStackHead) +ASM_OFFSET( 50, 90, Thread, m_threadAbortException) ASM_SIZEOF( 14, 20, EHEnum) diff --git a/src/coreclr/nativeaot/Runtime/amd64/PInvoke.S b/src/coreclr/nativeaot/Runtime/amd64/PInvoke.S index f5defdbbea461b..1dfc7a878afc5d 100644 --- a/src/coreclr/nativeaot/Runtime/amd64/PInvoke.S +++ b/src/coreclr/nativeaot/Runtime/amd64/PInvoke.S @@ -46,6 +46,7 @@ LEAF_ENTRY RhpPInvokeReturn, _TEXT mov qword ptr [rsi + OFFSETOF__Thread__m_pTransitionFrame], 0 cmp dword ptr [C_VAR(RhpTrapThreads)], TrapThreadsFlags_None jne 0f // forward branch - predicted not taken + mov qword ptr [rsi + OFFSETOF__Thread__m_generation], 0 ret 0: // passing transition frame pointer in rdi diff --git a/src/coreclr/nativeaot/Runtime/amd64/PInvoke.asm b/src/coreclr/nativeaot/Runtime/amd64/PInvoke.asm index 0b08d84484995e..d05aa0088d4c51 100644 --- a/src/coreclr/nativeaot/Runtime/amd64/PInvoke.asm +++ b/src/coreclr/nativeaot/Runtime/amd64/PInvoke.asm @@ -48,6 +48,7 @@ LEAF_ENTRY RhpPInvokeReturn, _TEXT mov qword ptr [rdx + OFFSETOF__Thread__m_pTransitionFrame], 0 cmp [RhpTrapThreads], TrapThreadsFlags_None jne @F ; forward branch - predicted not taken + mov qword ptr [rdx + OFFSETOF__Thread__m_generation], 0 ret @@: ; passing transition frame pointer in rcx diff --git a/src/coreclr/nativeaot/Runtime/arm64/PInvoke.S b/src/coreclr/nativeaot/Runtime/arm64/PInvoke.S index 8ed8a497d4abf7..d4ce497ca7de09 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/PInvoke.S +++ b/src/coreclr/nativeaot/Runtime/arm64/PInvoke.S @@ -51,13 +51,13 @@ NESTED_END RhpPInvoke, _TEXT LEAF_ENTRY RhpPInvokeReturn, _TEXT - ldr x9, [x0, #OFFSETOF__PInvokeTransitionFrame__m_pThread] - mov x10, 0 - str x10, [x9, #OFFSETOF__Thread__m_pTransitionFrame] + ldr x10, [x0, #OFFSETOF__PInvokeTransitionFrame__m_pThread] + str xzr, [x10, #OFFSETOF__Thread__m_pTransitionFrame] PREPARE_EXTERNAL_VAR_INDIRECT_W RhpTrapThreads, 9 cbnz w9, 0f // TrapThreadsFlags_None = 0 + str xzr, [x10, #OFFSETOF__Thread__m_generation] ret 0: // passing transition frame pointer in x0 diff --git a/src/coreclr/nativeaot/Runtime/arm64/PInvoke.asm b/src/coreclr/nativeaot/Runtime/arm64/PInvoke.asm index 52d55ad2846c77..12b33b71d5f37f 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/PInvoke.asm +++ b/src/coreclr/nativeaot/Runtime/arm64/PInvoke.asm @@ -45,13 +45,13 @@ ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LEAF_ENTRY RhpPInvokeReturn, _TEXT - ldr x9, [x0, #OFFSETOF__PInvokeTransitionFrame__m_pThread] - mov x10, 0 - str x10, [x9, #OFFSETOF__Thread__m_pTransitionFrame] + ldr x10, [x0, #OFFSETOF__PInvokeTransitionFrame__m_pThread] + str xzr, [x10, #OFFSETOF__Thread__m_pTransitionFrame] ldr x9, =RhpTrapThreads ldr w9, [x9] cbnz w9, %ft0 ;; TrapThreadsFlags_None = 0 + str xzr, [x10, #OFFSETOF__Thread__m_generation] ret 0 ;; passing transition frame pointer in x0 diff --git a/src/coreclr/nativeaot/Runtime/gcrhenv.cpp b/src/coreclr/nativeaot/Runtime/gcrhenv.cpp index c97b5e8a829901..0b3784b45228dc 100644 --- a/src/coreclr/nativeaot/Runtime/gcrhenv.cpp +++ b/src/coreclr/nativeaot/Runtime/gcrhenv.cpp @@ -659,10 +659,48 @@ void GCToEEInterface::SyncBlockCacheWeakPtrScan(HANDLESCANPROC /*scanProc*/, uin void GCToEEInterface::SyncBlockCacheDemote(int /*max_gen*/) { + int condemned = GCHeapUtilities::GetGCHeap()->GetCondemnedGeneration(); + + FOREACH_THREAD(pThread) + { + int32_t generation = pThread->GetGeneration(); + + // the stack is too old to be interesing in this GC + if (generation > condemned) + continue; + + // the stack is as young as it can be + if (generation == 0) + continue; + + pThread->SetGeneration(0); + } + END_FOREACH_THREAD } -void GCToEEInterface::SyncBlockCachePromotionsGranted(int /*max_gen*/) +void GCToEEInterface::SyncBlockCachePromotionsGranted(int max_gen) { + int condemned = GCHeapUtilities::GetGCHeap()->GetCondemnedGeneration(); + + FOREACH_THREAD(pThread) + { + int32_t generation = pThread->GetGeneration(); + + // the stack is too old to be interesing in this GC + if (generation > condemned) + continue; + + // the stack is as old as it can be + if (generation == max_gen) + continue; + + // do not age the current thread + if (pThread->IsCurrentThread()) + continue; + + pThread->SetGeneration(generation + 1); + } + END_FOREACH_THREAD } uint32_t GCToEEInterface::GetActiveSyncBlockCount() diff --git a/src/coreclr/nativeaot/Runtime/gcrhscan.cpp b/src/coreclr/nativeaot/Runtime/gcrhscan.cpp index e6c815094d29b9..7fd4106812df89 100644 --- a/src/coreclr/nativeaot/Runtime/gcrhscan.cpp +++ b/src/coreclr/nativeaot/Runtime/gcrhscan.cpp @@ -54,6 +54,10 @@ void GCToEEInterface::GcScanRoots(EnumGcRefCallbackFunc * fn, int condemned, in else #endif { + // Skip threads that cannot point to anything of interest for this GC + if (pThread->GetGeneration() > condemned) + continue; + InlinedThreadStaticRoot* pRoot = pThread->GetInlinedThreadStaticList(); while (pRoot != NULL) { diff --git a/src/coreclr/nativeaot/Runtime/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index 8972cd46ac9cb3..d0f28137545265 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -95,6 +95,8 @@ void Thread::WaitForGC(PInvokeTransitionFrame* pTransitionFrame) // Restore the saved error PalSetLastError(lastErrorOnEntry); + + m_generation = 0; } // @@ -163,6 +165,8 @@ void Thread::DisablePreemptiveMode() { WaitForGC(m_pDeferredTransitionFrame); } + + m_generation = 0; } #endif // !DACCESS_COMPILE @@ -418,6 +422,16 @@ bool Thread::CatchAtSafePoint() return true; } +int32_t Thread::GetGeneration() +{ + return (int32_t)m_generation; +} + +void Thread::SetGeneration(int32_t generation) +{ + m_generation = (size_t)generation; +} + uint64_t Thread::GetPalThreadIdForLogging() { return *(uint64_t*)&m_threadId; @@ -1086,7 +1100,10 @@ EXTERN_C NOINLINE void FASTCALL RhpWaitForGC2(PInvokeTransitionFrame * pFrame) { Thread * pThread = pFrame->m_pThread; if (pThread->IsDoNotTriggerGcSet()) + { + pThread->SetGeneration(0); return; + } pThread->WaitForGC(pFrame); } @@ -1260,6 +1277,7 @@ FORCEINLINE bool Thread::InlineTryFastReversePInvoke(ReversePInvokeFrame * pFram return false; // need to trap the thread } + m_generation = 0; return true; } @@ -1301,6 +1319,8 @@ void Thread::ReversePInvokeAttachOrTrapThread(ReversePInvokeFrame * pFrame) { WaitForGC(pFrame->m_savedPInvokeTransitionFrame); } + + m_generation = 0; } void Thread::EnsureRuntimeInitialized() @@ -1341,8 +1361,11 @@ FORCEINLINE void Thread::InlinePInvokeReturn(PInvokeTransitionFrame * pFrame) VolatileStoreWithoutBarrier(&m_pTransitionFrame, NULL); if (ThreadStore::IsTrapThreadsRequested()) { - RhpWaitForGC2(pFrame); + m_generation = 0; + return; } + + RhpWaitForGC2(pFrame); } Object * Thread::GetThreadAbortException() diff --git a/src/coreclr/nativeaot/Runtime/thread.h b/src/coreclr/nativeaot/Runtime/thread.h index aa3f370a8643d7..7aeb921b6cbec4 100644 --- a/src/coreclr/nativeaot/Runtime/thread.h +++ b/src/coreclr/nativeaot/Runtime/thread.h @@ -88,6 +88,7 @@ struct ThreadBuffer uint8_t m_rgbAllocContextBuffer[SIZEOF_ALLOC_CONTEXT]; uint32_t volatile m_ThreadStateFlags; // see Thread::ThreadStateFlags enum PInvokeTransitionFrame* m_pTransitionFrame; + size_t m_generation; PInvokeTransitionFrame* m_pDeferredTransitionFrame; // see Thread::EnablePreemptiveMode PInvokeTransitionFrame* m_pCachedTransitionFrame; PTR_Thread m_pNext; // used by ThreadStore's SList @@ -282,6 +283,8 @@ class Thread : private ThreadBuffer void SetGCSpecial(); bool IsGCSpecial(); bool CatchAtSafePoint(); + int32_t GetGeneration(); + void SetGeneration(int32_t age); // // Managed/unmanaged interop transitions support APIs diff --git a/src/coreclr/vm/qcall.h b/src/coreclr/vm/qcall.h index c8c890f438831e..667290d08ff788 100644 --- a/src/coreclr/vm/qcall.h +++ b/src/coreclr/vm/qcall.h @@ -206,7 +206,13 @@ class QCall // void Set(OBJECTREF o) { - LIMITED_METHOD_CONTRACT; + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; // The space for the return value has to be on the stack _ASSERTE(Thread::IsAddressInCurrentStack(m_ppObject));