Disable debugger evaluation of ValueTask<T>.Result (#25727)
authorStephen Toub <stoub@microsoft.com>
Tue, 16 Jul 2019 19:28:53 +0000 (15:28 -0400)
committerGitHub <noreply@github.com>
Tue, 16 Jul 2019 19:28:53 +0000 (15:28 -0400)
If the debugger evaluates a `ValueTask<T>`'s `Result`, that counts as the "you should only consume a `ValueTask<T>`" once, and ends up breaking / hanging / throwing exceptions and other bad stuff while stepping through code in the debugger.

This commit addresses that in two ways:
1. Adds `[DebuggerBrowsable(Never)]` to `Result` to prevent it from showing up in debugger views.
2. Adds a NotifyOfCrossThreadDependency call to its ToString.  This prevents the debugger from using ToString to show an implicit representation of the instance, and it forces the developer explicitly trying to access ToString (e.g. in the watch window) to click a button acknowleding the impact.

(Post 3.0, we should consider removing the `ValueTask<T>.ToString()` override altogether.)

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

index 7ffb4bb..285d3bf 100644 (file)
@@ -740,6 +740,7 @@ namespace System.Threading.Tasks
         }
 
         /// <summary>Gets the result.</summary>
+        [DebuggerBrowsable(DebuggerBrowsableState.Never)] // prevent debugger evaluation from invalidating an underling IValueTaskSource<T>
         public TResult Result
         {
             [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -780,6 +781,8 @@ namespace System.Threading.Tasks
         {
             if (IsCompletedSuccessfully)
             {
+                Debugger.NotifyOfCrossThreadDependency(); // prevent debugger evaluation from invalidating an underling IValueTaskSource<T> unless forced
+
                 TResult result = Result;
                 if (result != null)
                 {