Strange try/catch block behavior - c#

I am working with Github API via Octokit.net. Now I am writing code that responsible for TwoFactorAuth. When I send request for token, if it is 2FA account, than I must get "TwoFactorRequiredException". I am trying to catch it, but insteand of it I get "AggregateException". But when I look in InnerExceptions of Agg..Exc.. then I have only one, and it is "TwoFactorRequiredException".
Here is my code:
try
{
return client.Authorization.Create(newAuthorization).Result.Token;
}
catch ( Octokit.TwoFactorRequiredException )
{
_navigationService.NavigateAsync($"{nameof(TwoFactorAuthPage)}", animated: false);
}
//catch ( AggregateException ex )
//{
// foreach ( var exception in ex.InnerExceptions )
// {
// Debug.WriteLine(exception.GetType());
// }
//}
When it is commented, then I have AggregateExceptions, when it is uncommented, then I have AggExc and inside it I have TwoFactor..Exception.

That's because client.Authorization.Create(newAuthorization) returns a Task<T>, where T is your result type. Inside a Task, multiple exceptions can occur, that is why the Task Parallel Library returns an AggregateException capturing all the exceptions.
Instead of synchronously blocking on an async method, you should await on it, which asynchronously waits for it's completion. You gain two things from this. The first is that you don't synchronously block and can free the thread, and the second is that await unwraps the exception for you:
public async Task<Token> CallSomethingAsync()
{
try
{
var result = await client.Authorization.Create(newAuthorization);
result.Token;
}
catch ( Octokit.TwoFactorRequiredException )
{
_navigationService.NavigateAsync($"{nameof(TwoFactorAuthPage)}", animated: false);
}
}
Although I'm not sure what NavigateAsync does, I think you may also want to await on it's completion as well.

Related

Task WhenAll exception

I have to execute about 10 tasks and then obtain their results (all different). I have created a wrapper to catch the exceptions, I don't want to know which one failed, but if a failure it should not continue. I also use WhenAll for this, but the problem is that then I get the results of each one and I get an unhandled exception, is there no way not to continue if there are any exceptions from my wrapper?
var 1Task = api.1TaskAsync();
var 2Task = api.2TaskAsync();
var 3Task = api.3TaskAsync();
await taskService.RunSafeAsync(() => Task.WhenAll(1Task, 2Task, 3Task));
var result = await 1Task; <-------------exception
TaskService:
public async Task RunSafeAsync(Func<Task> task)
{
try
{
await task();
}
catch (Exception ex)
{
}
}
You can't retrieve the result from a function that has faulted. It doesn't have a result-- because it never finished. Attempting to retrieve its result (as you are) is going to throw an exception, because there is nothing to assign to the variable that is supposed to accept the return value.
To avoid this, check if the task is faulted before attempting to retrieve the result.
var 1Task = api.1TaskAsync();
var 2Task = api.2TaskAsync();
var 3Task = api.3TaskAsync();
await taskService.RunSafeAsync(() => Task.WhenAll(1Task, 2Task, 3Task));
if (!1Task.IsFaulted)
{
var result = await 1Task;
If you just want to not continue if there was an exception in any of the methods then don't wrap WhenAll and react if it throws.
try
{
await Task.WhenAll(...);
// None of the tasks threw an exception;
}
catch (Exception ex)
{
// One or more tasks threw an exception
return / throw / etc.
}
//Continue the happy path
Cancellation token
If you need to attempt tasks simultaneously and abort all tasks if one fails than you'll need to look at the cancellation token and activate the token if any exceptions occur (not in the catch for whole WhenAll, but each tasks would have to be executed in a try..catch).
Maybe sequentially?
BTW. With .WhenAll all tasks will be attempted. Maybe you actually want to execute the tasks in sequence and continue only if there are not exceptions:
try
{
await F1();
await F2();
}
catch(Exception ex)
{
}
This doesn't seem slick but it makes it very clear that task 2 will not run unless task 1 is a success.

async/wait: bubbleup exceptions?

This pertains to a program that has to collect some info from a server on the web.
Question: How can I get the exceptions from GetServerResponseAsync and Check to bubble up through CheckAsync to the Main program?
As depicted here, they do not. ErrorHandler never gets hit.
My Main program:
....
try
{
Task.Run(() => CheckAsync());
// bubble to here?
ReadConfiguration();
}
catch (Exception ex)
{
// gather and handle all exceptions here
ErrorHandler.NotifyMe(new[] { "some message" }, ErrorType.Stop); // never gets hit
}
public async Task CheckAsync()
{
await GetServerResponseAsync("slm_check"); // may throw exception
...
if (.....)
throw new Exception("...");
...
}
public async Task GetServerResponseAsync(string command)
{
...
// client = HttpClient()
using (apacheResponse = await client.GetAsync(ServerUrl + "...."))
{
if (....)
throw new Exception("Internal web server error", new Exception("Maybe MySQL server is down"));
using (HttpContent content = apacheResponse.Content)
{
if ( ....)
throw new Exception("error message");
}
}
}
How can I get the exceptions from GetServerResponseAsync and Check to bubble up through CheckAsync to the Main program?
Use await to consume your tasks, instead of ignoring them.
Specifically, this line:
Task.Run(() => CheckAsync());
is getting a Task back from the Task.Run method, which is then ignored. Instead of ignoring that task, the code should be awaiting it:
await Task.Run(() => CheckAsync());
As other commenters have pointed out, the Task.Run here doesn't really make sense. If your operation is asynchronous, it shouldn't need to also run on a background thread. Usually. :) So if you take out the Task.Run, your code would look like:
await CheckAsync();
which will properly propagate the exception.

Async Await Equivalent

When I run this code everything works fine:
public async void InvokePlugin(MyObject xTask)
{
try
{
var hndlr = new TimeoutHandler(RunTask);
var asyncResult = hndlr.BeginInvoke(xTask, null, new object());
if (!asyncResult.AsyncWaitHandle.WaitOne(xTask.Timeout, false))
{
throw new TimeoutException("Plugin didn't complete processing in a timely manner.");
}
hndlr.EndInvoke(asyncResult);
}
catch (Exception ex)
{
//Handle Exceptions
}
}
private delegate void TimeoutHandler(MyObject xTask);
I want to update this code to use Async/Await. I tried doing it like this:
public async void InvokePlugin(MyObject xTask)
{
try
{
var runTask = Task.Run(() => { RunTask(xTask); });
if (await Task.WhenAny(runTask, Task.Delay(xTask.Timeout)) == runTask)
{
// Task completed within timeout.
// Consider that the task may have faulted or been canceled.
// We re-await the task so that any exceptions/cancellation is rethrown.
await runTask;
}
else
{
throw new TimeoutException("Plugin didn't complete processing in a timely manner.");
}
}
catch (Exception ex)
{
//Handle Exceptions
}
}
...but it's not working. Clearly I'm doing somethign wring. It does call the RunTask Method and executes the first 2 lines fine but then it just ends and I can't seem to catch the exception in either the TaskRun method or code above. All I see in the Output windows is "Program has exited with code 0 (0x0)."
If the experts out there can either point me to what I'm doing wrong or give me suggestions as to how I can catch the exception and handle it I would be very grateful.
Also if you feel I missed any important details please ask and I will update my question.
Usually I'd say if it works don't fix it but in this case I'm trying to rearchitect a bit to allow for some enhancements so here I am.
Change async void to async Task. See my article on async best practices for more information.
After you do this, consume it asynchronously:
await the task, allowing async to grow.
async and await will naturally grow upward through your code base, until they reach Main, which cannot be async.
In your Main method, call GetAwaiter().GetResult() on the "top" task.
Blocking on asynchronous code is generally not a good idea, but blocking on a single task in a Console app's Main method is an exception to that rule.

Trying to understand Task.ContinueWith()

I'm trying to understand some (in my eyes) weird behaviour. I have a call to some async method and want to retrieve its result. (DeleteIndexAsync return a Task<bool>)
var deleteTask = Task.Run(() => DeleteIndexAsync(localItem))
.ContinueWith(t =>
{
//handle and log exceptions
}, TaskContinuationOptions.OnlyOnFaulted);
if (!deleteTask.Result)
In this scenario Result is false and the Status is WaitingForActivation.
Whereas this code does what I want
var deleteTask = Task.Run(() => DeleteIndexAsync(localItem));
deleteTask.ContinueWith(t =>
{
//handle and log exceptions
return false;
}, TaskContinuationOptions.OnlyOnFaulted);
if (!deleteTask.Result)
Can someone explain why? And is it possible to use async / await instead of Task here?
Edit:
var deleteTask = Task.Run(() => ThrowEx());
bool errorOccurred = false;
deleteTask.ContinueWith(t =>
{
errorOccurred = true;
}, TaskContinuationOptions.OnlyOnFaulted);
if (errorOccurred)
{
return true;
}
If you chain the calls, like in the first example, the value you are assigning to the deleteTask variable is actually the second Task. This is the one that is supposed to run only on failure of the first task (the one calling DeleteIndexAsync).
This is because both Task.Run and Task.ContinueWith return Tasks that they create. It explains why in the first example you get Status == WaitingForActivation. In the first snippet, accessing deleteTask.Result would cause an exception to be thrown. In case DeleteIndexAsync threw, it would be an AggregateException containing original exception (unless you accessed t.Exception), otherwise it would be stating that the "Operation was cancelled" - this is because you try to get the result of the task that was scheduled conditionally and the condition was not met.
If you made method containing the snipped async you could do it like this (not tested):
bool success = false;
try
{
success = await DeleteIndexAsync(localItem);
}
catch (Exception) {}
if (!success)
{
//TODO: handler
}
Regarding question edit:
Using captured variable should help, but your current solution introduces race condition. In this case it would be better to do like this:
var deleteTask = Task.Run(() => ThrowEx());
try
{
deleteTask.Wait();
}
catch (Exception ex)
{
return true;
}
but at this point you can drop the asynchronous call entirely, because you wait for the result immediately - unless this example simplifies work that can be done between Run and Wait)
I have a call to some async method and want to retrieve its result.
The best way to do this is with await, not Result.
Can someone explain why?
In the first example, deleteTask is the task returned from Task.Run. In the second example, deleteTask is the task returned from ContinueWith. This task never actually executes unless DeleteIndexAsync throws an exception.
And is it possible to use async / await instead of Task here?
Yes, that's the best approach. When working with asynchronous code, you should always use await instead of ContinueWith. In this case, the TaskContinuationOptions.OnlyOnFaulted means you should put the continuation code in a catch block after the await:
bool deleteResult;
try
{
deleteResult = await Task.Run(() => DeleteIndexAsync(localItem));
}
catch (Exception ex)
{
//handle and log exceptions
}
if (!deleteResult)
...
Or, since it looks like DeleteIndexAsync is asynchronous, removing Task.Run would be more appropriate:
bool deleteResult;
try
{
deleteResult = await DeleteIndexAsync(localItem);
}
catch (Exception ex)
{
//handle and log exceptions
}
if (!deleteResult)
...

How do you catch CancellationToken.Register callback exceptions?

I am using async I/O to communicate with an HID device, and I would like to throw a catchable exception when there is a timeout. I've got the following read method:
public async Task<int> Read( byte[] buffer, int? size=null )
{
size = size ?? buffer.Length;
using( var cts = new CancellationTokenSource() )
{
cts.CancelAfter( 1000 );
cts.Token.Register( () => { throw new TimeoutException( "read timeout" ); }, true );
try
{
var t = stream.ReadAsync( buffer, 0, size.Value, cts.Token );
await t;
return t.Result;
}
catch( Exception ex )
{
Debug.WriteLine( "exception" );
return 0;
}
}
}
The exception thrown from the Token's callback is not caught by any try/catch blocks and I'm not sure why. I assumed it would be thrown at the await, but it is not. Is there a way to catch this exception (or make it catchable by the caller of Read())?
EDIT:
So I re-read the doc at msdn, and it says "Any exception the delegate generates will be propagated out of this method call."
I'm not sure what it means by "propagated out of this method call", because even if I move the .Register() call into the try block the exception is still not caught.
I personally prefer to wrap the Cancellation logic into it's own method.
For example, given an extension method like:
public static async Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs))
{
if (task != await Task.WhenAny(task, tcs.Task))
{
throw new OperationCanceledException(cancellationToken);
}
}
return task.Result;
}
You can simplify your method down to:
public async Task<int> Read( byte[] buffer, int? size=null )
{
size = size ?? buffer.Length;
using( var cts = new CancellationTokenSource() )
{
cts.CancelAfter( 1000 );
try
{
return await stream.ReadAsync( buffer, 0, size.Value, cts.Token ).WithCancellation(cts.Token);
}
catch( OperationCanceledException cancel )
{
Debug.WriteLine( "cancelled" );
return 0;
}
catch( Exception ex )
{
Debug.WriteLine( "exception" );
return 0;
}
}
}
In this case, since your only goal is to perform a timeout, you can make this even simpler:
public static async Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeout)
{
if (task != await Task.WhenAny(task, Task.Delay(timeout)))
{
throw new TimeoutException();
}
return task.Result; // Task is guaranteed completed (WhenAny), so this won't block
}
Then your method can be:
public async Task<int> Read( byte[] buffer, int? size=null )
{
size = size ?? buffer.Length;
try
{
return await stream.ReadAsync( buffer, 0, size.Value, cts.Token ).TimeoutAfter(TimeSpan.FromSeconds(1));
}
catch( TimeoutException timeout )
{
Debug.WriteLine( "Timed out" );
return 0;
}
catch( Exception ex )
{
Debug.WriteLine( "exception" );
return 0;
}
}
EDIT: So I re-read the doc at msdn, and it says "Any exception the
delegate generates will be propagated out of this method call."
I'm not sure what it means by "propagated out of this method call",
because even if I move the .Register() call into the try block the
exception is still not caught.
What this means is that the caller of your cancellation callback (the code inside .NET Runtime) won't make an attempt to catch any exceptions you may throw there, so they will be propagated outside your callback, on whatever stack frame and synchronization context the callback was invoked on. This may crash the application, so you really should handle all non-fatal exceptions inside your callback. Think of it as of an event handler. After all, there may be multiple callbacks registered with ct.Register(), and each might throw. Which exception should have been propagated then?
So, such exception will not be captured and propagated into the "client" side of the token (i.e., to the code which calls CancellationToken.ThrowIfCancellationRequested).
Here's an alternative approach to throw TimeoutException, if you need to differentiate between user cancellation (e.g., a "Stop" button) and a timeout:
public async Task<int> Read( byte[] buffer, int? size=null,
CancellationToken userToken)
{
size = size ?? buffer.Length;
using( var cts = CancellationTokenSource.CreateLinkedTokenSource(userToken))
{
cts.CancelAfter( 1000 );
try
{
var t = stream.ReadAsync( buffer, 0, size.Value, cts.Token );
try
{
await t;
}
catch (OperationCanceledException ex)
{
if (ex.CancellationToken == cts.Token)
throw new TimeoutException("read timeout", ex);
throw;
}
return t.Result;
}
catch( Exception ex )
{
Debug.WriteLine( "exception" );
return 0;
}
}
}
Exception handling for callbacks registered with CancellationToken.Register() is complex. :-)
Token Cancelled Before Callback Registration
If the cancellation token is cancelled before the cancellation callback is registered, the callback will be executed synchronously by CancellationToken.Register(). If the callback raises an exception, that exception will be propagated from Register() and so can be caught using a try...catch around it.
This propagation is what the statement you quoted refers to. For context, here's the full paragraph that that quote comes from.
If this token is already in the canceled state, the delegate will be
run immediately and synchronously. Any exception the delegate
generates will be propagated out of this method call.
"This method call" refers to the call to CancellationToken.Register(). (Don't feel bad about being confused by this paragraph. When I first read it a while back, I was puzzled, too.)
Token Cancelled After Callback Registration
Cancelled by Calling CancellationTokenSource.Cancel()
When the token is cancelled by calling this method, cancellation callbacks are executed synchronously by it. Depending on the overload of Cancel() that's used, either:
All cancellation callbacks will be run. Any exceptions raised will be combined into an AggregateException that is propagated out of Cancel().
All cancellation callbacks will be run unless and until one throws an exception. If a callback throws an exception, that exception will be propagated out of Cancel() (not wrapped in an AggregateException) and any unexecuted cancellation callbacks will be skipped.
In either case, like CancellationToken.Register(), a normal try...catch can be used to catch the exception.
Cancelled by CancellationTokenSource.CancelAfter()
This method starts a countdown timer then returns. When the timer reaches zero, the timer causes the cancellation process to run in the background.
Since CancelAfter() doesn't actually run the cancellation process, cancellation callback exceptions aren't propagated out of it. If you want to observer them, you'll need to revert to using some means of intercepting unhandled exceptions.
In your situation, since you're using CancelAfter(), intercepting the unhandled exception is your only option. try...catch won't work.
Recommendation
To avoid these complexities, when possible don't allow cancellation callbacks to throw exceptions.
Further Reading
CancellationTokenSource.Cancel() - talks about how cancellation callback exceptions are handled
Understanding Cancellation Callbacks - a blog post I recently wrote on this topic

Categories

Resources