Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Async Deadlock?

I am fairly positive that I am creating a deadlock in my application and I am not sure how to resolve the issue. I have a few moving parts and am new to async and await so please bear with me.

I have a client that uploads a file as follows:

public static async Task<string> UploadToService(HttpPostedFile file, string authCode, int id)
{
    var memoryStream = new MemoryStream();
    file.InputStream.CopyTo(memoryStream);

    var requestContent = new MultipartFormDataContent();
    var fileContent = new ByteArrayContent(memoryStream.ToArray());
    fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse(file.ContentType);
    requestContent.Add(fileContent, "file", file.FileName);

    using (var httpClient = new HttpClient())
    {
        httpClient.BaseAddress = new Uri(BaseUrl);
        httpClient.DefaultRequestHeaders.Accept.Clear();

        var message =
            await
                httpClient.PostAsync(
                    string.Format("Upload?authCode={0}&id={1}", authCode, id),
                    requestContent);

        return await message.Content.ReadAsStringAsync();
    }
}

The piece receiving the file:

[HttpPost]
public Task<HttpResponseMessage> Upload(string authCode, int id)
{
    var request = Request;

    var provider = new CustomMultipartFormDataStreamProvider(root);

    var task =
        request.Content.ReadAsMultipartAsync(provider)
            .ContinueWith(o =>
            {
                // ...
                // Save file
                // ...

                return new HttpResponseMessage()
                {
                    Content = new StringContent("File uploaded successfully"),
                    StatusCode = HttpStatusCode.OK
                };
            });

    return task;
}

It is all kicked off with the following:

protected void Page_Load(object sender, EventArgs e)
{
    if (IsPostBack)
    {
        var file = HttpContext.Current.Request.Files[0];

        var response = UploadToService(file, hiddenAuthCode.Value, int.Parse(hiddenId.Value));
    }
}

Everything appears to be working except that the PostAsync never recognizes that task has been returned. I can see that the status of the await ... PostAsync ... task is WaitingForActivation but I'm not entirely sure what that means (remember, I'm a n00b to this stuff). My file is saved to the correct location but the application never recognizes the response from my service.

If someone could point me in the right direction, it would be much appreciated.

like image 398
Joe Avatar asked Jul 22 '14 05:07

Joe


1 Answers

I think the problem is that you simply call UploadToService in a fire-and-forget manner inside Page_Load. The request processing simply ends before this task has completed.

You should use <%@ Page Async="true" ...> in this WebForms page and include <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" /> in your web.config there.

Then use RegisterAsyncTask, the code would look like this:

protected void Page_Load(object sender, EventArgs e)
{
    if (IsPostBack)
    {
        var file = HttpContext.Current.Request.Files[0];

        RegisterAsyncTask(new PageAsyncTask(() => UploadToService(file, 
            hiddenAuthCode.Value, int.Parse(hiddenId.Value))));
    }
}

On a side note, you could improve this part:

file.InputStream.CopyTo(memoryStream);

like this:

await file.InputStream.CopyToAsync(memoryStream);

and replace ContinueWith with async/await:

[HttpPost]
public async Task<HttpResponseMessage> Upload(string authCode, int id)
{
    var request = Request;

    var provider = new CustomMultipartFormDataStreamProvider(root);

    await request.Content.ReadAsMultipartAsync(provider);

    return new HttpResponseMessage()
    {
        Content = new StringContent("File uploaded successfully"),
        StatusCode = HttpStatusCode.OK
    };
}

Related: "Using Asynchronous Methods in ASP.NET 4.5".

like image 55
noseratio Avatar answered Oct 01 '22 13:10

noseratio