Avoiding duplicate methods for Task and Task<T> - c#

I have some logic for Task and Task<T>.
Is there any way to avoid duplicating code ?
My current code is following:
public async Task<SocialNetworkUserInfo> GetMe()
{
return await WrapException(() => new SocialNetworkUserInfo());
}
public async Task AuthenticateAsync()
{
await WrapException(() => _facebook.Authenticate());
}
public async Task<T> WrapException<T>(Func<Task<T>> task)
{
try
{
return await task();
}
catch (FacebookNoInternetException ex)
{
throw new NoResponseException(ex.Message, ex, true);
}
catch (FacebookException ex)
{
throw new SocialNetworkException("Social network call failed", ex);
}
}
public async Task WrapException(Func<Task> task)
{
try
{
await task();
}
catch (FacebookNoInternetException ex)
{
throw new NoResponseException(ex.Message, ex, true);
}
catch (FacebookException ex)
{
throw new SocialNetworkException("Social network call failed", ex);
}
}

You can make the Task overload call the other one, and return a dummy value.
public async Task WrapException(Func<Task> task)
{
await WrapException<object>(async () => {
await task();
return null;
});
}
Or, since the async keyword is unnecessary here:
public Task WrapException(Func<Task> task)
{
return WrapException<object>(async () => {
await task();
return null;
});
}

Assuming that Func does not itself throw, the following would work.
public async Task<T> WrapException<T>(Func<Task<T>> task)
{
var actualTask = task();
await WrapException((Task)actualTask);
return actualTask.Result;
}
We know that Result won't block or throw since WrapException ensured it ran to completion.

Related

Concurrent entrancy occurs despite of use of SemaphoreSlim

I expected the following Connected and Disconnected handlers are called alternately.
But it wasn't. It is not 100% reproducible but sometimes the test failed. Repository is here.
I attach simplified code here because full code will be long. Please refer to the repository for the reproducible example.
public class ClientStressTest3 {
[Fact]
public async Task TestAsync() {
var client = new Client();
int openCloseDifference = 0;
var failures = Channel.CreateUnbounded<string>();
client.Connected += () => {
Interlocked.Increment(ref openCloseDifference);
int difference = openCloseDifference;
Debug.WriteLine("Connected: {}", difference);
if (Math.Abs(difference) > 1) {
// Failure point. Why enter here?
_ = failures.Writer.WriteAsync($"open close difference {difference}");
}
};
client.Disconnected += () => {
Interlocked.Decrement(ref openCloseDifference);
int difference = openCloseDifference;
Debug.WriteLine("Disconnected: {}", difference);
if (Math.Abs(difference) > 1) {
_ = failures.Writer.WriteAsync($"open close difference {difference}");
}
};
var tasks = new List<Task>();
for (int i = 0; i < 625; i++) {
tasks.Add(Task.Run(async () => {
try {
await client.ConnectAsync().ConfigureAwait(false);
}
catch (Exception ex) { }
}));
tasks.Add(Task.Run(async () => {
try {
await client.CloseAsync().ConfigureAwait(false);
}
catch (Exception ex) { }
}));
}
await Task.WhenAll(tasks).ConfigureAwait(false);
while (await failures.Reader.WaitToReadAsync().ConfigureAwait(false)) {
string failure = await failures.Reader.ReadAsync().ConfigureAwait(false);
throw new Exception(failure);
}
}
}
class Client {
public event Action Connected = delegate { };
public event Action Disconnected = delegate { };
private readonly SemaphoreSlim _connectSemaphore = new(1);
private Task _dispatch = Task.CompletedTask;
private WebSocket _clientSocket;
public async Task ConnectAsync() {
await _connectSemaphore.WaitAsync().ConfigureAwait(false);
// _dispatch may be same among multiple threads if restored at the same time.
try {
await _clientSocket.ConnectAsync().ConfigureAwait(false);
if (_dispatch != null) {
_dispatch = _dispatch.ContinueWith((_) => DispatchEventAsync(), TaskScheduler.Default);
}
}
finally {
_connectSemaphore.Release();
}
}
private async Task DispatchEventAsync() {
try {
Connected();
}
catch (Exception exception) { }
try {
while (await events.WaitToReadAsync().ConfigureAwait(false)) {
DispatchEvent(await events.ReadAsync().ConfigureAwait(false));
}
}
catch (Exception exception) { }
try {
Disconnected();
}
catch (Exception ex) { }
}
}
I think _connectSemaphore will guard the inner code execution, and only one thread will execute DispatchEventAsync.
But it seems sometimes two threads enter DispatchEventAsync at the same time. I can't understand this.
ContinueWith is a low-level method with dangerous default behavior (link is to my blog). In this case, your code is not behaving how you think it should because ContinueWith (like StartNew) doesn't understand asynchronous delegates.
Specifically, the task returned from ContinueWith (the same task stored in _dispatch) will complete when DispatchEventAsync asynchronously yields (i.e., hits its first await that asynchronously waits). This is likely the call to WaitToReadAsync, which is after Connected and before Disconnected. So the _dispatch task completes after Connected and before Disconnected.
Ideally, you should avoid ContinueWith completely. Sometimes a local asynchronous method helps, e.g.:
public async Task ConnectAsync() {
await _connectSemaphore.WaitAsync().ConfigureAwait(false);
try {
await _clientSocket.ConnectAsync().ConfigureAwait(false);
if (_dispatch != null) {
_dispatch = ChainAsync(_dispatch);
}
}
finally {
_connectSemaphore.Release();
}
static async Task ChainAsync(Task dispatch)
{
await dispatch;
await DispatchEventAsync();
}
}
If you do want to continue using ContinueWith for some reason, then you can use Unwrap:
public async Task ConnectAsync() {
await _connectSemaphore.WaitAsync().ConfigureAwait(false);
try {
await _clientSocket.ConnectAsync().ConfigureAwait(false);
if (_dispatch != null) {
_dispatch = _dispatch.ContinueWith(_ => DispatchEventAsync(), TaskScheduler.Default)
.Unwrap();
}
}
finally {
_connectSemaphore.Release();
}
}
Using either of these approaches, the _dispatch task will now complete at the end of DispatchEventAsync.
I think _connectSemaphore will guard the inner code execution, and only one thread will execute DispatchEventAsync.
Nope. Because you don't wait or await _dispatch. The semaphore only protects starting the task. The execution of the task happens in the background sometime after you release the semaphore.
But I'm chaining it with ContinueWith. Isn't it sufficien
No. That just adds another task that runs after; it doesn't actually run the task or wait for it to complete. If you want to run the _dispatch and then another task while holding the semaphore, just
await _dispatch;
await DispatchEventAsync();

How to await an async delegate correctly?

While in sync world, I have a TryExecute function to wrap try/catch/log logic for reuse, like this:
TryExecute(() => SyncFunction());
private static void TryExecute(Action action)
{
try
{
action();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
I don't understand how to rewrite it into async/await pattern.
As what I understand, I have five valid ways to rewrite it into async/await (ignore any other Visual Studio has warning).
Using original sync TryExecute() with async delegate:
(1) TryExecute(async () => await AsyncFunction());
It seems not waiting anymore, the TryExecute() passes without waiting AsyncFunction() to finished.
Rewrite to a new sync TryExecuteTask() returns Task, call it with or without async delegate:
(2) await TryExecuteTask(() => AsyncFunction());
(3) await TryExecuteTask(async () => await AsyncFunction());
private static Task TryExecuteTask(Func<Task> asyncAction)
{
try
{
return asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
Or rewrite to a new async TryExecuteAsync(), call it with or without async delegate:
(4) await TryExecuteAsync(() => AsyncFunction());
(5) await TryExecuteAsync(async () => await AsyncFunction());
private async static Task TryExecuteAsync(Func<Task> asyncAction)
{
try
{
await asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
But if I throw Exception from inside AsyncFunction(), then none of above five ways can catch Exception. All stopped with unhandled exception. Only catch without delegate works:
(0) try
{
await AsyncFunction();
}
catch (Exception ex)
{
Log(ex);
}
That means I can't use any forms of TryExecute() from (1) to (5) to reuse the try/catch/log logic, I can only repeating try/catch/log everywhere like (0).
My whole Console code is following:
class Program
{
async static Task Main(string[] args)
{
// Original sync way
TryExecute(() => SyncFunction());
Console.WriteLine("0");
try
{
await AsyncFunction();
}
catch (Exception ex)
{
Log(ex);
}
////Console.WriteLine("1");
////TryExecute(async () => await AsyncFunction());
////Console.WriteLine("2");
////await TryExecuteTask(() => AsyncFunction());
////Console.WriteLine("3");
////await TryExecuteTask(async () => await AsyncFunction());
////Console.WriteLine("4");
////await TryExecuteAsync(() => AsyncFunction());
////Console.WriteLine("5");
////await TryExecuteAsync(async () => await AsyncFunction());
Console.WriteLine("Finished without unhandled exception.");
}
private static void SyncFunction()
{
Console.WriteLine("SyncFunction starting");
Thread.Sleep(500);
Console.WriteLine("SyncFunction starting");
throw new Exception();
}
private async static Task AsyncFunction()
{
Console.WriteLine("AsyncFunction starting");
await Task.Run(() =>
{
Console.WriteLine("Sleep starting");
Thread.Sleep(500);
Console.WriteLine("Sleep end");
throw new Exception();
});
Console.WriteLine("AsyncFunction end");
}
private static void TryExecute(Action action)
{
try
{
action();
}
catch (Exception ex)
{
Log(ex);
}
}
private static Task TryExecuteTask(Func<Task> asyncAction)
{
try
{
return asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
private async static Task TryExecuteAsync(Func<Task> asyncAction)
{
try
{
await asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
private static void Log(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Because of the unhandled exception, I can only comment out all pieces but one in Main() to test every case.
Calling await TryExecuteAsync(AsyncFunction) works like you would expect:
class Program
{
async static Task Main(string[] args)
{
await TryExecuteAsync(AsyncFunction);
Console.WriteLine("Finished without unhandled exception.");
}
private async static Task AsyncFunction()
{
Console.WriteLine("AsyncFunction starting");
await Task.Run(() =>
{
Console.WriteLine("Sleep starting");
Thread.Sleep(3000);
Console.WriteLine("Sleep end");
throw new Exception();
});
Console.WriteLine("AsyncFunction end");
}
private async static Task TryExecuteAsync(Func<Task> asyncAction)
{
try
{
await asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
private static void Log(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
AsyncFunction() raises an exception that is logged and then rethrown in TryExecuteAsync. If you want to catch the rethrown exception, you should put a try/catch around the call to TryExecuteAsync:
async static Task Main(string[] args)
{
try
{
await TryExecuteAsync(AsyncFunction);
Console.WriteLine("Finished without unhandled exception.");
}
catch (Exception ex)
{
Console.WriteLine("Failed to execute: " + ex.Message);
}
}
I don't understand how to rewrite it into async/await pattern.
When converting to async, the first step is to convert what your method calls. In this case, the delegate should be converted to an async-compatible delegate first.
Action is a delegate that takes no parameters and has no return value, like void Method(). An asynchronous method that takes no parameters and has no return value looks like async Task Method(), so its delegate type would be Func<Task>.
Side note: it's especially important when dealing with delegates to remember that async void is unnatural and should be avoided.
Once you change your delegate type from Action to Func<Task>, you can await its return value, which causes your TryExecute method to be changed to async Task, as such:
private static async Task TryExecuteAsync(Func<Task> asyncAction)
{
try
{
await asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
none of above five ways can catch Exception. All stopped with unhandled exception.
That's actually just a side effect of running the code in the debugger. With asynchronous code, you do sometimes see "unhandled" exceptions that are not actually unhandled. This is because it's the compiler-generated code that is catching the exception and placing it on the task, where it will later be re-raised when your code awaits it and then your code will catch it. The debugger gets a bit freaked out when the original exception caught by something other than your code (it's caught by the compiler generated code), and it has no way of knowing that this is perfectly normal.
So if you just continue past the debugger's "unhandled" exception, you'll see it works just fine.

Cannot await async task

I have a method called StartAsync which await different task, this is the design:
public async Task StartAsync(int instance)
{
await SomeController.Foo();
await AnotherController.Foo2();
}
Now I need to run the StartAsync method multiple times, so I have created different Task, in this way I can manage a single execution of the Task of StartAsync:
Task bot1 = new Task(async () => { await new Bot().StartAsync(1); });
Task bot2 = new Task(async () => { await new Bot().StartAsync(2); });
these Tasks can be started by the input, essentially, if the user press 1 then the bot1 Task will start:
public async Task<bool> StartBotInstanceAsync(int instance)
{
try
{
switch(instance)
{
case 1:
await bot1.Start(); //<- Problem here
break;
case 2:
bot2.Start();
break;
}
return true;
}
catch(Exception ex)
{
Logger.Info(ex.ToString());
return false;
}
}
Essentially I need to write a log when an exception happen in side the try catch block, but for doing this I need to await the Task result, unfortunately I get this error:
Cannot await void
on await bot1.Start();
How can I manage the exception in this type of situation?
If you wish to do it with your current structure then you will need 2 separate statements.
case 1:
bot1.Start();
await bot1;
break;
If you want to use multiple bot instances, store them in an array and use the instance parameter as the index, eg :
_bots=new[]{ new Bot(),new Bot()};
public async Task<bool> StartBotInstanceAsync(int instance)
{
try
{
await _bots[instance-1].StartAsync();
return true;
}
catch(Exception ex)
{
Logger.Info(ex.ToString());
return false;
}
}
Or even better :
public async Task<bool> StartBotInstanceAsync(Bot bot)
{
try
{
await bot.StartAsync();
return true;
}
catch(Exception ex)
{
Logger.Info(ex.ToString());
return false;
}
}
var myBot=_bots[instance-1];
var started=await StartBotInstanceAsync(myBot);
More complex flows can be created easily by using async functions that take the bot as a parameter, eg :
public async Task SomeComplexFlow(Bot bot)
{
try
{
await bot.StartAsync();
await bot.DoSomething();
...
}
catch(Exception exc)
{
...
}
}
var myBot=_bots[instance-1];
await SomeComplexFlow(myBot);
It's possible to await multiple tasks for completion too. One coud use Task.WhenAll to wait for all bots to terminate, eg :
await Task.WhenAll(_bots[0].StopAsync()...);
Or, better yet :
var tasks=_bots.Select(bot=>bot.StopAsync());
await Tasks.WhenAll(tasks);

Throwing FormCancelledException from await instead of AggregateException in unit test

I'm currently trying to test the following code in an application that makes use of the Microsoft Bot Framework.
public async Task ResumeAfterCalculation_v2FormDialog(IDialogContext context, IAwaitable<Calculation_v2Form> result)
{
try
{
var extractedCalculationForm = await result;
//Removed additional code
}
catch (FormCanceledException ex)
{
var reply = "You have canceled the operation.";
await _chat.PostAsync(context, reply);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
finally
{
context.Done<object>(null);
}
}
When a user types 'quit' to the bot the 'await result' code throws a FormCanceledException and the code quits the form.
When creating a test I implemented a class to mock the IAwaitable:
public class TaskAwaiterHelper<T> : IAwaiter<T>, IAwaitable<T>
{
public Task<T> Task { get; }
public TaskAwaiterHelper(T obj)
{
this.Task = System.Threading.Tasks.Task.FromResult(obj);
}
public TaskAwaiterHelper(Task<T> task)
{
this.Task = task;
}
public bool IsCompleted { get { return Task.IsCompleted; } }
public void OnCompleted(Action action)
{
SynchronizationContext context = SynchronizationContext.Current;
TaskScheduler scheduler = context == null ? TaskScheduler.Current
: TaskScheduler.FromCurrentSynchronizationContext();
Task.ContinueWith(ignored => action(), scheduler);
}
public T GetResult()
{
return Task.Result;
}
public IAwaiter<T> GetAwaiter()
{
return this;
}
}
I then created the following test:
[Fact]
public async Task ResumeAfterCalculation_v2FormDialog_WasCancelled_ThenCallsDone()
{
//Arrange
var chat = new Mock<IChatHelper>();
var calculationApi = new Mock<ICalculationApi>();
var dialogContextMock = new Mock<IDialogContext>();
var rootLuisDialog = new RootLuisDialog(chat.Object, calculationApi.Object);
var taskAwaiter = new TaskAwaiterHelper<Calculation_v2Form>(new Task<Calculation_v2Form>(() =>
{
throw new FormCanceledException("Error created for test test", null);
}));
taskAwaiter.Task.Start();
//Act
await rootLuisDialog.ResumeAfterCalculation_v2FormDialog(dialogContextMock.Object, taskAwaiter);
//Assert
chat.Verify(c => c.PostAsync(dialogContextMock.Object, "You have canceled the operation."), Times.Once());
dialogContextMock.Verify(t => t.Done<object>(null), Times.Once());
}
Now whatever I try to do I the exception that's being thrown in the IAwaitable is being wrapped in an AggregateException, so we always end up in the catch (Exception ex) instead of the desired catch (FormCanceledException ex)
Is there a way to make a Task throw a specific Exception instead of an AggregateException (I mean there should be as the bot framework itself seems to be able to do it).
I just found the answer, I basically created a new class:
public class ExceptionThrower : IAwaitable<Calculation_v2Form>
{
public IAwaiter<Calculation_v2Form> GetAwaiter()
{
throw new FormCanceledException("Error created for test test", null);
}
}
And just provided this to the method:
var exceptionThrower = new ExceptionThrower();
await rootLuisDialog.ResumeAfterCalculation_v2FormDialog(dialogContextMock.Object, exceptionThrower);

async Exception not being caught

I have a windows service that references the following code. My code that uses the below method contains a try..catch block but it doesn't seem to catch RefereshTokenException that is thrown in the below method. Obviously my understanding of async is not correct.
private async void RefreshTokens()
{
try
{
var cognito = new CognitoApi();
var response = cognito.TokenRefresh(_refreshToken);
if (response.HttpStatusCode == HttpStatusCode.OK)
{
_idToken = new AwsToken(response.AuthenticationResult.IdToken);
_accessToken = new AwsToken(response.AuthenticationResult.AccessToken);
}
else
{
await _signIn(_credentials.SiteId, _credentials.LocationId, null);
}
}
catch (NotAuthorizedException)
{
await _signIn(_credentials.SiteId, _credentials.LocationId, null);
}
catch (Exception ex)
{
throw new RefreshTokenException("Failed refreshing tokens.", ex);
}
}
This is the code that calls RefreshTokens
public async void Process(QueueMessage queueMessage, Action<QueueMessage> retryAction)
{
_processingCounter.Increment();
try
{
......
IAwsToken idToken = authenticationService.Tokens.IdToken; //This is the code that calls "RefreshTokens" method
........
}
catch (Exception ex)
{
//Code never reaches here...
_logger.Error("Error in ProcessMessage", ex);
}
_processingCounter.Decrement();
}
This is an async void. One of the main reasons to avoid async void methods is that you cannot handle the exceptions they throw.
Make it an async Task and await it in the caller.
Note that you then have the same issue in that caller, async void Process(...)
Make that an async Task as well and work your way up. async/await should form a chain, from your GUI or Controller down to an async I/O call.

Categories

Resources