3

I'm fairly new to C# and started playing around with the TPL today. I decided to write a modified version of Task Task.WhenAll as an exercise. I'd like for it to have the following behavior:

  • Upon finding the first task that has faulted or been canceled, cancel the rest of the tasks instead of waiting for them to finish.
  • If the task faulted, the returned task should have the right exception set (i.e no swallowing by continuation and replacing with OperationCancelledException())
  • No async in the method signature (want to avoid bubbling it up).

I came up with the following crazy/stupid piece of code that doesn't work and I am having a hard time visualizing what's going on. I can't imagine any blocking going on and what i envisioned happening was a chain of tasks each waiting on the rest for completion. Could someone explain what's going on?

I wouldn't have it in production code and this is just to test my fundamentals. I realize an easier way of doing this would be to do a Task.WhenAll and have the tasks in the list themselves have continuations that do the cancellation on failure.

    public static Task WhenAllError(List<Task> tasks, CancellationToken ct)
    {
        var tcs = new TaskCompletionSource<object>();
        return Task.WhenAny(tasks).ContinueWith<Task>((t) =>
             {
                 if (tasks.Count == 0)
                 {
                     tcs.SetResult(null);
                     return tcs.Task;
                 }

                 if (t.IsFaulted)
                 {
                     Console.WriteLine("Task faulted. Cancelling other tasks: {0}", t.Id);
                     cts.Cancel();
                     // Make sure the tasks are cancelled if necessary
                     tcs.SetException(t.Exception);
                     return tcs.Task;
                 }
                 // Similarly handle Cancelled

                 tasks.Remove(t);
                 return WhenAllError(tasks, ct);
             }).Unwrap();
    }
svick
  • 236,525
  • 50
  • 385
  • 514
Panther
  • 95
  • 1
  • 7
  • Related: [How to properly cancel Task.WhenAll and throw the first exception?](https://stackoverflow.com/questions/42378265/how-to-properly-cancel-task-whenall-and-throw-the-first-exception) – Theodor Zoulias Mar 31 '22 at 18:25

1 Answers1

2

The CancellationToken class has no Cancel method. You need a CancellationTokenSource to be able to cancel the CancellationToken.

Similarly to affect the outcome of a task you need a TaskCompletionSource you can't safely cancel already running tasks. See this post

Community
  • 1
  • 1
NeddySpaghetti
  • 13,187
  • 5
  • 32
  • 61
  • Yes, I knew that :). Hence the name cts/ct. I didn't include that part of the code. Also you can't abort already tasks, but you can certainly cancel and wait for the tasks to be Canceled (hence this comment: "// Make sure the tasks are cancelled if necessary". – Panther Mar 14 '14 at 12:26
  • I was looking for an explanation as to how the tasks themselves are being scheduled, run, waited upon etc? Is there blocking? The user has a reference to a proxy task (Unwrap) and assume he waits on it, what happens to the resultant Tasks that'll be created? Do we have to wait on all of them as well? Or Will the last task's completion be propagated across tasks to the outermost task the user is waiting on? – Panther Mar 14 '14 at 12:30