-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ensure that WaitForPendingFinalizers has seen the expected Full GC count #105289
Changes from 3 commits
12aeee5
334f1c7
f6b5acf
55830b1
d1d2b7e
c2b8cf9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
using System.Runtime.InteropServices; | ||
using System.Diagnostics; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using System.Runtime; | ||
using Microsoft.DotNet.RemoteExecutor; | ||
using Xunit; | ||
|
@@ -292,6 +293,50 @@ private class TestObject | |
} | ||
} | ||
|
||
// [OuterLoop] // TODO: VS uncomment before merging | ||
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] | ||
public unsafe static void WaitForPendingFinalizersRaces() | ||
{ | ||
Task.Run(Test); | ||
Task.Run(Test); | ||
Task.Run(Test); | ||
Task.Run(Test); | ||
Task.Run(Test); | ||
Task.Run(Test); | ||
Test(); | ||
|
||
static void Test() | ||
{ | ||
for (int i = 0; i < 2000; i++) | ||
{ | ||
bool finalized = false; | ||
MakeAndNull(&finalized); | ||
GC.Collect(); | ||
GC.WaitForPendingFinalizers(); | ||
Assert.True(finalized); | ||
} | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.NoInlining)] | ||
static void MakeAndNull(bool* flag) | ||
{ | ||
var deadObj = new TestObjectWithFinalizer(flag); | ||
// it's dead here | ||
}; | ||
} | ||
|
||
unsafe class TestObjectWithFinalizer | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is unsafe implementation required here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Not required. I could allocate a mutable class and change a field. It is a testcase, so I used whatever was simpler. Accessing a local on another thread's stack is technically a UB in the memory model. The current code should work in any known dotnet implementation, but perhaps should change it to just use a class - to not rely on UB. |
||
{ | ||
bool* _flag; | ||
|
||
public TestObjectWithFinalizer(bool* flag) | ||
{ | ||
_flag = flag; | ||
} | ||
|
||
~TestObjectWithFinalizer() => *_flag = true; | ||
} | ||
|
||
[Fact] | ||
public static void SuppressFinalizer_NullObject_ThrowsArgumentNullException() | ||
{ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume that the subtraction and > 0 are to handle GC counts wrapping around. Signed integer overflows are undefined behavior in C++. I am not sure whether it can bite us here. Unsigned integer overflows have fully defined behavior in C++, so it would be more portable to do this with unsigned types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, a billion of gen2 gc would be a rare, but plausible scenario for a long term service.
I’d prefer the API to return size_t or uint64_t here, but it is what it is. Not sure why signed int was chosen.
We do know that collection counts are monotonically incrementing ticks that may wrap around. I’ll throw in some unsigned casts, just in case the compiler does something unexpected with compares.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One billion seconds is about 30 years. ;)
1-10 times per second is the rough guess for gen2 frequency, I think, excluding stress scenarios. So maybe signed int was chosen because it just does not matter.