Add TaskCompletionSource.SetCanceled(CancellationToken) (#32696)
authorFraser Waters <frassle@gmail.com>
Tue, 25 Feb 2020 14:48:29 +0000 (14:48 +0000)
committerGitHub <noreply@github.com>
Tue, 25 Feb 2020 14:48:29 +0000 (09:48 -0500)
* Add TaskCompletionSource.SetCanceled(CancellationToken)

api-approved by #30862

* Add to ref

* SetCanceled(default)

* Change some tests to use SetCaneled not TrySetCanceled

These tests used SetResult/SetException and TrySetCancelled so it could
pass a token. Changed to use SetCancelled to match the Result/Exception
useage.

* Add SetCanceled(CT) test

* Check exception on re-cancel

* Equal not Equals

* Catch aggregate not TaskCancelled directly

*  Inner not exc

* Markup

* s/m/n

src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskCompletionSource.cs
src/libraries/System.Runtime/ref/System.Runtime.cs
src/libraries/System.Threading.Tasks/tests/Task/TaskRtTests_Core.cs
src/libraries/System.Threading.Tasks/tests/UnwrapTests.cs

index 28773c7..911c0c9 100644 (file)
@@ -334,7 +334,29 @@ namespace System.Threading.Tasks
         /// <exception cref="System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception>
         public void SetCanceled()
         {
-            if (!TrySetCanceled())
+            SetCanceled(default);
+        }
+
+        /// <summary>
+        /// Transitions the underlying
+        /// <see cref="System.Threading.Tasks.Task{TResult}"/> into the
+        /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>
+        /// state, and enables a token to be stored into the canceled
+        /// <see cref="System.Threading.Tasks.Task{TResult}"/>.
+        /// </summary>
+        /// <param name="cancellationToken">The cancellation token to bind to this <see
+        /// cref="System.Threading.Tasks.Task{TResult}"/>.</param>
+        /// <exception cref="System.InvalidOperationException">
+        /// The underlying <see cref="System.Threading.Tasks.Task{TResult}"/> is already in one
+        /// of the three final states:
+        /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
+        /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
+        /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
+        /// </exception>
+        /// <exception cref="System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception>
+        public void SetCanceled(CancellationToken cancellationToken)
+        {
+            if (!TrySetCanceled(cancellationToken))
                 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted);
         }
     }
index ee19039..58a38eb 100644 (file)
@@ -10630,6 +10630,7 @@ namespace System.Threading.Tasks
         public TaskCompletionSource(System.Threading.Tasks.TaskCreationOptions creationOptions) { }
         public System.Threading.Tasks.Task<TResult> Task { get { throw null; } }
         public void SetCanceled() { }
+        public void SetCanceled(System.Threading.CancellationToken cancellationToken) { }
         public void SetException(System.Collections.Generic.IEnumerable<System.Exception> exceptions) { }
         public void SetException(System.Exception exception) { }
         public void SetResult(TResult result) { }
index 925f6cf..084d14e 100644 (file)
@@ -391,6 +391,24 @@ namespace System.Threading.Tasks.Tests
         }
 
         [Fact]
+        public static void RunTaskCompletionSourceTests_SetCanceled()
+        {
+            CancellationTokenSource cts = new CancellationTokenSource();
+            TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
+
+            tcs.SetCanceled(cts.Token);
+
+            Assert.Equal(TaskStatus.Canceled, tcs.Task.Status);
+
+            var ae = Assert.Throws<AggregateException>(() => tcs.Task.Result);
+            var tce = Assert.IsType<TaskCanceledException>(ae.InnerException);
+            Assert.Equal(cts.Token, tce.CancellationToken);
+
+            // Try to cancel again
+            Assert.Throws<InvalidOperationException>(() => tcs.SetCanceled(cts.Token));
+        }
+
+        [Fact]
         public static void RunTaskCompletionSourceTests_CancellationTests()
         {
             // Test that cancellation is persistent
index dcbe93e..756e1e3 100644 (file)
@@ -215,7 +215,7 @@ namespace System.Threading.Tasks.Tests
                     innerTcs.SetException(new InvalidOperationException());
                     break;
                 case TaskStatus.Canceled:
-                    innerTcs.TrySetCanceled(CreateCanceledToken());
+                    innerTcs.SetCanceled(CreateCanceledToken());
                     break;
             }
 
@@ -266,7 +266,7 @@ namespace System.Threading.Tasks.Tests
                     innerTcs.SetException(new InvalidOperationException());
                     break;
                 case TaskStatus.Canceled:
-                    innerTcs.TrySetCanceled(CreateCanceledToken());
+                    innerTcs.SetCanceled(CreateCanceledToken());
                     break;
             }
 
@@ -307,7 +307,7 @@ namespace System.Threading.Tasks.Tests
                     outerTcs.SetResult(null);
                     break;
                 case TaskStatus.Canceled:
-                    outerTcs.TrySetCanceled(CreateCanceledToken());
+                    outerTcs.SetCanceled(CreateCanceledToken());
                     break;
                 case TaskStatus.Faulted:
                     outerTcs.SetException(new InvalidCastException());
@@ -358,7 +358,7 @@ namespace System.Threading.Tasks.Tests
                     outerTcs.SetResult(null); // cancellation
                     break;
                 case TaskStatus.Canceled:
-                    outerTcs.TrySetCanceled(CreateCanceledToken());
+                    outerTcs.SetCanceled(CreateCanceledToken());
                     break;
                 case TaskStatus.Faulted:
                     outerTcs.SetException(new InvalidCastException());