Skip to content

Commit 713e0ff

Browse files
Revert "Ensure that WaitForPendingFinalizers has seen the expected Full GC count (dotnet#105289)"
This reverts commit 54a9efd.
1 parent 8e3896b commit 713e0ff

File tree

7 files changed

+22
-132
lines changed

7 files changed

+22
-132
lines changed

src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ internal static extern unsafe IntPtr RhpCallPropagateExceptionCallback(
276276

277277
// Indicate that the current round of finalizations is complete.
278278
[DllImport(Redhawk.BaseName)]
279-
internal static extern void RhpSignalFinalizationComplete(uint fCount, int observedFullGcCount);
279+
internal static extern void RhpSignalFinalizationComplete(uint fCount);
280280

281281
[DllImport(Redhawk.BaseName)]
282282
internal static extern ulong RhpGetTickCount64();

src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/__Finalizer.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,11 @@ public static void ProcessFinalizers()
2929
// otherwise memory is low and we should initiate a collection.
3030
if (InternalCalls.RhpWaitForFinalizerRequest() != 0)
3131
{
32-
int observedFullGcCount = RuntimeImports.RhGetGcCollectionCount(RuntimeImports.RhGetMaxGcGeneration(), false);
3332
uint finalizerCount = DrainQueue();
3433

35-
// Anyone waiting to drain the Q can now wake up. Note that there is a
36-
// race in that another thread starting a drain, as we leave a drain, may
37-
// consider itself satisfied by the drain that just completed.
38-
// Thus we include the Full GC count that we have certaily observed.
39-
InternalCalls.RhpSignalFinalizationComplete(finalizerCount, observedFullGcCount);
34+
// Tell anybody that's interested that the finalization pass is complete (there is a race condition here
35+
// where we might immediately signal a new request as complete, but this is acceptable).
36+
InternalCalls.RhpSignalFinalizationComplete(finalizerCount);
4037
}
4138
else
4239
{

src/coreclr/nativeaot/Runtime/FinalizerHelpers.cpp

Lines changed: 12 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -94,22 +94,6 @@ EXTERN_C void QCALLTYPE RhInitializeFinalizerThread()
9494
g_FinalizerEvent.Set();
9595
}
9696

97-
static int32_t g_fullGcCountSeenByFinalization;
98-
99-
// Indicate that the current round of finalizations is complete.
100-
EXTERN_C void QCALLTYPE RhpSignalFinalizationComplete(uint32_t fcount, int32_t observedFullGcCount)
101-
{
102-
FireEtwGCFinalizersEnd_V1(fcount, GetClrInstanceId());
103-
104-
g_fullGcCountSeenByFinalization = observedFullGcCount;
105-
g_FinalizerDoneEvent.Set();
106-
107-
if (YieldProcessorNormalization::IsMeasurementScheduled())
108-
{
109-
YieldProcessorNormalization::PerformMeasurement();
110-
}
111-
}
112-
11397
EXTERN_C void QCALLTYPE RhWaitForPendingFinalizers(UInt32_BOOL allowReentrantWait)
11498
{
11599
// This must be called via p/invoke rather than RuntimeImport since it blocks and could starve the GC if
@@ -119,32 +103,13 @@ EXTERN_C void QCALLTYPE RhWaitForPendingFinalizers(UInt32_BOOL allowReentrantWai
119103
// Can't call this from the finalizer thread itself.
120104
if (ThreadStore::GetCurrentThread() != g_pFinalizerThread)
121105
{
122-
// We may see a completion of finalization cycle that might not see objects that became
123-
// F-reachable in recent GCs. In such case we want to wait for a completion of another cycle.
124-
// However, since an object cannot be prevented from promoting, one can only rely on Full GCs
125-
// to collect unreferenced objects deterministically. Thus we only care about Full GCs here.
126-
int desiredFullGcCount =
127-
GCHeapUtilities::GetGCHeap()->CollectionCount(GCHeapUtilities::GetGCHeap()->GetMaxGeneration());
128-
129-
tryAgain:
130106
// Clear any current indication that a finalization pass is finished and wake the finalizer thread up
131107
// (if there's no work to do it'll set the done event immediately).
132108
g_FinalizerDoneEvent.Reset();
133109
g_FinalizerEvent.Set();
134110

135111
// Wait for the finalizer thread to get back to us.
136112
g_FinalizerDoneEvent.Wait(INFINITE, false, allowReentrantWait);
137-
138-
// we use unsigned math here as the collection counts, which are size_t internally,
139-
// can in theory overflow an int and wrap around.
140-
// unsigned math would have more defined/portable behavior in such case
141-
if ((int)((unsigned int)desiredFullGcCount - (unsigned int)g_fullGcCountSeenByFinalization) > 0)
142-
{
143-
// There were some Full GCs happening before we started waiting and possibly not seen by the
144-
// last finalization cycle. This is rare, but we need to be sure we have seen those,
145-
// so we try one more time.
146-
goto tryAgain;
147-
}
148113
}
149114
}
150115

@@ -211,6 +176,18 @@ EXTERN_C UInt32_BOOL QCALLTYPE RhpWaitForFinalizerRequest()
211176
} while (true);
212177
}
213178

179+
// Indicate that the current round of finalizations is complete.
180+
EXTERN_C void QCALLTYPE RhpSignalFinalizationComplete(uint32_t fcount)
181+
{
182+
FireEtwGCFinalizersEnd_V1(fcount, GetClrInstanceId());
183+
g_FinalizerDoneEvent.Set();
184+
185+
if (YieldProcessorNormalization::IsMeasurementScheduled())
186+
{
187+
YieldProcessorNormalization::PerformMeasurement();
188+
}
189+
}
190+
214191
//
215192
// The following helpers are special in that they interact with internal GC state or directly manipulate
216193
// managed references so they're called with a special co-operative p/invoke.

src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeImports.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,5 @@ internal static IntPtr RhGetModuleSection(TypeManagerHandle module, ReadyToRunSe
104104
[MethodImplAttribute(MethodImplOptions.InternalCall)]
105105
[RuntimeImport(RuntimeLibrary, "RhBulkMoveWithWriteBarrier")]
106106
internal static extern unsafe void RhBulkMoveWithWriteBarrier(ref byte dmem, ref byte smem, nuint size);
107-
108-
// Get maximum GC generation number.
109-
[MethodImplAttribute(MethodImplOptions.InternalCall)]
110-
[RuntimeImport(RuntimeLibrary, "RhGetMaxGcGeneration")]
111-
internal static extern int RhGetMaxGcGeneration();
112-
113-
// Get count of collections so far.
114-
[MethodImplAttribute(MethodImplOptions.InternalCall)]
115-
[RuntimeImport(RuntimeLibrary, "RhGetGcCollectionCount")]
116-
internal static extern int RhGetGcCollectionCount(int generation, bool getSpecialGCCount);
117107
}
118108
}

src/coreclr/vm/finalizerthread.cpp

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -404,15 +404,13 @@ VOID FinalizerThread::FinalizerThreadWorker(void *args)
404404
}
405405
LOG((LF_GC, LL_INFO100, "***** Calling Finalizers\n"));
406406

407-
int observedFullGcCount =
408-
GCHeapUtilities::GetGCHeap()->CollectionCount(GCHeapUtilities::GetGCHeap()->GetMaxGeneration());
409407
FinalizeAllObjects();
410408

411409
// Anyone waiting to drain the Q can now wake up. Note that there is a
412410
// race in that another thread starting a drain, as we leave a drain, may
413-
// consider itself satisfied by the drain that just completed.
414-
// Thus we include the Full GC count that we have certaily observed.
415-
SignalFinalizationDone(observedFullGcCount);
411+
// consider itself satisfied by the drain that just completed. This is
412+
// acceptable.
413+
SignalFinalizationDone();
416414
}
417415

418416
if (s_InitializedFinalizerThreadForPlatform)
@@ -540,13 +538,10 @@ void FinalizerThread::FinalizerThreadCreate()
540538
}
541539
}
542540

543-
static int g_fullGcCountSeenByFinalization;
544-
545-
void FinalizerThread::SignalFinalizationDone(int observedFullGcCount)
541+
void FinalizerThread::SignalFinalizationDone()
546542
{
547543
WRAPPER_NO_CONTRACT;
548544

549-
g_fullGcCountSeenByFinalization = observedFullGcCount;
550545
hEventFinalizerDone->Set();
551546
}
552547

@@ -560,13 +555,6 @@ void FinalizerThread::FinalizerThreadWait()
560555
// Can't call this from within a finalized method.
561556
if (!IsCurrentThreadFinalizer())
562557
{
563-
// We may see a completion of finalization cycle that might not see objects that became
564-
// F-reachable in recent GCs. In such case we want to wait for a completion of another cycle.
565-
// However, since an object cannot be prevented from promoting, one can only rely on Full GCs
566-
// to collect unreferenced objects deterministically. Thus we only care about Full GCs here.
567-
int desiredFullGcCount =
568-
GCHeapUtilities::GetGCHeap()->CollectionCount(GCHeapUtilities::GetGCHeap()->GetMaxGeneration());
569-
570558
GCX_PREEMP();
571559

572560
#ifdef FEATURE_COMINTEROP
@@ -577,8 +565,8 @@ void FinalizerThread::FinalizerThreadWait()
577565
g_pRCWCleanupList->CleanupWrappersInCurrentCtxThread();
578566
#endif // FEATURE_COMINTEROP
579567

580-
tryAgain:
581568
hEventFinalizerDone->Reset();
569+
582570
EnableFinalization();
583571

584572
// Under GC stress the finalizer queue may never go empty as frequent
@@ -592,18 +580,6 @@ void FinalizerThread::FinalizerThreadWait()
592580

593581
DWORD status;
594582
status = hEventFinalizerDone->Wait(INFINITE,TRUE);
595-
596-
// we use unsigned math here as the collection counts, which are size_t internally,
597-
// can in theory overflow an int and wrap around.
598-
// unsigned math would have more defined/portable behavior in such case
599-
if ((int)((unsigned int)desiredFullGcCount - (unsigned int)g_fullGcCountSeenByFinalization) > 0)
600-
{
601-
// There were some Full GCs happening before we started waiting and possibly not seen by the
602-
// last finalization cycle. This is rare, but we need to be sure we have seen those,
603-
// so we try one more time.
604-
goto tryAgain;
605-
}
606-
607583
_ASSERTE(status == WAIT_OBJECT_0);
608584
}
609585
}

src/coreclr/vm/finalizerthread.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class FinalizerThread
6767

6868
static void FinalizerThreadWait();
6969

70-
static void SignalFinalizationDone(int observedFullGcCount);
70+
static void SignalFinalizationDone();
7171

7272
static VOID FinalizerThreadWorker(void *args);
7373
static DWORD WINAPI FinalizerThreadStart(void *args);

src/libraries/System.Runtime/tests/System.Runtime.Tests/System/GCTests.cs

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using System.Runtime.InteropServices;
88
using System.Diagnostics;
99
using System.Threading;
10-
using System.Threading.Tasks;
1110
using System.Runtime;
1211
using Microsoft.DotNet.RemoteExecutor;
1312
using Xunit;
@@ -293,55 +292,6 @@ private class TestObject
293292
}
294293
}
295294

296-
[OuterLoop]
297-
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))]
298-
public static void WaitForPendingFinalizersRaces()
299-
{
300-
Task.Run(Test);
301-
Task.Run(Test);
302-
Task.Run(Test);
303-
Task.Run(Test);
304-
Task.Run(Test);
305-
Task.Run(Test);
306-
Test();
307-
308-
static void Test()
309-
{
310-
for (int i = 0; i < 20000; i++)
311-
{
312-
BoxedFinalized flag = new BoxedFinalized();
313-
MakeAndNull(flag);
314-
GC.Collect();
315-
GC.WaitForPendingFinalizers();
316-
Assert.True(flag.finalized);
317-
}
318-
}
319-
320-
[MethodImpl(MethodImplOptions.NoInlining)]
321-
static void MakeAndNull(BoxedFinalized flag)
322-
{
323-
var deadObj = new TestObjectWithFinalizer(flag);
324-
// it's dead here
325-
};
326-
}
327-
328-
class BoxedFinalized
329-
{
330-
public bool finalized;
331-
}
332-
333-
class TestObjectWithFinalizer
334-
{
335-
BoxedFinalized _flag;
336-
337-
public TestObjectWithFinalizer(BoxedFinalized flag)
338-
{
339-
_flag = flag;
340-
}
341-
342-
~TestObjectWithFinalizer() => _flag.finalized = true;
343-
}
344-
345295
[Fact]
346296
public static void SuppressFinalizer_NullObject_ThrowsArgumentNullException()
347297
{

0 commit comments

Comments
 (0)