2

What would I receive in result variable if completion source was cancelled?

async void SomeMethod()
{
   .....
   Run();
   var result = await GetResult();
   .....
}

Task<SomeResult> GetResult()
{
    return myCompletionSource.Task;
}

TaskCompletionSource myCompletionSource;

void Run()
{
     myCompletionSource= new TaskCompletionSource();
     TriggerSomeLongLastingLogicWhichWillCallCallBackBelow();

}

void SomeCallback()
{
     if (someCondition)
     {
         myCompletionSource.SetResult(<someResult>);
     }
     else
     {
         myCompletionSource.SetCancelled();
     }
}

I'm not quite sure whether this approach is correct.

  1. In other words is it a good practice to rely on task status rather than creating a wrapper for "someresult" with status variable?
  2. How to handle cancelled task? I'm not a fan of callbacks and don't like solution with ContinueWith, where I can analize task status.
Access Denied
  • 8,723
  • 4
  • 42
  • 72

1 Answers1

4

What would I receive in result variable if completion source was cancelled?

You code will throw an OperationCancelledException when awaiting a cancelled task. So the result variable will never be set.

You can handle the exception with a try/catch block:

async Task SomeMethod()
{
   try
   {
       .....
       Run();
       var result = await GetResult();
   }
   catch(OperationCancelledException)
   {
       // handle cancelled operation
   }
}

Also, SomeMethod should return a Task as void returning async methods are usually only appropriate for event handlers as they must return void. I blog about it briefly here.

In general, if you want an operation to be cancelable you pass in a CancellationToken which the operation must check and pass on to other operations it kicks off. So you pass it all the way down the chain and into your callback.

You can also register a callback with the CancellationToken that cancels the TaskCompletionSource when the token is cancelled so you don't need to do it in your method.

void Run()
{   
     var cts = new CancellationTokenSource();
     var myCompletionSource= new TaskCompletionSource();
     cts.Token.Register(() => myCompletionSource.SetCancelled());

     TriggerSomeLongLastingLogicWhichWillCallCallBackBelow(cts.Token);         
}

void SomeCallback(CancellationToken token)
{       
     // do some work
     ....

     token.ThrowIfCancellationRequested();

     if (someCondition)
     {
         myCompletionSource.SetResult(<someResult>);
     }
     else
     {
         myCompletionSource.SetException(new Exception("error occcured"));
     }
}
NeddySpaghetti
  • 13,187
  • 5
  • 32
  • 61
  • 1
    Your blog post is wrong about `async void`. Exceptions from an `async void` method can't be caught by the caller, but won't be unobserved. How such exceptions are treated depends on the type of application, but for instance in an Windows Forms application, they'll be handled the same as any other uncaught exception. They can be useful outside of event handlers, although event handlers are indeed the most common use. The only reason your unobserved task handler runs in the example in your blog is because you forgot to await the result of `Task.Run`. –  Apr 22 '15 at 07:13
  • Thanks for your feedback, I'll review that section. – NeddySpaghetti Apr 22 '15 at 23:19