I have a pretty basic script in my Unity game which is trying to submit form data in POST to a server. But unity seems to freeze/hang indefinitely. I am confused why this is happening and so i don't know how to solve the problem.
I made my code loosely following this article:
https://learn.microsoft.com/en-us/dotnet/csharp/tutorials/console-webapiclient#making-web-requests
My server does receive the request and does write one back. But because unity is frozen its not really usable at the moment.
My class looks like this:
public class HTTPManager : MonoBehaviour
{
private static HttpClient client = new HttpClient();
private void Awake()
{
client.BaseAddress = new Uri("http://localhost:8080/");
ProcessRepositories().Wait();
}
private async Task ProcessRepositories()
{
client.DefaultRequestHeaders.Accept.Clear();
FormUrlEncodedContent content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("u", "test")
});
//this goes to http://localhost:8080/hello with POST u=test
HttpResponseMessage result = await client.PostAsync("hello",content);
Debug.Log("Is Success: "+result.IsSuccessStatusCode);
Debug.Log("Status code: "+result.StatusCode);
Debug.Log("Reason Phrase: "+result.ReasonPhrase);
Debug.Log("Headers: "+result.Headers);
Debug.Log("Content: "+result.Content);
Debug.Log("Request Message: "+result.RequestMessage);
Debug.Log("Version: "+result.Version);
}
}
Whats causing the hanging issue??
ProcessRepositories().Wait();
is blocking on async code and it is causing a deadlock. Stephen Clearly has a whole series of posts on this.
Either make all the code async all the way up or schedule continuations on your Task.
EDIT: It seems that Awake is a Unity method. For this you have 4 options:
1) Remove async Task from ProcessRepositories(). Remove the await in the method body and make your calls to your server synchronous. This will cause jitters and it not a recommended solution in a game environment.
2) Remove .Wait() from ProcessRepositories(). This will cause your code to async post to server but you won't be able to do anything with the the response. This is fine if you don't care about the response.
3.) If you do care about the response, you can schedule Continuations on ProcessRepositories(). This is a type of "callback" that will run when your Task has RanToCompletion or it errored out. To achieve this, change ProcessRepositories() to ProcessRepositories().ContinueWith(task=>
{
// in here you can access task.Result to get your response
}) and change your ProcessRepositories method to return the HttpResponseMessage result
i.e return await client.PostAsync("hello",content);
4) Use ConfigureAwait(false) as mentioned below but that will return the result on a different context and seeing that this Unity, you probably want the result on the UI thread, so just watch out for that.
Don't use HttpClient with unity, use the class WWW and unity's built in coroutine features, yield return it and it will wait for the download to complete.
public class HTTPManager : MonoBehaviour
{
private IEnumerator Awake()
{
return ProcessRepositories();
}
private IEnumerator ProcessRepositories()
{
var form = new WWWForm();
form.AddField("u", "test");
var www = new WWW("http://localhost:8080/hello", form);
yield return www;
Debug.Log("responseHeaders: "+www.responseHeaders);
Debug.Log("text: "+www.text);
}
}
Related
In my .Net 6 WebPI service, I am queueing work to a background task queue, very closely based on the example here, but I could post parts of my implementation if that would help:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-6.0&tabs=visual-studio#queued-background-tasks
I am running into unexpected behavior where control is not returned to the caller, even after the return Ok(..) completes in the controller. Instead the request only completes after the await Task.Delay(1000); line is reached on the queued work item. The request returns to the client as soon as this line is reached, and does not need to wait for the Delay to finish.
I'm guessing this is because of the await either starting a new async context, or someone un-sticking the async context of the original request. My intention is for the request to complete immediately after queuing the work item.
Any insight into what is happening here would be greatly appreciated.
Controller:
public async Task<ActionResult> Test()
{
var resultMessage = await _testQueue.DoIt();
return Ok(new { Message = resultMessage });
}
Queueing Work:
public TestAdapter(ITaskQueue taskQueue)
{
_taskQueue = taskQueue;
}
public async Task<string> DoIt()
{
await _taskQueue.QueueBackgroundWorkItemAsync(async (_cancellationToken) =>
{
await Task.Delay(1000);
var y = 12;
});
return "cool";
}
IoC:
services.AddHostedService<QueueHostedService>();
services.AddSingleton<ITTaskQueue>(ctx =>
{
return new TaskQueue(MaxQueueCount);
});
TaskQueue:
private readonly Channel<BackgroundTaskContext> _queue;
public TaskQueue(int capacity)
{
var options = new BoundedChannelOptions(capacity)
{
FullMode = BoundedChannelFullMode.Wait
};
_queue = Channel.CreateBounded<BackgroundTaskContext>(options);
}
public async ValueTask QueueBackgroundWorkItemAsync(
Func<CancellationToken, ValueTask> workItem)
{
if (workItem == null)
{
throw new ArgumentNullException(nameof(workItem));
}
await _queue.Writer.WriteAsync(new BackgroundTaskContext(workItem, ...));
}
Not sure what you expect here. I'm assuming you want the async method to return the cool in the api response. That's fine but because your also awaiting the async call with in DoIt(), then it pauses until QueueBackgroundWorkItemAsync finishes. You could remove the await and it will run and return as you expect.
I can't say that I'm a big fan of that design as you lose contact with it with exception of the cancellation token. Another thought would be to Send that work off to a console job or function app using message bus or even another http call.
Additional Notes:
Async can be complicated to explain because in reality, it wraps up code and executes on a thread of it's choosing. The await simulates the synchronous behavior.
await Task.Delay(1000); // Runs on it's own thread but still halts code execution for 1 second.
await _taskQueue.QueueBackgroundWorkItemAsync(async (_cancellationToken) // Waits for control to be returned from the code inside.
var resultMessage = await _testQueue.DoIt(); // Always waits for the code inside to complete.
If your wanting something to run without pausing code execution, you can either remove the await or add a Task.Run(() => { }); pattern. Is it a good idea is a whole other question. It also matters whether you need information back from the async method. If you don't await it then you'll get null back as it doesn't wait around for the answer to be computed.
This appears just to have been user error using the debugger. The debugger is switching to the background task thread and hitting breakpoints there before the response fully returns giving the appearance that control was not being returned to the client and being carried into the background task.
Even after adding some synchronous steps in QueueBackgroundWorkItemAsync and putting breakpoints on them, control was not immediately returned to the original http call. Only after I tried adding a long running task like await Task.Delay(1000); did enough time/ticks pass for the http response to return. I had conflated this with just the await somehow freeing up the original http context.
I've been banging my head on this for hours, and I've thrown my hands up in the air on this one. As far as I can tell, I've encountered a deadlock surrounding HttpClient and Async.
The goal is for a series of unrelated Posts to all get fired off in rapid succession, wait for them all to get done, and then construct a document from the resultset. The program has a WPF UI, and this is triggered by button:
private async void Generate_Suite_BTN_Click(object sender, RoutedEventArgs e)
{
var suiteBuilder = new SuiteBuilder();
await Task.Run(() => suiteBuilder.worker_Run());
}
That triggers the worker_Run(), which has some switch logic, and eventually gets down to hitting SendFiles() which has a Parrallel.Foreach, since the sending of the files doesn't need to be sequential, and are unrelated to eachother:
private bool SendFiles()
{
var result = Parallel.ForEach(_InfoCollection, SendFile);
return result.IsCompleted;
}
Each of those (in the parallel), is awaited in SendFile(), which also has some switch logic, based on what we're sending, but ultimately boils down to:
var result = await Client.SendMessage ( vars );
results.Add(result.MessageId, result.StatusCode, result.HttpReason);
Here's the HttpClient portion in SendMessage():
public async Task<Result> SendMessage(vars)
{
var soapResponse = new XmlDocument();
try
{
Client.DefaultRequestHeaders.Add("SOAPAction", soapAction);
Client.Timeout = TimeSpan.FromSeconds(Timeout);
var content = new StringContent(soapRequest, Encoding.UTF8, contentType);
var post = await Client.PostAsync(url, content).ConfigureAwait(false);
var response = post.Content;
result.StatusCode = post.StatusCode;
result.HttpReason = post.ReasonPhrase;
var sResponse = await response.ReadAsStringAsync().ConfigureAwait(false);
soapResponse.Load(sResponse);
}
catch (Exception ex)
{
//Catch logic
}
}
I can see the request and response passing back and forth with Fiddler, but I'm having trouble line-by-line debugging, since as soon as I hit the PostAsync, VS flips out and continues all the way to the end of the program, skipping all breakpoints. Meanwhile, the request times out with a TaskCanceledException, long after the code that should come afterwards already has completed.
I've looked a dozens of question & answers here on SO, and elsewhere, but they're just not quite helping find the problem. Most seem to center around the liberal sprinkling of ".ConfigureAwait(false)" on Async calls, but it doesn't really seem to have any effect.
So, with the help of #JSteward in the comments, he pointed out that async and Parrallel.ForEach aren't well suited to working together, since return types of void when dealing with async should be avoided.
He recommended that I use Async only, from the top (Button Click) to the bottom (Message Send), and that worked out. Credit to him for the guidance.
This link was helpful in explaining why this was the case: Async/Await - Best Practices in Asynchronous Programming
SendFiles ended up looking something like this:
private async Task<bool> SendFiles()
{
var result = _InfoCollection.Select(SendFile);
await Task.WhenAll(result).ConfigureAwait(false);
return true;
}
All the other methods went async, with appropriate awaits, and return types of Task or Task<T>.
I was reading the following topic http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
and decided to write a common utility method in my library to do a GET on remote url via HTTPClient
public static async Task<T> GetAsync<T>(HttpGetObject getObject)
{
string baseUrl = getObject.BaseUrl;
string actionUrl = getObject.ActionRelativeUrl;
string acceptType = getObject.AcceptType;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(baseUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(acceptType));
AddCustomHeadersToHttpClient(client, getObject);
// HTTP GET
HttpResponseMessage httpResponseMessage = await client.GetAsync(actionUrl).ConfigureAwait(false);
if (httpResponseMessage.IsSuccessStatusCode)
{
T response = await httpResponseMessage.Content.ReadAsAsync<T>().ConfigureAwait(false);
return response;
}
else
{
string message = httpResponseMessage.Content.ReadAsStringAsync().Result;
throw new Exception(message);
}
}
return default(T);
}
I know the "await httpResponseMessage.Content.ReadAsAsync().ConfigureAwait(false)" will prevent the deadlock in the above code
First:
My query is for "string message = httpResponseMessage.Content.ReadAsStringAsync().Result" line, will .Result can cause deadlock or not in that line?
Second:
If I call that code from UI like this:
public static object DoGet()
{
// Build getObject
var task = Utility.GetAsync(getObject);
task.Wait();
var response = task.Result;
return response;
}
Will that cause a deadlock?
Please note that I know to avoid all the mess with async-await, all the methods from UI to DAL must be async-await but I am not in position at this moment to change all that structure, my goal at this moment is to call HttpClient library and do a few GET operations.
So my questions is that will the above code can cause a deadlock?
Third:
Is task.Wait(); even needed in the above code?
In the general case, you should assume that yes, calling .Result or .Wait() on anything awaitable is dangerous and can deadlock (unless you are the library issuing the task, and you understand the full context). It is possible that it will work OK in some specific cases, but you should not rely on that behaviour, even if it works today.
I have a Web API service that is used to retrieve and update a specific set of data (MyDataSet objects), and I am running into some confusion in using async/await when performing the following events:
Update MyDataSet with new values
Get MyDataSet (but only after the new values have been updated)
In my client, I have something similar to the following:
Harmony.cs
private async Task<string> GetDataSet(string request)
{
using(var httpClient = new HttpClient())
{
httpClient.baseAddress = theBaseAddress;
HttpResponseMessage response = await httpClient.GetAsync(request);
response.EnsureSuccessStatusCode();
return response.Content.ReadAsStringAsync().Result;
}
}
private async Task PostDataSet<T>(string request, T data)
{
using (var httpClient = new HttpClient())
{
client.BaseAddress = new Uri(theBaseAddress);
HttpResponseMessage response = await client.PostAsJsonAsync<T>(request, data);
response.EnsureSuccessStatusCode();
}
}
internal MyDataSet GetMyDataSetById(int id)
{
string request = String.Format("api/MyDataSet/GetById/{0}", id);
return JsonConvert.DeserializeObject<MyDataSet>(GetDataSet(request).Result);
}
internal void UpdateDataSet(MyDataSet data)
{
PostDataSet("api/MyDataSet/Update", data);
}
HarmonyScheduler.cs
internal void ScheduleDataSet()
{
MyDataSet data = ...
harmony.UpdateDataSet(data);
MyDataSet data2 = harmony.GetMyDataSetById(data.Id);
}
There is a compiler warning in Harmony.cs UpdateDataSet because the call is not awaited and execution will continue before the call is completed. This is affecting the program execution, because data2 is being retrieved before the update is taking place.
If I were to make UpdateDataSet async and add an await to it, then it just moves things up the stack a level, and now HarmonyScheduler gets the warning about not being awaited.
How do I wait for the update to be complete before retrieving data2, so that I will have the updated values in the data2 object?
How do I wait for the update to be complete before retrieving data2,
so that I will have the updated values in the data2 object?
The thing I see many people don't comprehend is the fact that using the TAP with async-await will infect your code like a plague.
What do I mean by that?
Using the TAP will cause async to bubble up all the way to the top of your call stack, that is why they say async method go "all the way". That is the recommendation for using the pattern. Usually, that means that if you want to introduce an asynchronous API, you'll have to provide it along side a separate synchronous API. Most people try to mix and match between the two, but that causes a whole lot of trouble (and many SO questions).
In order to make things work properly, you'll have to turn UpdateDataSet and GetMyDataSetById to be async as well. The outcome should look like this:
private readonly HttpClient httpClient = new HttpClient();
private async Task<string> GetDataSetAsync(string request)
{
httpClient.BaseAddress = theBaseAddress;
HttpResponseMessage response = await httpClient.GetAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
private async Task PostDataSetAsync<T>(string request, T data)
{
client.BaseAddress = new Uri(theBaseAddress);
HttpResponseMessage response = await client.PostAsJsonAsync<T>(request, data);
response.EnsureSuccessStatusCode();
}
internal async Task<MyDataSet> GetMyDataSetByIdAsync(int id)
{
string request = String.Format("api/MyDataSet/GetById/{0}", id);
return JsonConvert.DeserializeObject<MyDataSet>(await GetDataSetAsync(request));
}
internal Task UpdateDataSetAsync(MyDataSet data)
{
return PostDataSetAsync("api/MyDataSet/Update", data);
}
Note - HttpClient is meant to be reused instead of a disposable, single call object. I would encapsulate as a class level field and reuse it.
If you want to expose a synchronous API, do so using a HTTP library that exposes a synchronous API, such as WebClient.
Always wait on the tasks right before you need its results.
Its always better to wait on a task if it contains an await in its body.
Warning : Do not use .Wait() or .Result
You would understand the concept better if you go through the control flow as explained Control Flow in Async Programs.
So in your case I would make all the functions accessing the async methods GetDataSet(string request) and PostDataSet<T>(string request, T data) with return type Task as awaitable.
Since you dont seem to expect any result back from the PostDataSet function you could just wait for it to complete.
PostDataSet("api/MyDataSet/Update", data).Wait();
I have an identical method in two of my WP8 apps. Given the same url and same device, the method works on one app, but not the other. In the failing app, GetAsync is hanging after it is called. No time out, no exception.
Here is the method in question.
private async Task<Byte[]> DownloadData(string uri)
{
byte[] myDataBuffer = null;
var newUri = new Uri(uri, UriKind.Absolute);
var myWebClient = new HttpClient();
var response = await myWebClient.GetAsync(newUri);
if (response.Content.Headers.ContentType.MediaType == "text/plain"
|| response.Content.Headers.ContentLength < 200)
{
throw new Exception(await response.Content.ReadAsStringAsync());
}
myDataBuffer = await response.Content.ReadAsByteArrayAsync();
return myDataBuffer;
}
This happens every time on one particular app, but not the other. Same device. Has anybody ever experienced this behavior? The url is valid, the code is identical. Is there a project setting somewhere that might affect this? I'm using the HttpClient in another portion of the failing app and it works there.
I can change the code to use a HttpWebRequest and that works fine. Just not the HttpClient.
I just now discovered that if I copy the method into my button_click handler, it works there too. Is there a problem having this method inside a separate class? That seems odd to me.
update
What seems to be breaking it is multiple layers of async methods calling it. Within the class I have
public override byte[] GetImageData(string imageUri)
{
return GetImageDataAsync(imageUri).Result;
}
public async Task<byte[]> GetImageDataAsync(string imageUri)
{
return await DownloadData(imageUri);
}
from my button_click handler, I'm calling GetImageData(uri). If I change that to await GetImageDataAsync(uri) it works.
Is Result not the correct property to reference in GetImageData?
Here's a test url "http://www.rei.com/pix/common/REI_logo.gif"
Calling Result or Wait can cause deadlocks, as I explain on my blog.
The proper way to solve this is to use await. I assume that there's some reason you want to synchronously block, but it's better to use await and find a way to make it work in your code.