From 8e774c42fb797a3b847a6631616f4401098778c1 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Fri, 24 May 2019 15:05:04 -0700 Subject: [PATCH] Use Thread instead of Task.Run in some Task tests (dotnet/corefx#37932) Stephen Toub pointed that it is possible when using Task.Run, there could be some ThreadLocal fields on the thread executing the task holding a reference to the object we are trying to GC collect and finalize. The fix here is use Thread objects instead of Task. Commit migrated from https://github.com/dotnet/corefx/commit/bf7dbd6d37285a147dd30f5dfca6582046dcabc7 --- .../AsyncTaskMethodBuilderTests.cs | 18 +++++++----------- .../tests/Task/ExecutionContextFlowTest.cs | 18 +++++++----------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/AsyncTaskMethodBuilderTests.cs b/src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/AsyncTaskMethodBuilderTests.cs index 25f07ff..495c359 100644 --- a/src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/AsyncTaskMethodBuilderTests.cs +++ b/src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/AsyncTaskMethodBuilderTests.cs @@ -539,7 +539,8 @@ namespace System.Threading.Tasks.Tests var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); Task t = null; - await Task.Run(delegate // avoid any issues with the stack keeping the object alive, and escape xunit sync ctx + + Thread runner = new Thread(() => { async Task YieldOnceAsync(object s) { @@ -548,18 +549,13 @@ namespace System.Threading.Tasks.Tests } var state = new InvokeActionOnFinalization { Action = () => tcs.SetResult(true) }; - var al = new AsyncLocal(args => { - // Temporary logging to get more info when the test timeout to look who hold a reference to the finalizer object. - string currentValue = args.CurrentValue == null ? "'null'" : "'Object'"; - string previousValue = args.PreviousValue == null ? "'null'" : "'Object'"; - Console.WriteLine($"AsyncMethodsDropsStateMachineAndExecutionContextUponCompletion: Thread Id: {Thread.CurrentThread.ManagedThreadId} Current Value: {currentValue} Previous Value: {previousValue} ThreadContextChanged: {args.ThreadContextChanged}"); - }) - { - Value = state - }; // ensure the object is stored in ExecutionContext + var al = new AsyncLocal() { Value = state }; // ensure the object is stored in ExecutionContext t = YieldOnceAsync(state); // ensure the object is stored in the state machine al.Value = null; - }); + }) { IsBackground = true }; + + runner.Start(); + runner.Join(); 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 diff --git a/src/libraries/System.Threading.Tasks/tests/Task/ExecutionContextFlowTest.cs b/src/libraries/System.Threading.Tasks/tests/Task/ExecutionContextFlowTest.cs index b4122dc..b53d4f8 100644 --- a/src/libraries/System.Threading.Tasks/tests/Task/ExecutionContextFlowTest.cs +++ b/src/libraries/System.Threading.Tasks/tests/Task/ExecutionContextFlowTest.cs @@ -42,21 +42,17 @@ namespace System.Threading.Tasks.Tests var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); Task t = null; - await Task.Run(delegate // avoid any issues with the stack keeping the object alive + + Thread runner = new Thread(() => { var state = new InvokeActionOnFinalization { Action = () => tcs.SetResult(true) }; - var al = new AsyncLocal(args => { - // Temporary logging to get more info when the test timeout to look who hold a reference to the finalizer object. - string currentValue = args.CurrentValue == null ? "'null'" : "'Object'"; - string previousValue = args.PreviousValue == null ? "'null'" : "'Object'"; - Console.WriteLine($"TaskDropsExecutionContextUponCompletion: Thread Id: {Thread.CurrentThread.ManagedThreadId} Current Value: {currentValue} Previous Value: {previousValue} ThreadContextChanged: {args.ThreadContextChanged}"); - }) - { - Value = state - }; // ensure the object is stored in ExecutionContext + var al = new AsyncLocal(){ Value = state }; // ensure the object is stored in ExecutionContext t = Task.Run(() => { }); // run a task that'll capture EC al.Value = null; - }); + }) { IsBackground = true }; + + runner.Start(); + runner.Join(); await t; // wait for the task method to complete and clear out its state -- 2.7.4