C# HttpClient PostAsync blocks forever - c#

I have a strange behavior that I can't manage to explain.
In an async function, an awaited call blocks forever.
Note: it seams that the problem occurs since I moved from a console app to a Windows Form. (the call is called from the constructor of Form1().
_client is the HttpClient dotnet class.
public async Task GetConfigurationFile()
{
var stringContent = new StringContent(JsonConvert.SerializeObject(companyKey), Encoding.UTF8, "application/json");
HttpResponseMessage response = null;
// This call works and returns the respons after a few milliseconds
response = _client.PostAsync(_configurationFileEndpoint, stringContent).Result;
// The same awaited call block forever and never returns.
response = await _client.PostAsync(_configurationFileEndpoint, stringContent);
}
public Form1()
{
InitializeComponent();
_engine = new Engine();
}
public Engine()
{
// Logic similar to this.
Configuration configuration = null;
try
{
using (var reader = new StreamReader(Directory.GetCurrentDirectory() + "/configuration.json"))
{
configuration = Newtonsoft.Json.JsonConvert.DeserializeObject<Configuration>(reader.ReadToEnd());
}
}
catch (Exception ex)
{
// Something done
}
_apiCall = new PlatformCommunication(configuration);
if (configuration == null)
{
try
{
_apiCall.GetConfigurationFile().Wait();
}
catch (Exception exc)
{
}
}
}

You are doing this:
_apiCall.GetConfigurationFile().Wait();
As explained in many places, such as here - blocking on async code from UI thread is bad idea. When you have this:
response = await _client.PostAsync(_configurationFileEndpoint, stringContent);
the SynchronizationContext will be captured before await and execution after await will continue on that context, which means in this case on UI thread. It cannot continue there, because UI thread is blocked by GetConfigurationFile().Wait(), so you have deadlock.
When you have this:
response = _client.PostAsync(_configurationFileEndpoint, stringContent).Result;
The code inside PostAsync uses ConfigureAwait(false) on every async call, to prevent continuations running on captured context. So all continuations run on thread pool threads and you can get away with blocking on async call with Result in this case (doesn't make it good idea still). Then after this change your GetConfigurationFile becomes synchronous (there is no await left), so you can get away with Wait() also.
You can do the same ConfigureAwait(false):
response = await _client.PostAsync(_configurationFileEndpoint, stringContent).ConfigureAwait(false);
And it will help in this case, but that's not the way to solve this problem. The real way is to just not block on async code on UI thread. Move _apiCall.GetConfigurationFile() outside of constructor.

#YK1: to prevent blocking calls, I can move the code in the
constructor of Engine() to an Async Initialize function and await
_apiCall.GetConfigurationFile() instead of_apiCall.GetConfigurationFile().Wait(); But then in my Winform, I
need to await engine.Initialize() from an Async function which I don't
have? ( engine must run automatically, not be behind a start button),
reason why I put it in the constructor of the form which is not async.
Instead of constructor, move your startup code code to an async method. You can subscribe to Form_Load event and call that method.
class Engine
{
public async Task Init()
{
// Logic similar to this.
Configuration configuration = null;
try
{
using (var reader = new StreamReader(Directory.GetCurrentDirectory() + "/configuration.json"))
{
configuration = Newtonsoft.Json.JsonConvert.DeserializeObject<Configuration>(reader.ReadToEnd());
}
}
catch (Exception ex)
{
// Something done
}
_apiCall = new PlatformCommunication(configuration);
if (configuration == null)
{
try
{
await _apiCall.GetConfigurationFile();
}
catch (Exception exc)
{
}
}
}
}
and
private async void Form_Load(object sender, EventArgs e)
{
_engine = new Engine();
await _engine.Init();
}

Related

Async method is working as syncronous mehtod

I am trying to call a async method of ViewModel from another method in View but it is behaving as syncronous.
View Model:
public async Task<bool> GetReadyForUnlockWithQR()
{
try
{
SmilaHash = GetRandomeHashKey();
var data = JsonConvert.SerializeObject(GetSmilaInfo());
var res = await apiClient.PostAsync<String>("readyforunlockwithqr", data);
if (res != null)
{
JObject json = JObject.Parse(res);
if (json["doUnlock"] != null)
{
LoginStatus = json.SelectToken("doUnlock").Value<bool>();
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
CancelPendingRequests();
throw ex;
}
return false;
}
I have my api methods defined in a custome APIClient file. The above request may take a minute to complete. I don't want to stop the UI and perform my further operations in View. Following is my View:
private async void UnlockButton_Click(object sender, RoutedEventArgs e)
{
try
{
await ViewModel.GetReadyForUnlockWithQR();
DisplayQRCode();
}
catch(Exception ex)
{
if (ex is HttpRequestException)
{
Debug.WriteLine("Server not reachable");
MessageBox.Show("Server not reachable");
}
else if (ex is OperationCanceledException)
{
Debug.WriteLine("Timeout exception");
QRCodeImage.Source = null;
QRCodeCanvas.Visibility = Visibility.Hidden;
}
else
{
Debug.WriteLine(ex.Message);
}
}
}
I above code ideally the DisplayQRCode() function should work immediately after await ViewModel.GetReadyForUnlockWithQR(); but it is not happening. The DisplayQRCode() is waiting to receive response from ViewModel.GetReadyForUnlockWithQR() Why is this not behaving as logical asyn code.
The DisplayQRCode() is waiting to receive response from ViewModel.GetReadyForUnlockWithQR() Why is this not behaving as logical asyn code.
The asynchronous method is behaving serially (not "synchronously"), which is exactly what await is supposed to do.
You can think of await as "asynchronous wait": the method is paused and will not continue pass the await until the task completes, but it waits asynchronously, so the thread is freed (the method returns to its caller).
I above code ideally the DisplayQRCode() function should work immediately after await ViewModel.GetReadyForUnlockWithQR(); but it is not happening.
If you want to do that, then you can call GetReadyForUnlockWithQR but don't await the task until after DisplayQRCode completes:
var getReadyTask = ViewModel.GetReadyForUnlockWithQR();
DisplayQRCode();
await getReadyTask;

Using MessageBox when part of an await has ConfigureAwait false

Reading Stephen Cleary take on not blocking on Async code I write something like this
public static async Task<JObject> GetJsonAsync(Uri uri)
{
using (var client = new HttpClient())
{
var jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
return JObject.Parse(jsonString);
}
}
public async void Button1_Click(...)
{
var json = await GetJsonAsync(...);
textBox1.Text=json;
}
so far so good, I understand that after the ConfigureAwait the method is going to continue running on a different context after GetStringAsync returns.
but what about if I want to use something like MessageBox (which is UI) like this
public static async Task<JObject> GetJsonAsync(Uri uri)
{
if(someValue<MAXVALUE)
{
using (var client = new HttpClient())
{
//var jsonString = await client.GetStringAsync(uri); //starts the REST request
var jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
return JObject.Parse(jsonString);
}
}
else
{
MessageBox.Show("The parameter someValue is too big!");
}
}
can I do this?
Even more complicated, how about this?
public static async Task<JObject> GetJsonAsync(Uri uri)
{
if(someValue<MAXVALUE)
{
try{
using (var client = new HttpClient())
{
//var jsonString = await client.GetStringAsync(uri); //starts the REST request
var jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
return JObject.Parse(jsonString);
}
}
catch(Exception ex)
{
MessageBox.Show("An Exception was raised!");
}
}
else
{
MessageBox.Show("The parameter someValue is too big!");
}
}
Can I do this?
Now, I am thinking perhaps all the message boxes should be called outside GetJsonAync as good design, but my question is can the above thing be done?
can I do this? [use a MessageBox]
Yes, but mainly because it has nothing to do with async/await or threading.
MessageBox.Show() is special, it is a static method and is documented as thread-safe.
You can show a MessageBox from any thread, any time.
So maybe it was the wrong example, but you do have MessageBox in the title.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
try{
... // old context
... await client.GetStringAsync(uri).ConfigureAwait(false);
... // new context
}
catch
{
// this might bomb
someLabel.Text = "An Exception was raised!";
}
}
In this example, there could be code paths where the catch runs on the old and other paths where it runs on the new context.
Bottom line is: you don't know and should assume the worst case.
I would not use a Message Box, as it is very limited, and dated.
Also, Pop up's are annoying.
Use your own user control which enables user interaction the way you intend it.
In the context of Winforms / WPF / (and I guess UWP), only a single thread can manipulate the UI. Other threads can issue work to it via a queue of actions which eventually get invoked.
This architecture prevents other threads from constantly poking at the UI, which can make UX very janky (and thread unsafe).
The only way to communicate with it the UI work queue (in Winforms) is via the System.Windows.Form.Controls.BeginInvoke instance method, found on every form and control.
In your case:
public async void Button1_Click(...)
{
var json = await GetJsonAsync(...).ConfigureAwait(false);
BeginInvoke(UpdateTextBox, json);
}
private void UpdateTextBox(string value)
{
textBox1.Text=json;
}

File create blocking UWP from loading

UWP is way over my head and sorry for this question.
Why is a file create blocking my UWP from loading?
public static async Task CreateFile()
{
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
var item = await ApplicationData.Current.LocalFolder.TryGetItemAsync("sFile.xml");
if (item == null)
{
StorageFile file = await storageFolder.CreateFileAsync("sFile.xml");
await FileIO.WriteLinesAsync(file, GlobalVars.fileStrings);
}
}
This function is called from my main method
The CreateFile function never returns. Why is that?
Edit: Added main method
public GroupedItemsPage()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += navigationHelper_LoadState;
try
{
SampleDataSource.CreateFile().Wait();
Debug.WriteLine("Continue");
}
catch (Exception ex)
{
var msg = new MessageDialog(ex.StackTrace);
Task.Run(async () => { await msg.ShowAsync(); }).Wait();
throw ex.InnerException;
}
GlobalVars.LastFreeSignalCheckTimer.Tick += SampleDataSource.LastFreeSignalCheckTimer_Tick;
GlobalVars.LastFreeSignalCheckTimer.Interval = new TimeSpan(0, 0, 0, 120);
GlobalVars.LastFreeSignalCheckTimer.Start();
}
I think the problem is the .Wait() from your SampleDataSource.CreateFile().Wait(); use await SampleDataSource.CreateFile(); instead.
Wait() is a blocking operation you must be careful if you want to use it because you can easy build a deadlock. In most situations await is the better choice.
In this case you are blocking the UI thread with your Wait() so that the successful execution of the CreateFile() method in the same thread is prevented. With an await instead your code should work because the thread is not blocked while you wait so that other code (like your CreateFile()) can be executed.
Another solution is to use Task.Run to execute the method in background so that you can wait with Wait() because the method is not executed in the same blocked thread (but it would still be ugly code design to block the whole UI thread).
You are running into a deadlock. That's why you should never block on async code.
Instead of calling the async CreateFile method in the constructor of the Page, which is bad practice, you could call it once the page has been initialized by handling the Loaded event of the page. Then you can await the async methods as you should:
public sealed partial class GroupedItemsPage : Page
{
public GroupedItemsPage()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += navigationHelper_LoadState;
this.Loaded += GroupedItemsPage_Loaded;
}
private async void GroupedItemsPage_Loaded(object sender, RoutedEventArgs e)
{
try
{
await SampleDataSource.CreateFile();
Debug.WriteLine("Continue");
}
catch (Exception ex)
{
var msg = new MessageDialog(ex.StackTrace);
await msg.ShowAsync();
throw ex.InnerException;
}
GlobalVars.LastFreeSignalCheckTimer.Tick += SampleDataSource.LastFreeSignalCheckTimer_Tick;
GlobalVars.LastFreeSignalCheckTimer.Interval = new TimeSpan(0, 0, 0, 120);
GlobalVars.LastFreeSignalCheckTimer.Start();
}
}

NUnit test with Application loop in it hangs when Form is created before it

I have a few tests with WebBrowser control wrapped with MessageLoopWorker as described here: WebBrowser Control in a new thread
But when another test creates user control or form, the test freezes and never completes:
[Test]
public async Task WorksFine()
{
await MessageLoopWorker.Run(async () => new {});
}
[Test]
public async Task NeverCompletes()
{
using (new Form()) ;
await MessageLoopWorker.Run(async () => new {});
}
// a helper class to start the message loop and execute an asynchronous task
public static class MessageLoopWorker
{
public static async Task<object> Run(Func<object[], Task<object>> worker, params object[] args)
{
var tcs = new TaskCompletionSource<object>();
var thread = new Thread(() =>
{
EventHandler idleHandler = null;
idleHandler = async (s, e) =>
{
// handle Application.Idle just once
Application.Idle -= idleHandler;
// return to the message loop
await Task.Yield();
// and continue asynchronously
// propogate the result or exception
try
{
var result = await worker(args);
tcs.SetResult(result);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
// signal to exit the message loop
// Application.Run will exit at this point
Application.ExitThread();
};
// handle Application.Idle just once
// to make sure we're inside the message loop
// and SynchronizationContext has been correctly installed
Application.Idle += idleHandler;
Application.Run();
});
// set STA model for the new thread
thread.SetApartmentState(ApartmentState.STA);
// start the thread and await for the task
thread.Start();
try
{
return await tcs.Task;
}
finally
{
thread.Join();
}
}
}
The code steps-in well except return await tcs.Task; never returns.
Wrapping new Form into the MessageLoopWorker.Run(...) seems to make it better, but it does not work with more complicated code, unfortunately. And I have quite a lot of other tests with forms and user controls that I would like to avoid wrapping into messageloopworker.
Maybe MessageLoopWorker can be fixed to avoid the interference with other tests?
Update: following the amazing answer by #Noseratio I've reset the synchronisation context before the MessageLoopWorker.Run call and it now works well.
More meaningful code:
[Test]
public async Task BasicControlTests()
{
var form = new CustomForm();
form.Method1();
Assert....
}
[Test]
public async Task BasicControlTests()
{
var form = new CustomForm();
form.Method1();
Assert....
}
[Test]
public async Task WebBrowserExtensionTest()
{
SynchronizationContext.SetSynchronizationContext(null);
await MessageLoopWorker.Run(async () => {
var browser = new WebBrowser();
// subscribe on browser's events
// do something with browser
// assert the event order
});
}
When running the tests without nulling the sync context WebBrowserExtensionTest blocks when it follows BasicControlTests. With nulling it pass well.
Is it ok to keep it like this?
I repro'ed this under MSTest, but I believe all of the below applies to NUnit equally well.
First off all, I understand this code might have been taken out of context, but as is, it doesn't seem to be very useful. Why would you want to create a form inside NeverCompletes, which runs on an random MSTest/NUnit thread, different from the thread spawned by MessageLoopWorker?
Anyhow, you're having a deadlock because using (new Form()) installs an instance of WindowsFormsSynchronizationContext on that original unit test thread. Check SynchronizationContext.Current after the using statement. Then, you facing a classic deadlock well explained by Stephen Cleary in his "Don't Block on Async Code".
Right, you don't block yourself but MSTest/NUnit does, because it is smart enough to recognize async Task signature of NeverCompletes method and then execute something like Task.Wait on the Task returned by it. Because the original unit test thread doesn't have a message loop and doesn't pump messages (unlike is expected by WindowsFormsSynchronizationContext), the await continuation inside NeverCompletes never gets a chance to execute and Task.Wait is just hanging waiting.
That said, MessageLoopWorker was only designed to create and run WinForms object inside the scope of the async method you pass to MessageLoopWorker.Run, and then be done. E.g., the following wouldn't block:
[TestMethod]
public async Task NeverCompletes()
{
await MessageLoopWorker.Run(async (args) =>
{
using (new Form()) ;
return Type.Missing;
});
}
It was not designed to work with WinForms objects across multiple MessageLoopWorker.Run calls. If that's what you need, you may want to look at my MessageLoopApartment from here, e.g.:
[TestMethod]
public async Task NeverCompletes()
{
using (var apartment = new MessageLoopApartment())
{
// create a form inside MessageLoopApartment
var form = apartment.Invoke(() => new Form {
Width = 400, Height = 300, Left = 10, Top = 10, Visible = true });
try
{
// await outside MessageLoopApartment's thread
await Task.Delay(2000);
await apartment.Run(async () =>
{
// this runs on MessageLoopApartment's STA thread
// which stays the same for the life time of
// this MessageLoopApartment instance
form.Show();
await Task.Delay(1000);
form.BackColor = System.Drawing.Color.Green;
await Task.Delay(2000);
form.BackColor = System.Drawing.Color.Red;
await Task.Delay(3000);
}, CancellationToken.None);
}
finally
{
// dispose of WebBrowser inside MessageLoopApartment
apartment.Invoke(() => form.Dispose());
}
}
}
Or, you can even use it across multiple unit test methods, if you're not concerned about potential coupling of tests, e.g. (MSTest):
[TestClass]
public class MyTestClass
{
static MessageLoopApartment s_apartment;
[ClassInitialize]
public static void TestClassSetup()
{
s_apartment = new MessageLoopApartment();
}
[ClassCleanup]
public void TestClassCleanup()
{
s_apartment.Dispose();
}
// ...
}
Finally, neither MessageLoopWorker nor MessageLoopApartment were designed to work with WinForms object created on different threads (which is almost never a good idea anyway). You can have as many MessageLoopWorker/MessageLoopApartment instances as you like, but once a WinForm object has been created on the thread of a particular MessageLoopWorker/MessageLoopApartment instance, it should further be accessed and properly destroyed on the same thread only.

C# async HttpWebRequest deadlocking

For few hours I am struggling with async code in C# and I can't really get why is my code deadlocked.
So far I've red many articles and anything ringed the bell for me.
Hope you can help me.
Here is the code I am trying to run.
Main
Task.Run(async () =>
{
Task<EventDetailed[]> events = getDetailedEvents();
await events;
}).Wait();
getDetailedEvents:
static async Task<EventDetailed[]> getDetailedEvents()
{
...
EventDetailed[] result = await LoadDetailedEventsDetailsAsync(evnts).ConfigureAwait(false);
return result;
}
And the core of my problem.
LoadDetailedEventsDetailsAsync
async static Task<EventDetailed[]> LoadDetailedEventsDetailsAsync(Event[] events)
{
List<EventDetailed> detailed = new List<EventDetailed>();
List<Task<WebResponse>> responses = new List<Task<WebResponse>>();
List<Event> tasksWithStream = new List<Event>();
foreach (Event e in events)
{
var httpWebRequest = (HttpWebRequest)WebRequest.Create("http://...");
... some headers etc ...
e.Stream = httpWebRequest.GetRequestStreamAsync();
e.WebRequest = httpWebRequest;
tasksWithStream.Add(e);
}
foreach (var tsk in tasksWithStream)
{
try {
await tsk.Stream.ConfigureAwait(false);
using (var streamWriter = new StreamWriter(tsk.Stream.Result))
{
streamWriter.Write("...");
streamWriter.Flush();
streamWriter.Close();
}
responses.Add(tsk.WebRequest.GetResponseAsync());
}
catch (Exception ex)
{
Logger.mes("Failed to get event data.");
}
}
foreach (var response in responses)
{
try
{
await response.ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.mes("Failed to get event data.");
continue;
}
parseData.Add(ParseData(response));
}
A couple points:
First, it's important to note that you should almost never call .Wait (or .Result) on an async task - you should use await instead. One of the very few exceptions is in the Main method of a console app. The reason is that if you don't block the main thread, your program will simply exit prematurely.
Second, if you need to make multiple HTTP requests that do not depend on each other (i.e. request B does not need the results of request A), then there are huge performance gains to be had by executing them in parallel. Better still, you are not consuming a thread per request because the calls are asynchronous, i.e. they don't block a thread while waiting for a response, so the same thread can effectively fire off many concurrent requests.
I won't re-write your code, but I will suggest how I'd restructure it:
static void Main(string[] args)
{
// start all async tasks in parallel.
var tasks = GetEvents().Select(GetEventDetailsAsync);
// wait for them all to complete. normally you should use await instead of Wait,
// but you can't because you're in the main method of a console app.
Task.WhenAll(task).Wait();
}
static IEnumerable<Event> GetEvents()
{
// build a list of whatever metadata is needed to do your async work.
// do NOT do any actual async work here.
}
async static Task<EventDetailed> GetEventDetailsAsync(Event e)
{
// do all async work here, use await as needed,
// but only for one event (no loops).
}

Categories

Resources