Reduce Task's static ctor dependencies (#22172)
authorMarek Safar <marek.safar@gmail.com>
Fri, 25 Jan 2019 15:58:14 +0000 (16:58 +0100)
committerStephen Toub <stoub@microsoft.com>
Fri, 25 Jan 2019 15:58:14 +0000 (10:58 -0500)
* Reduce Task's static ctor dependencies

* Remove defensive check

* Revert "Remove defensive check"

This reverts commit df15ab0350e8f44fc5cbb1a18c5211cd1de11989.

src/System.Private.CoreLib/shared/System/Threading/Tasks/Task.cs

index 37fce39..3f580c4 100644 (file)
@@ -209,8 +209,7 @@ namespace System.Threading.Tasks
 
         // This dictonary relates the task id, from an operation id located in the Async Causality log to the actual
         // task. This is to be used by the debugger ONLY. Task in this dictionary represent current active tasks.
-        private static readonly Dictionary<int, Task> s_currentActiveTasks = new Dictionary<int, Task>();
-        private static readonly object s_activeTasksLock = new object();
+        private static Dictionary<int, Task> s_currentActiveTasks;
 
         // These methods are a way to access the dictionary both from this class and for other classes that also
         // activate dummy tasks. Specifically the AsyncTaskMethodBuilder and AsyncTaskMethodBuilder<>
@@ -218,8 +217,10 @@ namespace System.Threading.Tasks
         {
             Debug.Assert(task != null, "Null Task objects can't be added to the ActiveTasks collection");
 
+            LazyInitializer.EnsureInitialized(ref s_currentActiveTasks, () => new Dictionary<int, Task>());
+
             int taskId = task.Id;
-            lock (s_activeTasksLock)
+            lock (s_currentActiveTasks)
             {
                 s_currentActiveTasks[taskId] = task;
             }
@@ -229,8 +230,11 @@ namespace System.Threading.Tasks
 
         internal static void RemoveFromActiveTasks(Task task)
         {
+            if (s_currentActiveTasks == null)
+                return;
+
             int taskId = task.Id;
-            lock (s_activeTasksLock)
+            lock (s_currentActiveTasks)
             {
                 s_currentActiveTasks.Remove(taskId);
             }
@@ -640,15 +644,24 @@ namespace System.Threading.Tasks
                         if (antecedent == null)
                         {
                             // if no antecedent was specified, use this task's reference as the cancellation state object
-                            ctr = cancellationToken.UnsafeRegister(s_taskCancelCallback, this);
+                            ctr = cancellationToken.UnsafeRegister(t => ((Task)t).InternalCancel(false), this);
                         }
                         else
                         {
                             // If an antecedent was specified, pack this task, its antecedent and the TaskContinuation together as a tuple 
                             // and use it as the cancellation state object. This will be unpacked in the cancellation callback so that 
                             // antecedent.RemoveCancellation(continuation) can be invoked.
-                            ctr = cancellationToken.UnsafeRegister(s_taskCancelCallback,
-                                                                              new Tuple<Task, Task, TaskContinuation>(this, antecedent, continuation));
+                            ctr = cancellationToken.UnsafeRegister(t =>
+                            {
+                                var tuple = (Tuple<Task, Task, TaskContinuation>)t;
+            
+                                Task targetTask = tuple.Item1;
+                                Task antecedentTask = tuple.Item2;
+
+                                antecedentTask.RemoveContinuation(tuple.Item3);
+                                targetTask.InternalCancel(false);
+                            },
+                            new Tuple<Task, Task, TaskContinuation>(this, antecedent, continuation));
                         }
 
                         props.m_cancellationRegistration = new Shared<CancellationTokenRegistration>(ctr);
@@ -670,29 +683,6 @@ namespace System.Threading.Tasks
             }
         }
 
-
-        // Static delegate to be used as a cancellation callback on unstarted tasks that have a valid cancellation token.
-        // This is necessary to transition them into canceled state if their cancellation token is signalled while they are still not queued
-        private readonly static Action<object> s_taskCancelCallback = new Action<object>(TaskCancelCallback);
-        private static void TaskCancelCallback(object o)
-        {
-            var targetTask = o as Task;
-            if (targetTask == null)
-            {
-                if (o is Tuple<Task, Task, TaskContinuation> tuple)
-                {
-                    targetTask = tuple.Item1;
-
-                    Task antecedentTask = tuple.Item2;
-                    TaskContinuation continuation = tuple.Item3;
-                    antecedentTask.RemoveContinuation(continuation);
-                }
-            }
-            Debug.Assert(targetTask != null,
-                "targetTask should have been non-null, with the supplied argument being a task or a tuple containing one");
-            targetTask.InternalCancel(false);
-        }
-
         // Debugger support
         private string DebuggerDisplayMethodDescription
         {
@@ -2117,15 +2107,12 @@ namespace System.Threading.Tasks
                 {
                     lock (exceptionalChildren)
                     {
-                        exceptionalChildren.RemoveAll(s_IsExceptionObservedByParentPredicate); // RemoveAll has better performance than doing it ourselves
+                        exceptionalChildren.RemoveAll(t => t.IsExceptionObservedByParent); // RemoveAll has better performance than doing it ourselves
                     }
                 }
             }
         }
 
-        // statically allocated delegate for the removeall expression in Finish()
-        private readonly static Predicate<Task> s_IsExceptionObservedByParentPredicate = new Predicate<Task>((t) => { return t.IsExceptionObservedByParent; });
-
         /// <summary>
         /// FinishStageTwo is to be executed as soon as we known there are no more children to complete. 
         /// It can happen i) either on the thread that originally executed this task (if no children were spawned, or they all completed by the time this task's delegate quit)
@@ -4350,7 +4337,7 @@ namespace System.Threading.Tasks
                         // result from RemoveContinuations()
                         if (list.Count == list.Capacity)
                         {
-                            list.RemoveAll(s_IsTaskContinuationNullPredicate);
+                            list.RemoveAll(l => l == null);
                         }
 
                         if (addBeforeOthers)
@@ -4437,11 +4424,6 @@ namespace System.Threading.Tasks
             }
         }
 
-        // statically allocated delegate for the RemoveAll expression in RemoveContinuations() and AddContinuationComplex()
-        private readonly static Predicate<object> s_IsTaskContinuationNullPredicate =
-            new Predicate<object>((tc) => { return (tc == null); });
-
-
         //
         // Wait methods
         //