Fix access to Task.Id while finishing continuations
authorstephentoub <stoub@microsoft.com>
Thu, 11 Feb 2016 21:41:43 +0000 (16:41 -0500)
committerstephentoub <stoub@microsoft.com>
Thu, 11 Feb 2016 22:00:19 +0000 (17:00 -0500)
Task.Id is lazily-initialized with an interlocked operation and we strive to avoid accessing it anywhere in Task's internals on hot paths, but apparently when some ETW logging was added around continuations, calls were added that access Task.Id even if logging is disabled.  This is accounting for ~30% of the cycles consumed in a TCS Task's lifecycle!  This commit fixes it by making sure these accesses to Id are guarded.

src/mscorlib/src/System/Threading/Tasks/Task.cs

index 7501fb2..e852798 100644 (file)
@@ -3585,7 +3585,12 @@ namespace System.Threading.Tasks
             // Atomically store the fact that this task is completing.  From this point on, the adding of continuations will
             // result in the continuations being run/launched directly rather than being added to the continuation list.
             object continuationObject = Interlocked.Exchange(ref m_continuationObject, s_taskCompletionSentinel);
-            TplEtwProvider.Log.RunningContinuation(Id, continuationObject);
+            TplEtwProvider etw = TplEtwProvider.Log;
+            bool tplEtwProviderLoggingEnabled = etw.IsEnabled();
+            if (tplEtwProviderLoggingEnabled)
+            {
+                etw.RunningContinuation(Id, continuationObject);
+            }
 
             // If continuationObject == null, then we don't have any continuations to process
             if (continuationObject != null)
@@ -3658,7 +3663,10 @@ namespace System.Threading.Tasks
                     var tc = continuations[i] as StandardTaskContinuation;
                     if (tc != null && (tc.m_options & TaskContinuationOptions.ExecuteSynchronously) == 0)
                     {
-                        TplEtwProvider.Log.RunningContinuationList(Id, i, tc);
+                        if (tplEtwProviderLoggingEnabled)
+                        {
+                            etw.RunningContinuationList(Id, i, tc);
+                        }
                         continuations[i] = null; // so that we can skip this later
                         tc.Run(this, bCanInlineContinuations);
                     }
@@ -3672,7 +3680,10 @@ namespace System.Threading.Tasks
                     object currentContinuation = continuations[i];
                     if (currentContinuation == null) continue;
                     continuations[i] = null; // to enable free'ing up memory earlier
-                    TplEtwProvider.Log.RunningContinuationList(Id, i, currentContinuation);
+                    if (tplEtwProviderLoggingEnabled)
+                    {
+                        etw.RunningContinuationList(Id, i, currentContinuation);
+                    }
 
                     // If the continuation is an Action delegate, it came from an await continuation,
                     // and we should use AwaitTaskContinuation to run it.