Noel
Noel

Reputation: 2120

Parallel exceptions are being caught

somehow my exceptions seem to being caught by method they are executing in. Here is the code to call the method. As you can see I create a cancellation token with a time out. I register a method to call when the cancellation token fires and then I start a new task. The cancellation token appears to be working OK. As does the registered method.

var cancellationToken = new CancellationTokenSource(subscriber.TimeToExpire).Token;
cancellationToken.Register(() =>
{
    subscriber.Abort();
});
var task = Task<bool>.Factory.StartNew(() =>
{                  
 subscriber.RunAsync((T)messagePacket.Body, cancellationToken);
 return true;
})
.ContinueWith(anticedant => 
{
    if (anticedant.IsCanceled)
    {
        Counter.Increment(12);
        Trace.WriteLine("Request was canceled");
    }

    if (anticedant.IsFaulted)
    {
        Counter.Increment(13);
        Trace.WriteLine("Request was canceled");
    }

    if (anticedant.IsCompleted)
    {
        Counter.Increment(14);
    }

The next piece of code is the method that seems to be not only throwing the excetion (expected behavior. but also catching the exception.

public async override Task<bool> ProcessAsync(Message input, CancellationToken cancellationToken)
{
    Random r = new Random();
    Thread.Sleep(r.Next(90, 110));
    cancellationToken.ThrowIfCancellationRequested();
    return await DoSomethingAsync(input);
}

The exception is being thrown by the cancellation token but according to intellitrace it is being caught at the end of the method. I have tried a number of different options including throwing my own exception, but no matter what the continuewith function always executes the IsComleted or ran to completion code. Any ideas on what I am doing wrong? Thanks

Upvotes: 0

Views: 98

Answers (1)

Stephen Cleary
Stephen Cleary

Reputation: 456332

I assume that RunAsync is the same as ProcessAsync.

The exception is being thrown by the cancellation token but according to intellitrace it is being caught at the end of the method.

Yup. Any async method will catch its own exceptions and place them on its returned Task. This is by design.

no matter what the continuewith function always executes the IsComleted or ran to completion code.

Well, let's take another look at the code:

var task = Task<bool>.Factory.StartNew(() =>
{                  
  subscriber.RunAsync((T)messagePacket.Body, cancellationToken);
  return true;
})
.ContinueWith(

Consider the lambda passed to StartNew: it calls RunAsync, it ignores the Task that it returns, and then it returns true (successfully). So the Task returned by StartNew will always return successfully. This is why the ContinueWith always executes for a successfully-completed task.

What you really want is to await the Task returned by RunAsync. So, something like this:

var task = Task.Run(async () =>
{                  
  await subscriber.RunAsync((T)messagePacket.Body, cancellationToken);
  return true;
})
.ContinueWith(

You're still ignoring the return value of RunAsync (the bool it returns is ignored), but you're not ignoring the Task itself (including cancellation/exception information).

P.S. I'm assuming there's a lot of code you're not showing us; using StartNew/Run to just kick off some async work and return a value is very expensive.

P.P.S. Use await Task.Delay instead of Thread.Sleep.

Upvotes: 1

Related Questions