Passing exception from Task.Run to await properly and Debugger - c#

This is the task, where the exception is being thrown.
public Task<SensorReading> ReadSensorsAsync(int cell)
{
return Task.Run(() => {
throw new ArgumentNullException();
...
This is the async method:
private async void TimerCallback(object state)
{
try
{
var tasksRead = Enumerable.Range(3, 35).Select(i => sensorReader.ReadSensorsAsync(i)).ToList();
await Task.WhenAll(tasksRead);
var r = tasksRead.Select(x => x.Result).ToList();
var taskRecordREsult = RecordReadingAsync(r).Result;
}
catch(Exception e)
{
// expecting to catch everything here
}
}
What I was expecting is that exception would be handled in await part of the code. However, I am getting unhandled exception in Visual Studio debugger.
Question - how to properly pass all kind of exception from Task.Run to awaitable part?

One possible solution is to extract your Task.Run lambda into a standalone method, and add the [DebuggerStepThrough] attribute to it. In theory (I haven't tested this), this will suppress the first exception in the debugger, and allow it to be caught later. It's not pretty, but it should do the trick.

try this program and see if you still get breaking..
its working for me
class Program
{
static void Main(string[] args)
{
new Program().TimerCallback();
}
public Task<string> ReadSensorsAsync(string cell)
{
return Task.Run(() =>
{
if(cell == null) throw new ArgumentNullException();
return cell;
});
}
public Task<string> RecordReadingAsync(IEnumerable<string> cell)
{
return Task.Run(() =>
{
return string.Join(",", cell);
});
}
public async void TimerCallback()
{
try
{
var tasksRead = new string[] { "1", null, "3" }.Select(s => ReadSensorsAsync(s));
var taskRecordResult = await RecordReadingAsync(await Task.WhenAll(tasksRead));
Debugger.Log(1, "test", taskRecordResult);
}
catch (Exception e)
{
//catches here
Debugger.Log(1, "test", e.Message ?? e.ToString());
}
}
}

Related

How to properly catch exceptions in multiple async method calls?

I have some async method:
public async Task<Object> MethodAsync()
{
return await MakeHttpRequestAsync();
}
I need to make multiple calls of this method. One of them can throw an exception. But I need to keep all possible returned Objects from other method calls that succeeded.
Option 1:
Task t1 = MethodAsync();
Task t2 = MethodAsync();
try
{
Task.WaitAll(new Task[]{t1,t2});
}
catch(Exception e)
{
...
}
//are t1 and t2 finished execution here with success or exception?
Option 2:
Task t1 = MethodAsync();
Task t2 = MethodAsync();
try
{
t1.Wait()
}
catch(Exception e)
{
...
}
try
{
t2.Wait()
}
catch(Exception e)
{
...
}
//what happens if t2 throws exception before t1 finished?
Any other options?
edit:
Thought about third option:
public async Task<Tuple<Object, Object>> MultipleAsyncCallsAsync(some arguments)
{
Object result1 = null, result2 = null;
try
{
result1 = await MethodAsync();
}
catch(Exception e)
{
}
try
{
result2 = await MethodAsync();
}
catch (Exception e)
{
}
return Tuple.Create(result1, result2);
}
edit #2:
Looks like third option is not working, but it's working this way:
public async Task<Tuple<object, object>> MultipleAsyncCallsAsync(some arguments)
{
object result1 = null, result2 = null;
Task<object> t1 = null, t2 = null;
t1 = Task.Run(()=> MethodAsync());
t2 = Task.Run(()=> MethodAsync());
try
{
result1 = await t1;
}
catch(Exception e)
{
}
try
{
result2 = await t2;
}
catch (Exception e)
{
}
return Tuple.Create(result1, result2);
}
Put the tasks in an array and inspect their status like this:
var t1 = MethodAsync();
var t2 = MethodAsync();
var tasks = new[] {t1, t2};
try
{
await Task.WhenAll(tasks);
}
catch
{
var failedTasks = tasks.Where(t => t.IsFaulted);
var allExceptions = failedTasks.Select(t => t.Exception?.Flatten());
}
var results = tasks.Where(t => t.IsCompletedSuccessfully).Select(t => t.Result);
What is does is it awaits all tasks and then uses the task status (using IsCompletedSuccessfully ect.) to get the results of all the tasks that did complete succesfully.
You can determine the failed tasks using IsFaulted and get their exception using the tasks Exception property.
If you want to catch/inspect the exception before the call to Task.WhenAll(..) is completed wrap the code inside MethodAsync in a try/catch:
public async Task<Object> MethodAsync()
{
try
{
..
}
catch(Exception ex)
{
//handle or throw
return null;
}
}
Or, if you cannot modify MethodAsync for some reason, you can wrap the method in a try/catch using this:
Func<Task<Object>, Task<Object>> TryExecute = new Func<Task<Object>, Task<object>>(async t =>
{
try
{
return await t;
}
catch (Exception ex)
{
//handle exception
return null;
}
});
and use it like this:
var t1 = TryExecute(MethodAsync());
var t2 = TryExecute(MethodAsyncF());
var tasks = new[] {t1, t2};
await Task.WhenAll(tasks);
Whenever there's a question about this kind of procedure ("call n methods, (a)wait for them all to complete, and then retrieve the results for each method and do something with them"), a better solution is almost always to create a higher-order method.
For example, if you want to do some kind of logging or something but otherwise ignore exceptions, then you can create a higher-order method like this:
public async Task<T> LogAndIgnoreExceptions(Task<T> task) where T : class
{
try
{
return await task;
}
catch (Exception ex)
{
Log(ex);
return null;
}
}
Then you can use Task.WhenAll in a very natural way:
var results = await Task.WhenAll(
LogAndIgnoreExceptions(MethodAsync()),
LogAndIgnoreExceptions(MethodAsync())
);
if (results[0] is not null)
{
T result = results[0];
}
else
{
// First call had an error.
}
Or, if you want to preserve the exception details and results, what you want is a little helper type like Try to enable railway programming:
var results = await Task.WhenAll(
Try.Create(MethodAsync()),
Try.Create(MethodAsync())
);
if (results[0].IsValue)
{
T result = results[0].Value;
}
else
{
Exception exception = results[0].Exception;
}

AggregateException Not Thrown in Task.Run()

I am trying to catch an AggregateException from a Task.Run() operation which intentionally fails, however an AggregateException is not thrown. Why?
public void EnterWaitingRoom(string val)
{
try
{
Task.Run(() => InvokeHubMethod("HubMethod",
new object[] { val }));
}
catch (AggregateException ex)
{
// This exception is not caught
throw ex;
}
}
private async Task<object> InvokeHubMethod(string method, object[] args)
{
return await Task.Run(() => _hubProxy.Invoke<object>(method,
args).Result);
}
I expect the exception to be thrown but it is not.
I also tried adding .Wait and still don't get the exception.
The request is coming from a windows UI.
Any ideas. Thanks.
This is how you enter the async/await from an EventHandler
public async void Button_Click(object sender, RoutedEventArgs args )
{
object result = await EnterWaitingRoom( "whatever" );
}
private Task<object> EnterWaitingRoom(string val)
{
return InvokeHubMethod(
"HubMethod",
new object[] { val } );
}
private Task<object> InvokeHubMethod(string method, object[] args)
{
return _hubProxy.Invoke<object>(
method,
args);
}

Exception not fired when using TPL

I have the following code which does not fire the AggregateException
Aggregate Exception is not fired and I don't understand why? Normally it should as Aggregate exception is used to catch exceptions while running code using tasks
class Program
{
static void Main(string[] args)
{
var task1 = Task.Factory.StartNew(() =>
{
Test();
}).ContinueWith((previousTask) =>
{
Test2();
});
try
{
task1.Wait();
}
catch (AggregateException ae)
{
foreach (var e in ae.InnerExceptions)
{
// Handle the custom exception.
if (e is CustomException)
{
Console.WriteLine(e.Message);
}
// Rethrow any other exception.
else
{
throw;
}
}
}
}
static void Test()
{
throw new CustomException("This exception is expected!");
}
static void Test2()
{
Console.WriteLine("Test2");
}
}
public class CustomException : Exception
{
public CustomException(String message) : base(message)
{ }
}
}
That's because you are waiting for completion of your continuation task (which runs Test2()), not for completion of task which runs Test(). First task fails with exception and then continuation task does nothing with this exception (you don't check if previousTask has failed) and completes successfully. To catch that exception you need to wait for first task or check result of it in continuation:
var task1 = Task.Factory.StartNew(() =>
{
Test();
});
var task2 = task1.ContinueWith((previousTask) =>
{
Test2();
});
or
var task1 = Task.Factory.StartNew(() =>
{
Test();
}).ContinueWith((previousTask) =>
{
if (previousTask.Exception != null) {
// do something with it
throw previousTask.Exception.GetBaseException();
}
Test2();
}); // note that task1 here is `ContinueWith` task, not first task
That's all not related of course to whether you should really do it like this or not, just to answer the question.

Async await call does not return

I have a problem, where i call an async method, and the call does not return back. I assume its a race of threads. How do I write this correctly?
This is where it starts. I first call an async method called "GetCachedValuesAsync"
public void OnNavigatingTo(NavigationParameters parameters)
{
Task.Run(async () =>
{
await GetCachedValuesAsync();
ClipRefernce = GenerateRefernce(clips);
});
}
Here is the method signature for GetCachedValueAsync
public async Task GetCachedValuesAsync()
{
try
{
clips = await BlobCache.LocalMachine.GetObject<List<Clip>>("clips");
}
catch (KeyNotFoundException ex)
{
clips = new List<Clip>();
}
}
I do not get the call returned from BlobCache, BlobCahce method is part of a library called akavache.
The code also does not hit: ClipRefernce = GenerateRefernce(clips);
I appreciate your help
Edit 1
This is GenerateRefernce method.
public string GenerateRefernce(List<Clip> clips)
{
string refernce = "";
if(clips.Count > 0)
{
var clip = clips.LastOrDefault();
refernce = String.Format("Ref {0:yyyy}/{1}",DateTime.Now , clip.ClipId + 1);
}
else{
refernce = String.Format("Ref {0:yyyy}/{1}", DateTime.Now, 1);
}
return refernce;
}
You need to remove the sync method from the Task.Run like this:
public void OnNavigatingTo(NavigationParameters parameters)
{
Task.Run(async () =>
{
await GetCachedValuesAsync();
});
ClipRefernce = GenerateRefernce(clips);
}
public async Task GetCachedValuesAsync()
{
try
{
clips = await BlobCache.LocalMachine.GetObject<List<Clip>>("clips");
}
catch (KeyNotFoundException ex)
{
clips = new List<Clip>();
}
}

Try-Catch Async Exceptions

This example "fails":
static async void Main(string[] args)
{
try
{
await TaskEx.Run(() => { throw new Exception("failure"); });
}
catch (Exception)
{
throw new Exception("success");
}
}
That is, the exception with the text "failure" bubbles up.
Then I tried this workaround:
static async void Main(string[] args)
{
try
{
await SafeRun(() => { throw new Exception("failure"); });
}
catch (Exception)
{
throw new Exception("success");
}
}
static async Task SafeRun(Action action)
{
var ex = default(Exception);
await TaskEx.Run(() =>
{
try
{
action();
}
catch (Exception _)
{
ex = _;
}
});
if (ex != default(Exception))
throw ex;
}
That didn't help either.
I suppose my Async CTP refresh installation could be hosed.
Should this code work as I expect ("success" bubbles up, not "failure"), or is this not "supposed" to work that way. And if not, how would you work around it?
The behavior you are seeing is likely an edge case bug or may even be correct, if unintuitive. Normally when you invoke an async method synchronously, it wraps a task around to execute and since there is no one waiting on the task to finish, the exception never makes it to the main thread. If you were to call Main directly it would succeed, but then your runtime would see an exception of "success" on another thread.
Since main is the entrypoint of your application, it is invoked synchronously and likely as the entrypoint doesn't trigger the Task wrapping behavior, so that await isn't run properly and the TaskEx.Run throws on its own thread, which shows up in the runtime as an exception being thrown on another thread.
If you were to run main as an async method, i.e. returning a Task (since an async that returns void can only really be called via await) and blocking on it from your synchronous main context, you would get the appropriate behavior as the below test illustrates:
static async Task Main() {
try {
await TaskEx.Run(() => { throw new Exception("failure"); });
} catch(Exception) {
throw new Exception("success");
}
}
static async Task Main2() {
await Main();
}
[Test]
public void CallViaAwait() {
var t = Main2();
try {
t.Wait();
Assert.Fail("didn't throw");
} catch(AggregateException e) {
Assert.AreEqual("success",e.InnerException.Message);
}
}
[Test]
public void CallDirectly() {
var t = Main();
try {
t.Wait();
Assert.Fail("didn't throw");
} catch(AggregateException e) {
Assert.AreEqual("success", e.InnerException.Message);
}
}
I.e. the Task faults with an AggregateException which contains the success exception as it's inner exception.

Categories