-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
Test failure: System.Tests.GCTests.KeepAlive_Null #104218
Comments
Doesn't look to be jitstress specific, fails on: using System.Runtime.CompilerServices;
using Xunit;
class KeepAliveNullTest
{
private static void Main()
{
for (int i = 0; i < 200000; i++)
{
TestObject.Finalized = false;
MakeAndNull();
GC.Collect();
GC.WaitForPendingFinalizers();
Assert.True(TestObject.Finalized);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void MakeAndNull()
{
var deadObj = new TestObject();
// it's dead here
}
public class TestObject
{
public static bool Finalized { get; set; }
~TestObject() => Finalized = true;
}
} with |
Tagging subscribers to this area: @dotnet/gc |
Unless This is a very simple case. The object gets unreachable and GC.Collect() should see that. Could it be that NoInlining is not working? |
I've just checked - both |
Seems like the DATAS is the culprit, the test doesn't fail with |
My initial bisection suggests f69972f...d2dbdd0, though the repro is intermittent so it's possible I just haven't seen it repro with f69972f yet. In any case if that's the range #103501 looks most interesting, cc @jkotas |
I do not think that the problem is with GC not collecting the object. I think the problem is in the WaitForPendingFinalizers() synchronization. If I change the loop to be:
I never see The code has a comment about such race condition and considers it acceptable: runtime/src/coreclr/vm/finalizerthread.cpp Lines 409 to 412 in 104e88d
This change made finalization loop more efficient. It makes any race conditions related to finalization more likely to show up. |
While it's understandable, I bet we have a ton of test cases here and there relying on |
I think that race goes like:
But can this happen in a completely single-threaded test? (and as I understand happens fairly reliably now) |
Background event source activity. There is a ton of it on typical machine. |
I have seen code where sequence |
Perhaps the WFPF should check if the queue is empty and if not, wait on the event one more time before returning? This API does not need to be very performant, but being less racey, if possible, could be good. |
Can we add some flag or counter to guarantee that |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
@VSadov Do you plan to work on this as part of your effort to improve reliability of tests that use |
I can take a look, but I think this should be a separate change. What we have here is a problem with actual behavior of the feature. Ideally the fix is just removing the race, so it is unobservable to the user code, but the change would need a bit more careful. |
Yes, I agree it should be a separate change. |
Failed in: runtime-coreclr libraries-jitstress 20240702.1 Failed tests:
Error message:
Stack trace:
|
Here is a complete repro: using System.Runtime.CompilerServices;
unsafe class KeepAliveNullTest
{
private static void Main()
{
Task.Run(Test);
Task.Run(Test);
Task.Run(Test);
Task.Run(Test);
Task.Run(Test);
Task.Run(Test);
Test();
}
private static void Test()
{
for (int i = 0; i < 20000000; i++)
{
bool finalized = false;
MakeAndNull(&finalized);
GC.Collect();
GC.WaitForPendingFinalizers();
if (!finalized) Console.WriteLine("FAIL1");
GC.Collect();
GC.WaitForPendingFinalizers();
if (!finalized) Console.WriteLine("FAIL2");
GC.Collect();
GC.WaitForPendingFinalizers();
if (!finalized) Console.WriteLine("FAIL3");
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void MakeAndNull(bool* flag)
{
var deadObj = new TestObject(flag);
// it's dead here
}
public class TestObject
{
bool* _flag;
public TestObject(bool* flag)
{
_flag = flag;
}
~TestObject() => *_flag = true;
}
} Prints:
|
Interesting: this does not repro on 8.0 Core and does not repro on 9.0 NativeAOT It is possible that it is just different timing. |
Saw some |
The fix in #105289 works. No failures seen in either CoreCLR or NativeAOT compiled test. |
Failed in: runtime-coreclr libraries-jitstressregs 20240630.1
Failed tests:
Error message:
Stack trace:
The text was updated successfully, but these errors were encountered: