Calling async extension methods from separate assembly - c#

I've encountered a weird behavior regarding async extension methods in separate assemblies.
We have the following:
One assembly handling sending of EventGridEvent. Target is .NET Standard 2.0. This assembly references Microsoft.Azure.EventGrid.
One assembly using assembly no. 1. Target is .NET Framework 4.7.
For some reason, making synchronous methods from assembly no. 2 to assembly no. 1 results in weird behaviour. Consider the two functions we have in assembly no. 1:
public async Task PublishAsync(...)
{
await _eventGridClient.PublishEventsAsync(_eventGridTopicHostName, ...);
}
public void Publish(...)
{
_eventGridClient.PublishEventsAsync(_eventGridTopicHostName, ...).Wait();
}
If we call the first method from assembly no. 2 with PublishAsync().Wait(), it will never return. Publish() will, however. But, if Publish() calls PublishAsync().Wait(), that method will also hang.
Worth mentioning is that EventGridClient contains LongRunningOperationRetryTimeout with default set to 30, which is ignored. It never returns.
Anyone have any idea what causes this behavior? A workaround is to copy code, but we would like to avoid that.
Thanks in advance.

You should never block on async code by calling Wait() or .Result on the returned Task. #Stephen Cleary explains why on his blog.
When _eventGridClient.PublishEventsAsync is called, the SynchronizationContext is captured. When the task completes, it waits for the context to become available but it never will since you are blocking it with your call to .Wait(). This leads to a deadlock.
You may get of out trouble by avoiding capturing the context by calling ConfigureAwait(false):
public async Task PublishAsync(...)
{
await _eventGridClient.PublishEventsAsync(_eventGridTopicHostName, ...)
.ConfigureAwait(false);
}
But the best solution is still not to block at all. Async code should be "async all the way" as explained in the linked blog post.

The problem was that the calling method was running on a UI thread. It was solved by wrapping the call like so: Task.Run(() => ...).Wait()

Related

Calling an async method with `.Result` in a Linq expression causes deadlock

We ran into a bug where we had to validate a list of objects with an async method. The writer of the code wanted to just stuff it into a Linq expression like so:
var invalidObjects = list
.Where(x => _service.IsValidAsync(x).Result)
.ToList();
The validating method looked something like this:
public async Task<bool> IsValidAsync(object #object) {
var validObjects = await _cache.GetAsync<List<object>>("ValidObjectsCacheKey");
return validObjects.Contains(#object);
}
This little solution caused the whole application to hang on the await _cache.GetAsync line.
The cache is a distributed cache (redis).
After changing the linq to a simple foreach and properly awaiting _service.IsValidAsync, the code ran deadlock-free and basically in an instant.
I understand on a basic level how async-await works, but I can't wrap my head around why this happened especially because the list only had one object.
Any suggestion is welcome!
EDIT: The application is running on .net Core 2.2, but the library in which the problem happened is targeting .netstandard 2.0.
System.Threading.SynchronizationContext.Current returns null at the time of the deadlock
EDIT2: It turns changing the cache provider (but still accessing it via an async method) also resolves the issue, so the bug might actually be in the redis cache client:
https://github.com/aspnet/Caching/blob/master/src/Microsoft.Extensions.Caching.StackExchangeRedis/RedisCache.cs
Apart from the already stated issue of mixing async-await and blocking calls like .Result or .Wait()
Reference Async/Await - Best Practices in Asynchronous Programming
To summarize this second guideline, you should avoid mixing async and blocking code. Mixed async and blocking code can cause deadlocks, more-complex error handling and unexpected blocking of context threads. The exception to this guideline is the Main method for console applications, or—if you’re an advanced user—managing a partially asynchronous codebase.
Sometimes the simple approach, as you have already discovered, is to traverse the list and properly await the asynchronous function
For example
var invalidObjects = //...
foreach(var x in list){
if(!(await _service.IsValidAsync(x)))
invalidObjects.Add(x);
}

Calling Async Method from Sync Method

I see a lot of questions about calling asyc methods from sync ones, like here: How to call asynchronous method from synchronous method in C#?
I understand the solutions however, experimentally, I am seeing that I don't need to add a .Wait() or Task.Run(async () => await MyAsyncMethod()) for it to run synchronously. I have a get method that pulls information from a DB.
My code is below and when I run, it waits at var task = GetItemAsync() and I get the correct result from the DB. I find it hard to believe that the DB call could come back before the code would move on which seems to imply that it ran synchronously. Am I missing something?
public object GetItem()
{
var task = GetItemAsync();
var result = task.Result;
return result
}
See The docs on Task<>.Result
System.Threading.Tasks.Task.Result is a blocking call that waits until the task finishes. So it has a built in wait call essentially.
In the remarks section of that microsoft doc:
Accessing the property's get accessor blocks the calling thread until the asynchronous operation is complete; it is equivalent to calling the Wait method.
It's unfortunate that Microsoft did not reflect this in the name of the accessor because it's an extremely critical side effect of this accessor.
The only right way to call awaitable(method which returns Task) method is to await it. But you can await only within the method which returns Task. In legacy code it happens you cannot change the method signature because you have too many reference and you need to basically refactor entire code base. So you have these options:
.Result
.Wait()
.GetAwaiter().GetResult()
Neither of those are good. All of them are going to block the thread. But if you cannot change the method signature to await the execution, I would pick the last option - GetAwaiter().GetResult() becasue it will not wrap the exception into AggregateException

Set ConfigureAwait(false) for entire project/dll

According to an article in MSDN Magazine, it is the best practice to "Use ConfigureAwait(false) when you can." Furthermore it states, "If you can use ConfigureAwait at some point within a method, then I recommend you use it for every await in that method after that point." Stephen Cleary, the author of that article, states on his blog that "In your 'library' async methods, use ConfigureAwait(false) wherever possible [emphasis added]."
Given that most or all of my await statements in a library project that is meant to be widely used should have .ConfigureAwait(false) is it possible to do something/change a setting to make it so that the default behavior of await within my project/dll is to not continue on the a captured context? That is, so that I could omit all the calls to .ConfigureAwait(false) and instead add .ConfigureAwait(true) only when I need the context to be preserved.
No, there is no option for this. The Microsoft team did consider a compiler setting or something for that behavior, but ended up rejecting it.
The main reason is that it's quite difficult to tell how the code behaves without knowing how the compiler switch is set.
There is now a third party library that supports this. The ConfigureAwait.Fody Nuget package allows you to use a ConfigureAwait attribute to mark a class, assembly or method such that all awaits should be .ConfigureAwait(false). The project page has this example usage:
using Fody;
[ConfigureAwait(false)]
public class MyAsyncLibrary
{
public async Task MyMethodAsync()
{
await Task.Delay(10);
await Task.Delay(20);
}
public async Task AnotherMethodAsync()
{
await Task.Delay(30);
}
}
I have not tried this myself, but given what I know about how Fody add-ins work, it is certainly possible.

How do I fix this deadlock in Win Forms constructor when using async/await?

Here is my minimum repro case:
public Form1()
{
Task.Delay(100).Wait(); // Works just fine
this.Await().Wait(); // Blocks indefinitely
}
private async Task Await()
{
await Task.Delay(100);
}
What is going on here? Why are these two behaving differently? What can I do to make the later one work?
My actual case is less trivial, and I can't "just use the first option".
You're seeing a classic deadlock situation that I describe on my blog and in an MSDN article. In short, after the await completes, the async method is attempting to resume on the UI thread, which you have blocked by calling Wait.
To fix it, you ideally want to use async all the way (i.e., never block on async code). Constructors pose a difficulty here (since they cannot be async); I explore several options on my blog. The correct option depends on your code base, but I recommend the async factory method if possible. The options are:
Async factory method.
Asynchronous lazy initialization.
Asynchronous initialization pattern.
If you absolutely cannot use one of the options that I describe on my blog, then you can work around this by using ConfigureAwait(false) in all your async methods, and then your Wait() will not deadlock. However, this will block the UI thread during those asynchronous method calls (which sort of defeats the purpose of them being async in the first place...)

Does Task.RunSynchronously() work "recursively"?

If I call RunSynchronously() on a Task in C#, will this lead to asynchronous calls further down the rabbit hole to be run synchronously as well?
Let's say I have a method named UpdateAsync(). Inside this method another asynchronous call is made to DoSomethingAsync() and inside this again we find DoSomethingElseAsync(), will calling RunSynchronously() on 'UpdateAsync()' lead to RunSynchronously() also indirectly being called on DoSomethingAsync()?
The reason for my question:
I have a situation where I "need" to call an asynchronous method (UpdateAsync()) inside a catch-block and wonder if calling this with RunSynchronously() is safe. The documentation is quite clear on the fact that you can't await inside a catch-block. (Strictly speaking I could use a boolean inside the catch-block and call UpdateAsync() after the try-catch, but that feels rather dirty). Sorry about the dual question, but as you probably understand I don't quite know how to phrase it and do not have a really good understanding of this field.
(Edit:
I don't know how to find out if a method was called asynchronously. How would you write a unit test for this? Is it possible to log it somehow?)
I "need" to call an asynchronous method and wonder if calling this with RunSynchronously() is safe
There are two kinds of Tasks: code-based* Tasks (you can create those by using the Task constructor or Task.Factory.StartNew()) and promise-style Tasks (you can create them manually by using TaskCompletionSource or by writing an async method).
And the only Tasks that you can start by calling Start() or RunSynchronously() are unstarted code-based Tasks. Since async methods return promise-style Tasks (or possibly already started code-based Tasks), calling RunSynchronously() on them is not valid and will result in an exception.
So, to actually answer your question: what you're asking isn't possible, so it doesn't make sense to ask whether it's safe.
* This is not an official name, I don't know if there is one.
It's hard to predict without code how it will execute nested async methods.
You can log on each async method Thread.CurrentThread.ManagedThreadId property and compare id with other thread IDs that you have. When they vary, then your async methods are in multithreaded executtion
Or try to use Concurrency Visualizer from Visual Studio, Analyze menu. With Task class instances and even with C#5 async syntax there is no way to get to know that you are executing in parallel or another thread.
I think I'll be contradicting #svick's answer, but I feel the OP question is valid, because an async method's "promised" (to use Svick's terminology) Task can be obtained, but not started, thus allowing to do Task.RunSynchronously().
static void Main(string[] args)
{
...
//obtain a Task that's not started
var t = new Task<int>((ob) => GetIntAsync((string)ob).Result, someString);
...
}
static async Task<int> GetIntAsync(string callerThreadId)
{
...
Having said that, the answer is: No, the RunSynchronously() affects the task you run this on. If the call chain later contains more async calls, they run asynchronously.
I have a little console app that is modeling this, is someone is interested in seeing it, but the concept is pretty simple to reproduce - just chain enough asynchronous calls and toggle between running the earliest one synchronously and asynchronously to see the different behavior.

Categories