Move TaskContinuation to shared partition (dotnet/coreclr#22167)
authorMarek Safar <marek.safar@gmail.com>
Thu, 24 Jan 2019 20:56:41 +0000 (21:56 +0100)
committerJan Kotas <jkotas@microsoft.com>
Thu, 24 Jan 2019 20:56:41 +0000 (12:56 -0800)
* Move TaskContinuation to shared partition

* Remove TASK_STATE_THREAD_WAS_ABORTED from Task

* Use already existing ThrowAsync

Commit migrated from https://github.com/dotnet/coreclr/commit/ef52964bb6db4687265abda8be65aa4c930796d0

src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs [moved from src/coreclr/src/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs with 93% similarity]

index 04b374f..e293399 100644 (file)
     <Compile Include="$(BclSourcesRoot)\System\Threading\Monitor.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\Overlapped.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\SynchronizationContext.cs" />
-    <Compile Include="$(BclSourcesRoot)\System\Threading\Tasks\TaskContinuation.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\Thread.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\ThreadPool.CoreCLR.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\Timer.cs" />
index 6c5da60..7640d41 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\Task.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskCanceledException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskCompletionSource.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskContinuation.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskExceptionHolder.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskExtensions.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskFactory.cs" />
index 21f1810..217f530 100644 (file)
@@ -14,14 +14,10 @@ using System.Diagnostics;
 using System.Diagnostics.Tracing;
 using System.Reflection;
 using System.Runtime.ExceptionServices;
-#if FEATURE_COMINTEROP
-using System.Runtime.InteropServices.WindowsRuntime;
-#endif // FEATURE_COMINTEROP
 using System.Threading;
 using System.Threading.Tasks;
 using System.Text;
 using Internal.Runtime.CompilerServices;
-using Internal.Runtime.Augments;
 
 #if CORERT
 using Thread = Internal.Runtime.Augments.RuntimeThread;
@@ -131,7 +127,7 @@ namespace System.Runtime.CompilerServices
                 // and decrement its outstanding operation count.
                 try
                 {
-                    AsyncMethodBuilderCore.ThrowAsync(exception, targetContext: _synchronizationContext);
+                    System.Threading.Tasks.Task.ThrowAsync(exception, targetContext: _synchronizationContext);
                 }
                 finally
                 {
@@ -143,7 +139,7 @@ namespace System.Runtime.CompilerServices
                 // Otherwise, queue the exception to be thrown on the ThreadPool.  This will
                 // result in a crash unless legacy exception behavior is enabled by a config
                 // file or a CLR host.
-                AsyncMethodBuilderCore.ThrowAsync(exception, targetContext: null);
+                System.Threading.Tasks.Task.ThrowAsync(exception, targetContext: null);
             }
 
             // The exception was propagated already; we don't need or want to fault the builder, just mark it as completed.
@@ -162,7 +158,7 @@ namespace System.Runtime.CompilerServices
             {
                 // If the interaction with the SynchronizationContext goes awry,
                 // fall back to propagating on the ThreadPool.
-                AsyncMethodBuilderCore.ThrowAsync(exc, targetContext: null);
+                Task.ThrowAsync(exc, targetContext: null);
             }
         }
 
@@ -369,7 +365,7 @@ namespace System.Runtime.CompilerServices
             }
             catch (Exception e)
             {
-                AsyncMethodBuilderCore.ThrowAsync(e, targetContext: null);
+                System.Threading.Tasks.Task.ThrowAsync(e, targetContext: null);
             }
         }
 
@@ -415,7 +411,7 @@ namespace System.Runtime.CompilerServices
                     // exceptions well at that location in the state machine, especially if the exception may occur
                     // after the ValueTaskAwaiter already successfully hooked up the callback, in which case it's possible
                     // two different flows of execution could end up happening in the same async method call.
-                    AsyncMethodBuilderCore.ThrowAsync(e, targetContext: null);
+                    System.Threading.Tasks.Task.ThrowAsync(e, targetContext: null);
                 }
             }
             else
@@ -427,7 +423,7 @@ namespace System.Runtime.CompilerServices
                 }
                 catch (Exception e)
                 {
-                    AsyncMethodBuilderCore.ThrowAsync(e, targetContext: null);
+                    System.Threading.Tasks.Task.ThrowAsync(e, targetContext: null);
                 }
             }
         }
@@ -1069,48 +1065,6 @@ namespace System.Runtime.CompilerServices
         internal static Task TryGetContinuationTask(Action continuation) =>
             (continuation?.Target as ContinuationWrapper)?._innerTask;
 
-        /// <summary>Throws the exception on the ThreadPool.</summary>
-        /// <param name="exception">The exception to propagate.</param>
-        /// <param name="targetContext">The target context on which to propagate the exception.  Null to use the ThreadPool.</param>
-        internal static void ThrowAsync(Exception exception, SynchronizationContext targetContext)
-        {
-            // Capture the exception into an ExceptionDispatchInfo so that its 
-            // stack trace and Watson bucket info will be preserved
-            var edi = ExceptionDispatchInfo.Capture(exception);
-
-            // If the user supplied a SynchronizationContext...
-            if (targetContext != null)
-            {
-                try
-                {
-                    // Post the throwing of the exception to that context, and return.
-                    targetContext.Post(state => ((ExceptionDispatchInfo)state).Throw(), edi);
-                    return;
-                }
-                catch (Exception postException)
-                {
-                    // If something goes horribly wrong in the Post, we'll 
-                    // propagate both exceptions on the ThreadPool
-                    edi = ExceptionDispatchInfo.Capture(new AggregateException(exception, postException));
-                }
-            }
-
-#if CORERT
-            RuntimeAugments.ReportUnhandledException(edi.SourceException);
-#else
-
-#if FEATURE_COMINTEROP
-            // If we have the new error reporting APIs, report this error.
-            if (WindowsRuntimeMarshal.ReportUnhandledError(edi.SourceException))
-                return;
-#endif // FEATURE_COMINTEROP
-
-            // Propagate the exception(s) on the ThreadPool
-            ThreadPool.QueueUserWorkItem(state => ((ExceptionDispatchInfo)state).Throw(), edi);
-
-#endif // CORERT
-        }
-
         /// <summary>
         /// Logically we pass just an Action (delegate) to a task for its action to 'ContinueWith' when it completes.
         /// However debuggers and profilers need more information about what that action is. (In particular what 
index e518a84..6f49037 100644 (file)
@@ -184,7 +184,6 @@ namespace System.Threading.Tasks
         internal const int TASK_STATE_RAN_TO_COMPLETION = 0x1000000;                           //bin: 0000 0001 0000 0000 0000 0000 0000 0000
         internal const int TASK_STATE_WAITINGFORACTIVATION = 0x2000000;                        //bin: 0000 0010 0000 0000 0000 0000 0000 0000
         internal const int TASK_STATE_COMPLETION_RESERVED = 0x4000000;                         //bin: 0000 0100 0000 0000 0000 0000 0000 0000
-        internal const int TASK_STATE_THREAD_WAS_ABORTED = 0x8000000;                          //bin: 0000 1000 0000 0000 0000 0000 0000 0000
         internal const int TASK_STATE_WAIT_COMPLETION_NOTIFICATION = 0x10000000;               //bin: 0001 0000 0000 0000 0000 0000 0000 0000
         //This could be moved to InternalTaskOptions enum
         internal const int TASK_STATE_EXECUTIONCONTEXT_IS_NULL = 0x20000000;                   //bin: 0010 0000 0000 0000 0000 0000 0000 0000
@@ -1136,9 +1135,8 @@ namespace System.Threading.Tasks
                 }
                 catch (Exception e)
                 {
-                    // we 1) either received an unexpected exception originating from a custom scheduler, which needs to be wrapped in a TSE and thrown
-                    //    2) or a a ThreadAbortException, which we need to skip here, because it would already have been handled in Task.Execute
-                    if (!taskQueued && !(e is ThreadAbortException))
+                    // we received an unexpected exception originating from a custom scheduler, which needs to be wrapped in a TSE and thrown
+                    if (!taskQueued)
                     {
                         // We had a problem with TryRunInline() or QueueTask().  
                         // Record the exception, marking ourselves as Completed/Faulted.
@@ -1160,7 +1158,7 @@ namespace System.Threading.Tasks
                         // And re-throw.
                         throw tse;
                     }
-                    // We had a problem with waiting or this is a thread abort.  Just re-throw.
+                    // We had a problem with waiting. Just re-throw.
                     else throw;
                 }
             }
@@ -1964,6 +1962,48 @@ namespace System.Threading.Tasks
             }
         }
 
+        /// <summary>Throws the exception on the ThreadPool.</summary>
+        /// <param name="exception">The exception to propagate.</param>
+        /// <param name="targetContext">The target context on which to propagate the exception.  Null to use the ThreadPool.</param>
+        internal static void ThrowAsync(Exception exception, SynchronizationContext targetContext)
+        {
+            // Capture the exception into an ExceptionDispatchInfo so that its 
+            // stack trace and Watson bucket info will be preserved
+            var edi = ExceptionDispatchInfo.Capture(exception);
+
+            // If the user supplied a SynchronizationContext...
+            if (targetContext != null)
+            {
+                try
+                {
+                    // Post the throwing of the exception to that context, and return.
+                    targetContext.Post(state => ((ExceptionDispatchInfo)state).Throw(), edi);
+                    return;
+                }
+                catch (Exception postException)
+                {
+                    // If something goes horribly wrong in the Post, we'll 
+                    // propagate both exceptions on the ThreadPool
+                    edi = ExceptionDispatchInfo.Capture(new AggregateException(exception, postException));
+                }
+            }
+
+#if CORERT
+            RuntimeAugments.ReportUnhandledException(edi.SourceException);
+#else
+
+#if FEATURE_COMINTEROP
+            // If we have the new error reporting APIs, report this error.
+            if (System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.ReportUnhandledError(edi.SourceException))
+                return;
+#endif
+
+            // Propagate the exception(s) on the ThreadPool
+            ThreadPool.QueueUserWorkItem(state => ((ExceptionDispatchInfo)state).Throw(), edi);
+
+#endif // CORERT
+        }
+
         /// <summary>
         /// Checks whether this is an attached task, and whether we are being called by the parent task.
         /// And sets the TASK_STATE_EXCEPTIONOBSERVEDBYPARENT status flag based on that.
@@ -3202,9 +3242,7 @@ namespace System.Threading.Tasks
             if (AsyncCausalityTracer.LoggingOn)
                 AsyncCausalityTracer.TraceSynchronousWorkStart(this, CausalitySynchronousWork.CompletionNotification);
 
-            // Skip synchronous execution of continuations if this task's thread was aborted
-            bool canInlineContinuations = !(((m_stateFlags & TASK_STATE_THREAD_WAS_ABORTED) != 0) ||
-                                             ((m_stateFlags & (int)TaskCreationOptions.RunContinuationsAsynchronously) != 0));
+            bool canInlineContinuations = (m_stateFlags & (int)TaskCreationOptions.RunContinuationsAsynchronously) == 0;
 
             switch (continuationObject)
             {
@@ -2,23 +2,14 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
-//
-//
-//
-// Implementation of task continuations, TaskContinuation, and its descendants.
-//
-// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-
 using System.Security;
 using System.Diagnostics;
 using System.Runtime.ExceptionServices;
 using System.Runtime.CompilerServices;
-using System.Threading;
 
-#if FEATURE_COMINTEROP
-using System.Runtime.InteropServices.WindowsRuntime;
-#endif // FEATURE_COMINTEROP
+#if CORERT
+using Thread = Internal.Runtime.Augments.RuntimeThread;
+#endif
 
 namespace System.Threading.Tasks
 {
@@ -258,18 +249,19 @@ namespace System.Threading.Tasks
                 // Either TryRunInline() or QueueTask() threw an exception. Record the exception, marking the task as Faulted.
                 // However if it was a ThreadAbortException coming from TryRunInline we need to skip here, 
                 // because it would already have been handled in Task.Execute()
-                if (!(e is ThreadAbortException &&
-                      (task.m_stateFlags & Task.TASK_STATE_THREAD_WAS_ABORTED) != 0))    // this ensures TAEs from QueueTask will be wrapped in TSE
-                {
-                    TaskSchedulerException tse = new TaskSchedulerException(e);
-                    task.AddException(tse);
-                    task.Finish(false);
-                }
-
+                TaskSchedulerException tse = new TaskSchedulerException(e);
+                task.AddException(tse);
+                task.Finish(false);
                 // Don't re-throw.
             }
         }
 
+        //
+        // This helper routine is targeted by the debugger.
+        //
+#if PROJECTN
+        [DependencyReductionRoot]
+#endif
         internal abstract Delegate[] GetDelegateContinuationsForDebugger();
     }
 
@@ -370,7 +362,7 @@ namespace System.Threading.Tasks
     internal sealed class SynchronizationContextAwaitTaskContinuation : AwaitTaskContinuation
     {
         /// <summary>SendOrPostCallback delegate to invoke the action.</summary>
-        private readonly static SendOrPostCallback s_postCallback = state => ((Action)state)(); // can't use InvokeAction as it's SecurityCritical
+        private static readonly SendOrPostCallback s_postCallback = state => ((Action)state)(); // can't use InvokeAction as it's SecurityCritical
         /// <summary>Cached delegate for PostAction</summary>
         private static ContextCallback s_postActionCallback;
         /// <summary>The context with which to run the action.</summary>
@@ -499,8 +491,14 @@ namespace System.Threading.Tasks
                 // The target scheduler may still deny us from executing on this thread, in which case this'll be queued.
                 var task = CreateTask(state =>
                 {
-                    try { ((Action)state)(); }
-                    catch (Exception exc) { ThrowAsyncIfNecessary(exc); }
+                    try
+                    {
+                        ((Action)state)();
+                    }
+                    catch (Exception exception)
+                    {
+                        Task.ThrowAsync(exception, targetContext: null);
+                    }
                 }, m_action, m_scheduler);
 
                 if (inlineIfPossible)
@@ -629,7 +627,7 @@ namespace System.Threading.Tasks
                 return;
             }
 
-            Guid savedActivityId = Guid.Empty;
+            Guid savedActivityId = default;
             if (etwLog.TasksSetActivityIds && m_continuationId != 0)
             {
                 Guid activityId = TplEtwProvider.CreateGuidForTaskID(m_continuationId);
@@ -699,9 +697,9 @@ namespace System.Threading.Tasks
                     ExecutionContext.RunInternal(context, callback, state);
                 }
             }
-            catch (Exception exc) // we explicitly do not request handling of dangerous exceptions like AVs
+            catch (Exception exception) // we explicitly do not request handling of dangerous exceptions like AVs
             {
-                ThrowAsyncIfNecessary(exc);
+                Task.ThrowAsync(exception, targetContext: null);
             }
             finally
             {
@@ -741,7 +739,7 @@ namespace System.Threading.Tasks
             }
             catch (Exception exception)
             {
-                ThrowAsyncIfNecessary(exception);
+                Task.ThrowAsync(exception, targetContext: null);
             }
             finally
             {
@@ -793,7 +791,7 @@ namespace System.Threading.Tasks
             }
             catch (Exception exception)
             {
-                ThrowAsyncIfNecessary(exception);
+                Task.ThrowAsync(exception, targetContext: null);
             }
             finally
             {
@@ -818,30 +816,6 @@ namespace System.Threading.Tasks
             ThreadPool.UnsafeQueueUserWorkItemInternal(atc, preferLocal: true);
         }
 
-        /// <summary>Throws the exception asynchronously on the ThreadPool.</summary>
-        /// <param name="exc">The exception to throw.</param>
-        protected static void ThrowAsyncIfNecessary(Exception exc)
-        {
-            // Awaits should never experience an exception (other than an TAE or ADUE), 
-            // unless a malicious user is explicitly passing a throwing action into the TaskAwaiter. 
-            // We don't want to allow the exception to propagate on this stack, as it'll emerge in random places, 
-            // and we can't fail fast, as that would allow for elevation of privilege.
-            //
-            // If unhandled error reporting APIs are available use those, otherwise since this 
-            // would have executed on the thread pool otherwise, let it propagate there.
-
-            if (!(exc is ThreadAbortException))
-            {
-#if FEATURE_COMINTEROP
-                if (!WindowsRuntimeMarshal.ReportUnhandledError(exc))
-#endif // FEATURE_COMINTEROP
-                {
-                    var edi = ExceptionDispatchInfo.Capture(exc);
-                    ThreadPool.QueueUserWorkItem(s => ((ExceptionDispatchInfo)s).Throw(), edi);
-                }
-            }
-        }
-
         internal override Delegate[] GetDelegateContinuationsForDebugger()
         {
             Debug.Assert(m_action != null);