WPF await switches from the UI thread - c#

I have a function called from the UI thread, I'm double checking if that's so by
System.Windows.Threading.Dispatcher.FromThread(System.Threading.Thread.CurrentThread)
everything is called from a void method, which can't be async void because it has an out parameter (method from 3rd party API).
So in this void method I make a call to an async method, the main method doesn't have to wait, so I just run it and forget (planning to handle exceptions in the ContinueWith method, but I'm not there yet).
Problem is, that when in the method chain an await is performed on the ExecuteTaskAsync method of the RestSharp client, after that line, the thread is witched to MTA, the SynchronizationContext.Current is different too.
Why is this happening? Shouldn't the await keyword return to the orginal SynchronizationContext? Why is the SynchronizationContext.Current changing during that call?
Sorry for no code snippets, it's a part of a bigger app and it's hard to snippet it out, but I'm doing it right now, so code snippets will be added.

Ok, resolved it, the mentioned void method from the 3rd party API, was running with a thread that has a dispatcher, because
System.Windows.Threading.Dispatcher.FromThread(System.Threading.Thread.CurrentThread)
Returned a dispatcher, I don't know why, but that didn't matter, I had to run my async method in an Invoke like
Application.Current.Dispatcher.Invoke(() => myAsyncMethod());
and then the await in my method didn't switched the SynchronizationContext.
It looks like my assumption that when there is a dispatcher it's the proper context was wrong, don't know why :)

Related

Background started task does not finish/gets terminated after the first encountered await

In an ASP.NET application, I have an action which when hit, starts a new background task in the following way:
Task.Factory.StartNew(async () => await somethingWithCpuAndIo(input), CancellationToken.None, TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning, TaskScheduler.FromCurrentSynchronizationContext());
I'm not awaiting it, I just want it to start, and continuing doing its work in the background.
Immediately after that, I return a response to the client.
For some reason though, after the initial thread executing the background work hits an await that awaits the completion of a method, when debugging, I successfully resolve the method, but upon returning, execution just stops and does not continue below that point.
Interestingly, if I fully await this task (using double await), everything works as expected.
Is this due to the SynchronizationContext? The moment I return a response, the synchronizationContext is disposed/removed? (The SynchronizationContext is being used inside the method)
If it is due to that, where exactly does the issue happen?
A) When the Scheduler attempts to assign the work on the given synchronizationContext, it will already be disposed, so nothing will be provided
B) Somewhere down the lines in the method executing, when I return a response to the client, the synchronizationContext is lost, regardless of anything else.
C) Something else entirely?
If it's A), I should be able to fix this by simply doing Thread.Sleep() between scheduling the work and returning a response. (Tried that, it didn't work.)
If it's B) I have no idea how I can resolve this. Help will be appreciated.
As Gabriel Luci has pointed out, it is due the the first awaited incomplete Task returning immediately, but there's a wider point to be made about Task.Factory.StartNew.
Task.Factory.StartNew should not be used with async code, and neither should TaskCreationOptions.LongRunning. TaskCreationOptions.LongRunning should be used for scheduling long running CPU-bound work. With an async method, it may be logically long running, but Task.Factory.StartNew is about starting synchronous work, the synchronous part of an async method is the bit before the first await, this is usually very short.
Here is the guidance from David Fowler (Partner Software Architect at Microsoft on the ASP.NET team) on the matter:
https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/86b502e88c752e42f68229afb9f1ac58b9d1fef7/AsyncGuidance.md#avoid-using-taskrun-for-long-running-work-that-blocks-the-thread
See the 3rd bulb:
Don't use TaskCreationOptions.LongRunning with async code as this will
create a new thread which will be destroyed after first await.
Your comments made you intentions a little clearer. What I think you want to do is:
Start the task and don't wait for it. Return a response to the client before the background task completes.
Make sure that the somethingWithCpuAndIo method has access to the request context.
But,
A different thread won't be in the same context, and
As soon as the first await is hit, a Task is returned, which also means that Task.Factory.StartNew returns and execution of the calling method continues. That means that the response is returned to the client. When the request completes, the context is disposed.
So you can't really do both things you want. There are a couple of ways to work around this:
First, you might be able to not start it on a different thread at all. This depends on when somethingWithCpuAndIo needs access to the context. If it only needs the context before the first await, then you can do something like this:
public IActionResult MyAction(input) {
somethingWithCpuAndIo(input); //no await
}
private async Task somethingWithCpuAndIo(SomeThing input) {
// You can read from the request context here
await SomeIoRequest().ConfigureAwait(false);
// Everything after here will run on a ThreadPool thread with no access
// to the request context.
}
Every asynchronous method starts running synchronously. The magic happens when await is given an incomplete Task. So in this example, somethingWithCpuAndIo will start executing on the same thread, in the request context. When it hits the await, a Task is returned to MyAction, but it is not awaited, so MyAction completes executing and a response gets sent to the client before SomeIoRequest() has completed. But ConfigureAwait(false) tells it that we don't need to resume execution in the same context, so somethingWithCpuAndIo resume execution on a ThreadPool thread.
But that will only help you if you don't need the context after the first await in somethingWithCpuAndIo.
Your best option is to still execute on a different thread, but pass the values you need from the context into somethingWithCpuAndIo.
But also, use Task.Run instead of Task.Factory.StartNew for reasons described in detail here.
Update: This can very likely cause unpredictable results, but you can also try passing a reference to HttpContext.Current to the thread and setting HttpContext.Current in the new thread, like this:
var ctx = HttpContext.Current;
Task.Run(async () => {
HttpContext.Current = ctx;
await SomeIoRequest();
});
However, it all depends on how you are using the context. HttpContext itself doesn't implement IDiposable, so it, itself, can't be disposed. And the garbage collector won't get rid of it as long as you're holding a reference to it. But the context isn't designed to live longer than the request. So after the response is returned to the client, there may be many parts of the context that are disposed or otherwise unavailable. Test it out an see what explodes. But even if nothing explodes right now, you (or someone else) might come back to that code later, try to use something else in the context and get really confused when it blows up. It could make for some difficult-to-debug scenarios.

Manually capturing and applying SynchronizationContext when completing a Task

I was having a problem with a hanging await (described here). During research I found out that calling SetResult on my TaskCompletionSource actually invokes awaiting continuation in the context of the thread that called SetResult (this is also spelled out in this answer to a somewhat related question). In my case this is a different thread (a thread-pool worker thread) from the one that started the await (an ASP.NET request thread).
While I'm still not sure why this would cause a hang, I decided to try forcing the SetResult into the original context. I stored the value of SynchronizationContext.Current before entering await on the request thread and manually applied it in the worker thread via SynchronizationContext.SetSynchronizationContext just before calling SetResult. This solved the hang and I can now await all my async methods without having to specify ConfigureAwait(false).
My question is: is this a reasonable and correct approach to manually capturing and applying the SynchronizationContext? FWIW, I tried doing a simple Post() with the SetResult delegate first, but that still caused a hang. I'm obviously a bit out of my comfort zone here... Please help me understand what's going on!
SetResult is not guaranteed to call anything. Therefore, this is not reliable.
You need to switch the sync context at the point where it is captured. A common pain point here is WebClient which captures the context when starting a web request. So your code would look like this:
SetContext(newContext);
new WebClient().DownloadAsync(...);
SetContext(oldContext);
Restore the old context to not disturb anything.
In other words the problem is in the continuation code, not in the code calling SetResult.
To my embarrassment, I had completely overlooked that my HTTP handler was derived from a small base class, which implemented IAsyncHttpHandler in a very questionable way in order to add support for async handlers:
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
...
var task = HandleRequestAsync(...);
Task.Run(async () => { await task; }).GetAwaiter().GetResult();
...
}
I can't even remember why I did this in the first place (it was over a year ago), but it definitely was THE stupid part I was looking for for the last couple of days!
Changing the handler base class to .NET 4.6's HttpTaskAsyncHandler got rid of the hangs. Sorry for wasting everyone's time! :(

Difference between calling .Wait() on an async method, and Task.Run().Wait()

I have a server-side click event on a ASP.NET WebForms website. In this event I call a method which in turn calls its async partner method, adding .Wait() on the call.
This method then goes several levels down (i.e., calls another async method, which calls another async method, and so on) and eventually calls an async method on an HttpClient object. At this point the thread seems to disappear down a rabbit hole; the method never calls back.
Now, I know that the async series of calls works as expected because the same code is also called from a Web API controller (the controller method calls the async version of that first method, not the synchronous 'partner' method), which is fully async, and it returns as expected.
So basically I have something like this, which never returns
protected void btn_Click(object sender, EventArgs e)
> Class1.DoSomething()
> Class1.DoSomethingAsync.Wait()
...
> await ClassN.Authenticate()
{
await myHttpClient.PostAsync() // never returns
}
I did try using .ConfigureAwait(false) on that first async method but without any success.
I also have this, which does return
Task<IHttpActionResult> MyWebApiMethod()
> await Class1.DoSomethingAsync()
...
> await ClassN.Authenticate()
{
await myHttpClient.PostAsync() // does return
}
I've found that I can make the first version work if I change it to the following:
protected void btn_Click(object sender, EventArgs e)
> Class1.DoSomething()
> Task.Run(async () => await Class1.DoSomethingAsync()).Wait()
...
> await ClassN.Authenticate()
{
await myHttpClient.PostAsync()
}
But I don't know why.
Can anyone explain the difference between calling
Class1.DoSomethingAsync.Wait()
and calling
Task.Run(async () => await Class1.DoSomethingAsync()).Wait()
I explain this behavior in my blog post Don't Block on Asynchronous Code and my MSDN article on asynchronous best practices.
the thread seems to disappear down a rabbit hole; the method never calls back.
This happens because one of the awaits is attempting to resume on the ASP.NET context, but the request thread (using that ASP.NET context) is blocked waiting for the task to complete. This is what causes your deadlock.
I did try using .ConfigureAwait(false) on that first async method but without any success.
In order to avoid this deadlock using ConfigureAwait(false), it must be applied to every await in every method called. So DoSomethingAsync must use it for every await, every method that DoSomethingAsync calls must use it for every await (e.g., Authenticate), every method that those methods call must use it for every await (e.g., PostAsync), etc. Note that at the end here, you are dependent on library code, and in fact HttpClient has missed a few of these in the past.
I've found that I can make the first version work if I change it [to use Task.Run].
Yup. Task.Run will execute its delegate on a thread pool thread without any context. So this is why there's no deadlock: none of the awaits attempt to resume on the ASP.NET context.
Why don't you use it the correct way. async void btn_Click and await Class1.DoSomethingAsync() ?
Do not use Task.Run with already asynchronous methods, this is waste of threads. Just change signature of button_click eventhandler
And here's the right answer: don't block on asynchronous code. Just use an async void event handler and use await instead.
P.S. ASP.NET Core no longer has an ASP.NET context, so you can block as much as you want without fear of deadlocks. But you still shouldn't, of course, because it's inefficient.

Sync to async dispatch: how can I avoid deadlock?

I'm trying to create a class that has synchronous methods and calls some other library methods which are asynchronous. For that reason I use Task.Result to wait for the async operation to finish. My methods are called by WPF app in synchronous way. This leads to a deadlock. I know that the best way is to make all my methods asynchronous but my situation requires them to be synchronous. From the other hand they use other library which is asynchronous.
My question is: How can I avoid the deadlock in such situation?
Steps to reproduce:
User hits a button in the app (method Button1_OnClick)
This method creates an instance of IPlugin and then calls its method RequestSomething()
This method then calls async library this way: asyncTarget.MethodFromAsyncLibrary("HelloFromPlugin").Result
The library calls back its method NotifyNewValueProgressAsync()
NotifyNewValueProgressAsync() delegates the call back to the WPF application
Since the UI context is blocked by this line asyncTarget.MethodFromAsyncLibrary("HelloFromPlugin").Result the callback in step 5 leads to a deadlock.
See code example below and related comments:
public class SyncAdapterPlugin : IPlugin, IProgressAsyncHandler
{
//Constructor and fields are omitted here
//This method is called from UI context by WPF application and it delegates synchronous call to asynchronous method
string IPlugin.RequestSomething()
{
//In order to be able to run the callback I need to capture current UI context
_context = TaskScheduler.FromCurrentSynchronizationContext();
var asyncTarget = new ClassFromMyLibrary1(this);
var resultFromAsyncLibrary = asyncTarget.MethodFromAsyncLibrary("HelloFromPlugin").Result; //Deadlock here!
return resultFromAsyncLibrary;
}
//This method does opposite, it delegates asynchronous callback to synchronous
async Task<bool> IProgressAsyncHandler.NotifyNewValueProgressAsync(string message)
{
//NotifyNewValueProgress method is implemented by WPF application and will update UI elements.
//That's why it's needed to run the callback on captured UI context.
Func<bool> work = () => _syncProgressHandler.NotifyNewValueProgress(message);
if (_context != null)
{
return await
Task.Factory.StartNew(work, CancellationToken.None, TaskCreationOptions.None, _context)
.ConfigureAwait(false);
}
return work();
}
}
Complete code example is here https://dotnetfiddle.net/i48sRc.
FYI, Some background on this issue you can also find in this SO question.
The plugin framework is fundamentally flawed. In particular, it requires a synchronous RequestSomething that expects to be able to call NotifyNewValueProgressAsync to update the UI. However, it's not possible to display a UI update while the UI thread is running a synchronous method.
This forces you to use one of the most dangerous and evil sync-over-async hacks: the nested message loop hack (as briefly described in my article on brownfield async). Since this is a WPF app, you'd use a nested dispatcher frame. The main pain with this hack is that it introduces reentrancy across your entire UI layer, which is the most subtle and difficult kind of concurrency problem.
By definition a synchronous method isn't going to be asynchronous. You will need to wrap the calls to the synchronous methods from the UI in a Task using TAP and await them there while making the method you await from asynchronous.

SignalR invoke not returning when used in an async method

I'm not sure if this is a SignalR issue or an async/await issue. When my client app (WPF) starts up, it does some initialisation:-
public async void Initialise()
{
// Get data from the server - async, as it may be long-running.
var data = await _hubProxy.Invoke<FooData>("Method1");
_dataProcessor.ProcessData(data);
}
The _dataProcessor is a helper class that does some stuff with the data passed to it, then at some point calls a different server method using a line similar to:-
var moreData = _hubProxy.Invoke<BarData>("Method2").Result;
None of the code in the helper class is async.
The first server invoke (in Initialise()) works fine - it gets back the data and passes it to the helper class. This proceeds without issue until its invoke - it gets called but never returns, and the thread never proceeds past this line. I've put a breakpoint in the server method and can confirm that it is being called and returning a value, but for some reason this is not getting back to the client.
The strange thing is, if I take out the async/await keywords in the Initialise() method, everything works fine. What am I doing wrong? Why would these keywords affect the synchronous code in the helper class?
(I realise you shouldn't normally use async void, but as the Initialise() method is "fire and forget", I thought it was okay in this scenario).
This line causes a deadlock on the UI thread:
var moreData = _hubProxy.Invoke<BarData>("Method2").Result;
Make ProcessData an async method and await _hubProxy.Invoke inside it:
var moreData = await _hubProxy.Invoke<BarData>("Method2");
Then, await _dataProcessor.ProcessData in your Initialise:
await _dataProcessor.ProcessData(data);
Make sure you don't use .Result or .Wait anywhere else.
Another way of solving it is just this:
public async void Initialise()
{
// Get data from the server - async, as it may be long-running.
var data = await _hubProxy.Invoke<FooData>("Method1").ConfigureAwait(false);
_dataProcessor.ProcessData(data);
}
Note ConfigureAwait(false). In this case the continuation after await will happen on a non-UI thread. This will eliminate the deadlock, but this is not an ideal solution, rather a workaround. Moreover, your logic may require the continuation on the UI thread, for UI access or some thread-safety concerns.
The best solution would be to use both ConfigureAwait(false) (if possible) and avoid blocking with .Result or .Wait, at the same time.

Categories