TaskCompletionSource SynchronizationContext - c#

I'm writing some basic Firebase code in a Xamarin iOS app and am running into a classic deadlock situation with a TaskCompletionSource.
public Task<string> GetUsers()
{
var tcs = new TaskCompletionSource<string>();
_instance.GetChild("users").ObserveSingleEvent(DataEventType.Value,
x => { tcs.SetResult(x); });
return tcs.Task;
}
When I block on this code like so:
var users = GetUsers().Result;
The application deadlocks.
If I understand correctly, the callback is trying to run on the same context that the .Result is waiting on.
What I don't understand is that if I modify my code to await the GetUsers() call in a Task like so:
var result = Task.Run(
async () => await AppContext.Database.GetUsers().ConfigureAwait(false)
).Result;
It still deadlocks.
What's going on here in the second case? Shouldn't the fact that the code is running on another thread due to the Task.Run mean that the outside .Result doesn't block the callback invocation?
EDIT:
Following up on Nkosi's comment I'm asking this because I'm curious as to why the code is blocking. If I await the call
var users = await GetUsers().ConfigureAwait(false);
then the deadlock goes away. I'd just like to understand why it blocks when wrapped in a Task because based on my (clearly incorrect) understanding of Task.Run, it shouldn't.

ObserveSingleEvent always dispatches callback to UI thread (and I think all or almost all firebase callbacks do that). It does not capture synhronization context or something like that - just always dispatches callback to UI thread (remember - it's just a wrapper around native IOS code). So when you block your UI thread by waiting on Result - it will deadlock for obvious reasons, regardless of from which thread you call GetUsers. Links you mention describe another situation when called code captures current synhronization context, so they call that code from background thread which has no synhronization context and callbacks will not be posted to it. That's not the case here.

Related

Await a thread wont work when dispatch inside of the thread

I'm running a Task in a new thread and want to wait for it to finish.
var a = "logic before the task starts";
await Task.Factory.StartNew(() => MyHugeFunction(_token), _token);
var b = "logic after the task is finished";
It worked perfectly until I started to dispatch inside of the thread with:
await Application.Current.Dispatcher.BeginInvoke(new Action(async () =>
{
SomeLogic();
}));
The task itself works, but the await for my running task isn't working anymore. As soon as I am dispatching in the thread, the var b will be assigned in the main thread.
I already have some workarounds but I just wondered if I'm doing something stupid or this is caused by other circumstances
I'm working with C# 8.0 and .Net Framework 4.7.2.
I'm running a Task in a new thread and want to wait for it to finish.
You want to use Task.Run instead of StartNew for that. StartNew is a dangerous, low-level API that should almost never be used, and in the few cases where you should use it, you should always pass a TaskScheduler.
await Application.Current.Dispatcher.BeginInvoke(new Action(async () =>
Imma stop you right there. First, you're explicitly creating an Action delegate with async, which results in an async void method, which should be avoided. Next, using Dispatcher to do any kind of Invoke or BeginInvoke really shouldn't be done at all.
Instead, use the Progress<T> type. This keeps your logic in your background thread, which can pass objects as updates to the UI thread, and the UI thread decides how to display those progress updates in the UI. Note the nice separation of concerns there, which tends to go out the window whenever people start using Dispatcher.
Both StartNew and Dispatcher are commonly seen in SO answers and blogs, but they're suboptimal solutions regardless.

How does control flow when there is synchronous code in awaited function?

Say in button click event I have:
await someFn()
And in the someFn code, I have got some synchronous code and some asynchronous code.
As per online reading on this topic, they all seem to say that when await is encountered, control immediately goes back the caller (UI thread in this case) while the asynchronous waiting happens.
I haven't come across situation where it is explained what happens when there is some synchronous code just before the async code in the awaited function. Does the synchronous code execute before passing the control to the caller (UI thread)?
I know that- had it been run using await Task.Run(...) instead of await someFn() then it would execute in a separate thread.
It is true that "when await is encountered, control immediately goes back the caller", under the condition that the awaitable will not be completed at the await point. Otherwise (if the awaitable is already completed) the control will stay inside the method, by executing immediately the code that follows the await.
That's all that influences the behavior of the await operator: the result of the IsCompleted property of the awaiter. It doesn't matter if the method that produced the awaitable included synchronous code or not. The await doesn't know, and doesn't care. A synchronous code block inside the someFn will indeed make a difference regarding the responsiveness of your application, but that has nothing to do with the await. To understand why, it may help to rewrite your code in a more verbose but equivalent form. This:
await someFn();
...is equivalent to this:
Task t = someFn();
await t;
The UI of your application will freeze because the someFn() blocks, not because the await t blocks. Unless of course you have created some wicked task-like awaitable type, that blocks when the IsCompleted property of its awaiter is invoked.
If you see an await in your code, for example when you see var obj = await someFn();, then you can treat is as a placeholder for the following code:
var waitingTask task = someFn();
if (!task.IsCompleted) {
var thisTask = [Magic]
return thisTask.Task;
}
var obj = waitingTask.Result;
In reality the code is rewritten to something more complex, a so called state machine. The [Magic] part is hard to explain but what it does is capturing the current SynchronizationContext, the current line that is executing and the task it's waiting on.
As soon as the task that you are waiting on completes, it will try to restore this method on the captured SynchronizationContext and continue working. It's this SynchronizationContext that tells you if you are working on the UI thread or not. This is why it's advised to use ConfigureAwait(false) to avoid restoring on the UI thread if it's not needed, avoiding possible slow downs on the UI for executing code that does nothing with the UI.
The main point here is that you see that the code before an await is executed synchronously/immediately. Even the code inside the function that you are calling is executed synchronously/immediately until you reach an await in that function that is not completed. From this point on the stack 'rewinds', creating (returning) a new Task on each point that waits for the underlying code to complete. If the lowest task completes, only that task that waits for it is 'revived', all tasks above it still stay 'waiting' and only when that bottom function has reached it real end of the method will it 'revive' the task that is waiting for it's result, and so on to the top of the stack.

Use ConfigureAwait(false) or Task.Run to avoid blocking UI Thread

I'm trying to find out which approach is better let's say we have a button, after user clicks it we perform 1. Send a async request using httpClient
2. Some heavy synchronous staff like computations and saving data to a database.
Like that:
button1.Click += async(sender, e) =>
{
bool a = await Task.Run(async () => { return await MyTask1();});
}
async Task<bool> MyTask1()
{
await new HttpClient().GetAsync("https://www.webpage.com");
DoHeavyStuffFor5minutes();
return true;
}
button2.Click += async(sender, e) =>
{
bool a = await MyTask2();
}
async Task<bool> MyTask2()
{
await new HttpClient().GetAsync("https://www.webpage.com").ConfigureAwait(false);
DoHeavyStuffFor5minutes();
}
From what i understand GetAsync does not block my UI thread because underneath it uses a method which make it runs on different thread perhaps Task.Run or any other method that allows that.
But DoHeavyStuffFor5Minutes will block my UI because it will get called on the caller SynchornizationContext.
So i read that using ConfigureAwait(false) will make code after GetAsync do not run on the same SynchornizationContext as the caller. My question is, which approach is better first or the second one?
There is no need to execute HttpClient.GetAsync on a background thread using Task.Run since the HTTP request is truly asynchronous by nature so in this case your second approach is better that the first one.
When the Task returned by GetAsync has eventually finished, the remainder or MyTask2() will be executed on a thread pool thread assuming you opt out of capturing the context by calling ConfigureAwait(false).
Note however that ConfigureAwait(false) does not guarantee that the callback or remainer won't be run in the original context in all cases.
From Stephen Toub's blog post:
Does ConfigureAwait(false) guarantee the callback won’t be run in the original context?
"No. It guarantees it won’t be queued back to the original contex...but that doesn’t mean the code after an await task.ConfigureAwait(false) won’t still run in the original context. That’s because awaits on already-completed awaitables just keep running past the await synchronously rather than forcing anything to be queued back. So, if you await a task that’s already completed by the time it’s awaited, regardless of whether you used ConfigureAwait(false), the code immediately after this will continue to execute on the current thread in whatever context is still current."
So you might want to off-load DoHeavysTuffFor5minutes, which I assume is a CPU-bound and potentially long-running operation, to a background thread using Task.Run to be on the safe side. At least in the general case.
Also note that a method that is named *Async and returns a Task or Task<T> might still block the calling thread depending on its implementation. In general, this may be a reason to use your first approach of a calling both methods on a background thread in order to avoid blocking the UI thread. If you however use well-implemented APIs, such as HttpClient, this isn't an issue though.

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.

What happens while waiting on a Task's Result?

I'm using the HttpClient to post data to a remote service in a .NET 4.0 project. I'm not concerned with this operation blocking, so I figured I could skip ContinueWith or async/await and use Result.
While debugging, I ran into an issue where the remote server wasn't responsive. As I stepped through the code, it seemed like my code just stopped running on the third line... the current stack pointer line stopped being highlighted yellow, and didn't advance to the next line. It just disappeared. It took me a while to realize that I should wait for the request to timeout.
var client = new HttpClient();
var task = client.PostAsync("http://someservice/", someContent);
var response = task.Result;
My understanding was that calling Result on the Task caused the code to execute synchronously, to behave more like this (I know there is no Post method in the HttpClient):
var client = new HttpClient();
var response = client.Post("http://someservice/", someContent);
I'm not sure this is a bad thing, I'm just trying to get my head around it. Is it really true that by virtue of the fact that the HttpClient is returning Tasks instead of the results directly, my application is automatically taking advantage of asynchrony even when I think I'm avoiding it?
In Windows, all I/O is asynchronous. Synchronous APIs are just a convenient abstraction.
So, when you use HttpWebRequest.GetResponse, what actually happens is the I/O is started (asynchronously), and the calling thread (synchronously) blocks, waiting for it to complete.
Similarly, when you use HttpClient.PostAsync(..).Result, the I/O is started (asynchronously), and the calling thread (synchronously) blocks, waiting for it to complete.
I usually recommend people use await rather than Task.Result or Task.Wait for the following reasons:
If you block on a Task that is the result of an async method, you can easily get into a deadlock situation.
Task.Result and Task.Wait wrap any exceptions in an AggregateException (because those APIs are holdovers from the TPL). So error handling is more complex.
However, if you're aware of these limitations, there are some situations where blocking on a Task can be useful (e.g., in a Console application's Main).
Capturing the result of a task blocks the current thread. There is no point in using a async version of a method in this case. Post() and PostAsync().Result will both block.
If you want to make use of concurrency, you should write it as such:
async Task PostContent()
{
var client = new HttpClient();
Task t = await client.PostAsync("http://someservice/", someContent);
//code after this line will execute when the PostAsync completes.
return t;
}
Since PostContent() itself returns a Task, the method calling it should also await.
async void ProcessResult()
{
var result = await PostContent();
//Do work with the result when the result is ready
}
For instance, if you call ProcessResult() in a button click handler, you see that the UI is still responsive, other controls still function.

Categories