Skip to content

Commit

Permalink
Try to fix finalizer-based async tests (#56384)
Browse files Browse the repository at this point in the history
A dump from a failure showed the finalizable object we're relying on was unrooted and waiting for finalization.  Issue more Collects to see if that helps.
  • Loading branch information
stephentoub authored Jul 27, 2021
1 parent 392ff31 commit cc12918
Show file tree
Hide file tree
Showing 2 changed files with 10 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ public static async Task AsyncMethodsDropsStateMachineAndExecutionContextUponCom
// We want to make sure that holding on to the resulting Task doesn't keep
// that finalizable object alive.

var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
bool finalized = false;

Task t = null;

Expand All @@ -544,7 +544,7 @@ async Task YieldOnceAsync(object s)
GC.KeepAlive(s); // keep s referenced by the state machine
}
var state = new InvokeActionOnFinalization { Action = () => tcs.SetResult() };
var state = new InvokeActionOnFinalization { Action = () => Volatile.Write(ref finalized, true) };
var al = new AsyncLocal<object>() { Value = state }; // ensure the object is stored in ExecutionContext
t = YieldOnceAsync(state); // ensure the object is stored in the state machine
al.Value = null;
Expand All @@ -556,19 +556,15 @@ async Task YieldOnceAsync(object s)
await t; // wait for the async method to complete and clear out its state
await Task.Yield(); // ensure associated state is not still on the stack as part of the antecedent's execution

for (int i = 0; i < 2; i++)
for (int i = 0; i < 10 && !Volatile.Read(ref finalized); i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}

try
{
await tcs.Task.WaitAsync(TimeSpan.FromSeconds(60));
}
catch (Exception e)
if (!Volatile.Read(ref finalized))
{
Environment.FailFast("Look at the created dump", e);
Environment.FailFast("Look at the created dump");
}

GC.KeepAlive(t); // ensure the object is stored in the state machine
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ public static async Task TaskDropsExecutionContextUponCompletion()
// We want to make sure that holding on to the resulting Task doesn't keep
// that finalizable object alive.

var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
bool finalized = false;

Task t = null;

Thread runner = new Thread(() =>
{
var state = new InvokeActionOnFinalization { Action = () => tcs.SetResult() };
var state = new InvokeActionOnFinalization { Action = () => Volatile.Write(ref finalized, true) };
var al = new AsyncLocal<object>(){ Value = state }; // ensure the object is stored in ExecutionContext
t = Task.Run(() => { }); // run a task that'll capture EC
al.Value = null;
Expand All @@ -54,19 +54,15 @@ public static async Task TaskDropsExecutionContextUponCompletion()

await t; // wait for the task method to complete and clear out its state

for (int i = 0; i < 2; i++)
for (int i = 0; i < 10 && !Volatile.Read(ref finalized); i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}

try
{
await tcs.Task.WaitAsync(TimeSpan.FromSeconds(60)); // finalizable object should have been collected and finalized
}
catch (Exception e)
if (!Volatile.Read(ref finalized))
{
Environment.FailFast("Look at the created dump", e);
Environment.FailFast("Look at the created dump");
}

GC.KeepAlive(t); // ensure the object is stored in the state machine
Expand Down

0 comments on commit cc12918

Please sign in to comment.