Use Thread instead of Task.Run in some Task tests (dotnet/corefx#37932)
authorTarek Mahmoud Sayed <tarekms@microsoft.com>
Fri, 24 May 2019 22:05:04 +0000 (15:05 -0700)
committerGitHub <noreply@github.com>
Fri, 24 May 2019 22:05:04 +0000 (15:05 -0700)
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

src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/AsyncTaskMethodBuilderTests.cs
src/libraries/System.Threading.Tasks/tests/Task/ExecutionContextFlowTest.cs

index 25f07ff..495c359 100644 (file)
@@ -539,7 +539,8 @@ namespace System.Threading.Tasks.Tests
             var tcs = new TaskCompletionSource<bool>(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<object>(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<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;
-            });
+            }) { 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
index b4122dc..b53d4f8 100644 (file)
@@ -42,21 +42,17 @@ namespace System.Threading.Tasks.Tests
             var tcs = new TaskCompletionSource<bool>(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<object>(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<object>(){ 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