Today I read a lot about async/await and it completely blew my mind.
I can't understand why the following test passed.
[Test]
public void Test()
{
var listener = new AsyncHttpListener();
listener.ListeningAsync();
try
{
new WebClient().DownloadString("http://localhost:8080/");
}
catch (Exception)
{
}
listener.Close();
}
public class AsyncHttpListener
{
private readonly HttpListener listener;
public AsyncHttpListener()
{
listener = new HttpListener();
listener.Prefixes.Add("http://localhost:8080/");
listener.Start();
}
public void Close()
{
listener.Close();
}
public async void ListeningAsync()
{
var context = await listener.GetContextAsync();
HandleContext(context);
}
private void HandleContext(HttpListenerContext context)
{
throw new Exception("test excpetion");
}
}
Test passed, but output contains:
System.Exception
test excpetion
at AsyncHttpListenerTest.AsyncHttpListener.HandleContext(HttpListenerContext context) in AsyncHttpListener.cs: line 30
at AsyncHttpListenerTest.AsyncHttpListener.d__0.MoveNext() in AsyncHttpListener.cs: line 25
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.b__1(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
I expect that exception will be transmitted from task thread (HandleContext() method) to caller context and test fail. How can i get this behavior?
Make your method async Task instead of async void, and make your test method async Task instead of void:
public async Task ListeningAsync()
{
var context = await listener.GetContextAsync();
HandleContext(context);
}
[Test]
public async Task Test()
{
var listener = new AsyncHttpListener();
await listener.ListeningAsync();
try
{
new WebClient().DownloadString("http://localhost:8080/");
}
catch (Exception)
{
}
listener.Close();
}
There are several good reasons to avoid async void. Error handling is one of them. Errors raised from async void methods go straight to the SynchronizationContext that was current when the method started.
The reason your test passed is because async methods may return to their caller before they complete. The test runner sees the test method return (without throwing an exception yet), and marks it as "passed". If you return Task from your test method, then the test runner knows to wait for the Task to complete before considering the test complete.
Related
How do you wait until a function with a task inside is done before continuing?
public void A()
{
Debug.Log("before")
CopyInfoFromDB();
Debug.Log("after")
}
public void CopyInfoFromDB()
{
FirebaseDatabase.DefaultInstance.GetReference(path)
.GetValueAsync().ContinueWith(task =>
{
if (task.IsFaulted)
{
Debug.Log("failed");
}
name = ...// loading local varibles from Task.result
});
}
I want it to wait for CopyInfoFromDB to be completed before printing "after". How should I write function A differently?
If you are going to use async-await - be prepared to make all methods involved in pipeline to be asynchronous as well
public async Task A()
{
Debug.Log("before")
await CopyInfoFromDB();
Debug.Log("after")
}
public Task CopyInfoFromDB()
{
return FirebaseDatabase.DefaultInstance
.GetReference(path)
.GetValueAsync();
}
In case GetValueAsync fails, exception will be thrown on the line where you are awaiting for it.
Looking for help after searching has not produced a good suggestion.
I always avoid having async void methods in code. I don't use event handlers. Sometimes a vendor or library gives you no choice, and their methods are implemented as async void.
If my method itself returns Task, but i have no choice but to call a 3rd party library method with async void, is there a way to safely wrap their method in such a way that I can keep my code free of the async void dangers, as listed here about terminating my process?
StackOverflow - why is async void bad
An example of my concern is as follows:
3rd party library method looks like this
public async void GetSomethingFromAService()
{
/// their implementation, and somewhere along the way it throws an exception, in this async void method --- yuck for me
}
My method say on a service controller:
public async Task<bool> MyMethod()
{
await ThirdPartyLibrary.GetSomethingFromAService();
return await Task.FromResult(true);
}
My method is fine except the 3rd party library is async void and throws an exception. My app is going to die. I don't want it to because my code is well written an not async void. But I can't control their code. Can i wrap the call to their async void method in such a way to protect my code from dying?
It's tricky and it might not work for all scenarios, but it may be possible to track the life-time of an async void method, by starting its execution on a custom synchronization context. In this case, SynchronizationContext.OperationStarted / SynchronizationContext.OperationCompleted will be called upon start and end of the asynchronous void method, correspondingly.
In case an exception is thrown inside an async void method, it will be caught and re-thrown via SynchronizationContext.Post. Thus, it's also possible to collect all exceptions.
Below is an a complete console app example illustrating this approach, loosely based on Stephen Toub's AsyncPump (warning: only slightly tested):
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncVoidTest
{
class Program
{
static async void GetSomethingFromAService()
{
await Task.Delay(2000);
throw new InvalidOperationException(nameof(GetSomethingFromAService));
}
static async Task<int> MyMethodAsync()
{
// call an ill-designed 3rd party async void method
// and await its completion
var pump = new PumpingContext();
var startingTask = pump.Run(GetSomethingFromAService);
await Task.WhenAll(startingTask, pump.CompletionTask);
return 42;
}
static async Task Main(string[] args)
{
try
{
await MyMethodAsync();
}
catch (Exception ex)
{
// this will catch the exception thrown from GetSomethingFromAService
Console.WriteLine(ex);
}
}
}
/// <summary>
/// PumpingContext, based on Stephen Toub's AsyncPump
/// https://blogs.msdn.com/b/pfxteam/archive/2012/02/02/await-synchronizationcontext-and-console-apps-part-3.aspx
/// https://stackoverflow.com/q/49921403/1768303
/// </summary>
internal class PumpingContext : SynchronizationContext
{
private int _pendingOps = 0;
private readonly BlockingCollection<ValueTuple<SendOrPostCallback, object>> _callbacks =
new BlockingCollection<ValueTuple<SendOrPostCallback, object>>();
private readonly List<Exception> _exceptions = new List<Exception>();
private TaskScheduler TaskScheduler { get; }
public Task CompletionTask { get; }
public PumpingContext(CancellationToken token = default(CancellationToken))
{
var taskSchedulerTcs = new TaskCompletionSource<TaskScheduler>();
this.CompletionTask = Task.Run(() =>
{
SynchronizationContext.SetSynchronizationContext(this);
taskSchedulerTcs.SetResult(TaskScheduler.FromCurrentSynchronizationContext());
try
{
// run a short-lived callback pumping loop on a pool thread
foreach (var callback in _callbacks.GetConsumingEnumerable(token))
{
try
{
callback.Item1.Invoke(callback.Item2);
}
catch (Exception ex)
{
_exceptions.Add(ex);
}
}
}
catch (Exception ex)
{
_exceptions.Add(ex);
}
finally
{
SynchronizationContext.SetSynchronizationContext(null);
}
if (_exceptions.Any())
{
throw new AggregateException(_exceptions);
}
}, token);
this.TaskScheduler = taskSchedulerTcs.Task.GetAwaiter().GetResult();
}
public Task Run(
Action voidFunc,
CancellationToken token = default(CancellationToken))
{
return Task.Factory.StartNew(() =>
{
OperationStarted();
try
{
voidFunc();
}
finally
{
OperationCompleted();
}
}, token, TaskCreationOptions.None, this.TaskScheduler);
}
public Task<TResult> Run<TResult>(
Func<Task<TResult>> taskFunc,
CancellationToken token = default(CancellationToken))
{
return Task.Factory.StartNew<Task<TResult>>(async () =>
{
OperationStarted();
try
{
return await taskFunc();
}
finally
{
OperationCompleted();
}
}, token, TaskCreationOptions.None, this.TaskScheduler).Unwrap();
}
// SynchronizationContext methods
public override SynchronizationContext CreateCopy()
{
return this;
}
public override void OperationStarted()
{
// called when async void method is invoked
Interlocked.Increment(ref _pendingOps);
}
public override void OperationCompleted()
{
// called when async void method completes
if (Interlocked.Decrement(ref _pendingOps) == 0)
{
_callbacks.CompleteAdding();
}
}
public override void Post(SendOrPostCallback d, object state)
{
_callbacks.Add((d, state));
}
public override void Send(SendOrPostCallback d, object state)
{
throw new NotImplementedException(nameof(Send));
}
}
}
We are using Tasks in our .Net 4 (no async await available) application and sometimes they are used to launch 'Fire and Forget' operations like the following one:
private void Test()
{
Task task = Task.Factory.StartNew(() =>
{
throw new ApplicationException("Test");
});
}
We want this exception to crash the application without waiting the task (as otherwise it makes no sense to have it in a task, at least in our scenarios) and without waiting the finalizer as we want to shutdown the application when an unexpected error happens to avoid state corruptions (we are saving the state present when the exception happened).
My guess is that somehow we should work with a continuation task but that puts the continuation code inside another task that will not make the application crash so I'm blocked here.
Any help will be very appreciated
Edit: if switching to the ThreadPool the result is the expected one. The following code crashes the application:
ThreadPool.QueueUserWorkItem((c) =>
{
throw new ApplicationException("Test");
});
I finally found how to do it even when it is a bit complicated:
namespace ThreadExceptions
{
using System;
using System.Threading;
using System.Threading.Tasks;
public static class TaskExtensions
{
public static Task ObserveExceptions(this Task task)
{
return task.ContinueWith((t) =>
{
ThreadPool.QueueUserWorkItem((w) =>
{
if (t.Exception != null)
{
foreach (Exception ex in t.Exception.InnerExceptions)
{
throw new TaskException(ex);
}
}
});
}, TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.PreferFairness);
}
}
}
This will make the application crash without waiting for the task. That's was I was looking for.
Try this solution using FailFast
This method terminates the process without running any active
try/finally blocks or finalizers.
private void Test()
{
Task task = Task.Factory.StartNew(() =>
{
Environment.FailFast("Test", new ApplicationException("Test"));
});
}
You could write your own Task class which wraps the various Task methods that you want to use, and add the exception handling to it.
For example:
public static class TaskWithExceptionHandling
{
public static Task StartNew(Action action)
{
var task = Task.Factory.StartNew(action);
task.ContinueWith(exceptionHandler, TaskContinuationOptions.OnlyOnFaulted);
return task;
}
private static void exceptionHandler(Task task)
{
// Handle unhandled aggregate task exception from 'task.Exception' here.
Console.WriteLine("Exception: " + task.Exception.GetBaseException().Message);
}
}
Which you would substitute for the Task class like so:
Task task = TaskWithExceptionHandling.StartNew(() =>
{
throw new InvalidOperationException("Test exception");
});
Console.ReadLine();
The following asynchronous xUnit.net test with a lambda marked with the async modifier fails by reporting that no exception was thrown:
[Theory, AutoWebData]
public async Task SearchWithNullQueryThrows(
SearchService sut,
CancellationToken dummyToken)
{
// Fixture setup
// Exercise system and verify outcome
Assert.Throws<ArgumentNullException>(async () =>
await sut.SearchAsync(null, dummyToken));
// Teardown
}
To make sure that an ArgumentNullException is actually thrown I explicitly used a try-catch block. It worked, however the resulting code is not clean (compared to the first test):
[Theory, AutoWebData]
public async Task SearchWithNullQueryThrows(
SearchService sut,
CancellationToken dummyToken)
{
// Fixture setup
var expected = typeof(ArgumentNullException);
Type actual = null;
// Exercise system
try
{
await sut.SearchAsync(null, dummyToken);
}
catch (ArgumentNullException e)
{
actual = e.GetType();
}
// Verify outcome
Assert.Equal(expected, actual);
// Teardown
}
Why the Assert.Throws<T> with the lambda marked with the async modifier fails?
Update
This has been solved in xUnit 2, with the addition of Assert.ThrowsAsync.
I am suspecting that Assert.Throws is not async-aware. I recommend raising this issue with the xUnit team, suggesting a ThrowsAsync be added.
An async delegate in this case is returning Task or Task<T>, and the ArgumentNullException is not thrown out of the delegate directly; instead, it is placed on the Task (Task.Exception.InnerException). Assert.Throws is expecting the exception to be thrown out of the delegate directly, not placed on a property of the return value.
You can create your own AssertEx.ThrowsAsync as such:
public static async Task ThrowsAsync<TException>(Func<Task> func)
{
var expected = typeof(TException);
Type actual = null;
try
{
await func();
}
catch (Exception e)
{
actual = e.GetType();
}
Assert.Equal(expected, actual);
}
which can be used as such:
[Theory, AutoWebData]
public async Task SearchWithNullQueryThrows(
SearchService sut,
CancellationToken dummyToken)
{
// Fixture setup
// Exercise system and verify outcome
await AssertEx.ThrowsAsync<ArgumentNullException>(async () =>
await sut.SearchAsync(null, dummyToken));
// Teardown
}
I use a similar approach in MSTest.
If you also need to return the exception to verify it then this might be useful:
public static async Task<Exception> AssertThrowsAsync<TException>(Func<Task> func)
{
var expected = typeof (TException);
Exception exception = null;
Type actual = null;
try
{
await func();
}
catch (Exception e)
{
actual = e.GetType();
exception = e;
}
Assert.NotNull(exception);
Assert.Equal(expected, actual);
return exception;
}
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.