4
votes

I am trying to secure my isolated storage with mutex so I can access it from both mobile application and BackgroundAudioPlayer.

These are my helpers class to access files in isostorage:

    public static async Task WriteToFile(string text)
    {
        using (var mut = new Mutex(false, "IsoStorageMutex"))
        {
            mut.WaitOne();
            try
            {
                // Get the text data from the textbox. 
                byte[] fileBytes = System.Text.Encoding.UTF8.GetBytes(text.ToCharArray());

                // Get the local folder.
                var local = ApplicationData.Current.LocalFolder;

                // Create a new folder name DataFolder.
                var dataFolder = await local.CreateFolderAsync("MusicFolder",
                                                               CreationCollisionOption.OpenIfExists);

                // Create a new file named DataFile.txt.
                var file = await dataFolder.CreateFileAsync("Streams.txt",
                                                            CreationCollisionOption.ReplaceExisting);

                // Write the data from the textbox.
                using (var s = await file.OpenStreamForWriteAsync())
                {
                    s.Write(fileBytes, 0, fileBytes.Length);
                }
            }
            finally
            {
                mut.ReleaseMutex();
            }
        }
    }

    public static async Task<string> ReadFile()
    {
        using (var mut = new Mutex(false, "IsoStorageMutex"))
        {
              mut.WaitOne();
              var result = String.Empty;
              try
              {
                  // Get the local folder.
                  var local = ApplicationData.Current.LocalFolder;

                  if (local != null)
                  {
                      // Get the DataFolder folder.
                      var dataFolder = await local.GetFolderAsync("MusicFolder");

                      // Get the file.
                      var file = await dataFolder.OpenStreamForReadAsync("Streams.txt");

                      // Read the data.

                      using (var streamReader = new StreamReader(file))
                      {
                          result = streamReader.ReadToEnd();
                      }
                  }
              }
              finally
              {
                  mut.ReleaseMutex();
              }
              return result;
        }
    }

But when I try to access it in Background Agent I get this error:

 Object synchronization method was called from an unsynchronized block of code.

StackTrace:

   at System.Threading.Mutex.ReleaseMutex()
   at YouRadio.IsolatedStorage.StorageHelpers.<ReadFile>d__b.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at YouRadio.AudioPlaybackAgent.AudioPlayer.<AddTracksFromIsoStorageToPlaylist>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at YouRadio.AudioPlaybackAgent.AudioPlayer.<OnUserAction>d__2.MoveNext()

What I am doing wrong?

1
Looks like a byproduct with async/await. Any reason you are using the ApplicationData API rather than IsolateStorage APIs? Here is my take on file storage codepaste.net/63vh4oShawn Kendrot

1 Answers

1
votes

You are implementing Mutex the wrong way. You should have a global instance of it created in your IsolatedStorageHelper class and your methods using that instance.

public class IsolatedStorageHelper
{
    private static Mutex mut = new Mutex(false, "IsoStorageMutex");

    public static async Task WriteToFile(string text)
    {
        mut.WaitOne();
        try
        {
            ...
        }
        finally
        {
            mut.ReleaseMutex();
        }
    }

    public static async Task<string> ReadFile()
    {
          mut.WaitOne();
          var result = String.Empty;
          try
          {
              ...
          }
          finally
          {
              mut.ReleaseMutex();
          }
          return result;
    }
}

A mutex has thread-affinity, the owner of a mutex is a thread. The thread that acquired it must also be the one that calls ReleaseMutex(). Breaking which makes this nasty exception to be thrown.

Depending on your requirements you may also have different mutex for reading and writing file. Also if all the methods are static in your class, you may make it a singleton class. That way you can have a private constructor to initialize mutex and other stuff.