Daniel Armstrong
Daniel Armstrong

Reputation: 849

What is the correct way to avoid a deadlock during windows store async file methods

I am trying to write a function to load some data from the roaming folder on a windows 8 store app.

I get a deadlock (hang) on roamingLoad.Wait() in the Load method;

What is the correct way to do this? What is being done wrong below?

Edit: I should point out that if I step though it in the debugger it works perfectly.

And is there a way to do it without the top method becomeing async? If I do that then every calling method must say await to block until it's done no?

    public override bool Load(string in_FileNameAndDirectory, StreamTask in_StreamTask, bool in_PreferRoaming)
    {
        if (PreferRoaming && SupportsRoamingSave)
        {
            try
            {
                Task<bool> roamingLoad = RoamingLoadAsync(in_FileNameAndDirectory, in_StreamTask);
                roamingLoad.Wait();
                return roamingLoad.Result;
            }
            catch (Exception error)
            {
                return false;
            }
        }
        return Load(in_FileNameAndDirectory, in_StreamTask);
    }


    private async Task<bool> RoamingLoadAsync(string inFileNameAndDirectory, StreamTask inStreamTask)
    {
        try
        {
            StorageFolder roamingFolder = ApplicationData.Current.RoamingFolder;
            StorageFile sampleFile = await roamingFolder.GetFileAsync(inFileNameAndDirectory);

            using (Stream stream = await sampleFile.OpenStreamForReadAsync())
                inStreamTask(stream);
        }
        catch (Exception error)
        {
            return false;
        }
        return true;
    }

Upvotes: 1

Views: 130

Answers (1)

Stephen Cleary
Stephen Cleary

Reputation: 456987

What is the correct way to do this?

You're running into a common deadlock problem (that I explain in full on my blog). To summarize, you should not use Task.Wait or Task<T>.Result on an asynchronous task. Instead, you should use await.

In order to use await, your method must be async. This means that this signature is just fundamentally incompatible with asynchronous operations:

public override bool Load(string in_FileNameAndDirectory, StreamTask in_StreamTask, bool in_PreferRoaming)

Assuming that the bool result is in fact important (which is certainly debatable - returning bool results instead of exceptions is kind of '80s, wouldn't you say?), the correct way to write this is to change the base class signature so that it supports asynchronous operations:

public override async Task<bool> LoadAsync(string in_FileNameAndDirectory, StreamTask in_StreamTask, bool in_PreferRoaming)
{
  if (PreferRoaming && SupportsRoamingSave)
  {
    try
    {
      return await RoamingLoadAsync(in_FileNameAndDirectory, in_StreamTask);
    }
    catch (Exception error)
    {
      return false;
    }
  }
  return Load(in_FileNameAndDirectory, in_StreamTask);
}

Upvotes: 1

Related Questions