1
\$\begingroup\$

This is a continuation of the last thread.

I wanted to add a thread-safety and I was considering AwaitableCriticalSection and Task+Mutex. I ended up with mutex; it can be a bit slower but it would allow different files to be stored independently.

public static async Task<T> ReadJson<T>(String fileName)
{
    if (String.IsNullOrEmpty(fileName)) return default(T);

    return await TaskEx.Run(() =>
    {
        using (var myMutex = new Mutex(false, String.Format("{0}@{1}", AppUid, fileName)))
        {
            try
            {
                myMutex.WaitOne();
                using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                using (var stream = new IsolatedStorageFileStream(fileName, FileMode.Open, store))
                using (var sr = new StreamReader(stream))
                using (var jr = new JsonTextReader(sr))
                    return IsoStorageEx.jsonSerializer.Deserialize<T>(jr);
            }
            finally 
            { 
                myMutex.ReleaseMutex(); 
            }
        }
    });
}


public static async Task<bool> WriteJson<T>(String fileName, T content)
{
    if (String.IsNullOrEmpty(fileName))
        return false;

    return await TaskEx.Run(() =>
        {
            using (var myMutex = new Mutex(false, String.Format("{0}@{1}", AppUid, fileName)))
            {
                try
                {
                    myMutex.WaitOne();
                    using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                    {
                        store.CreateFolderIfNecessary(fileName);
                        using (var stream = new IsolatedStorageFileStream(fileName, FileMode.Create, FileAccess.Write, store))
                        using (var sw = new StreamWriter(stream))
                        using (var jw = new JsonTextWriter(sw))
                            IsoStorageEx.jsonSerializer.Serialize(jw, content);
                    }
                }
                finally 
                { 
                    myMutex.ReleaseMutex(); 
                }

                return true;
            }
        });
}

Where AppUid is just a random string, so mutexes of different apps would not collide.

Sample can be downloaded here.

Any suggestions?

\$\endgroup\$
1
\$\begingroup\$
public static async Task<T> ReadJson<T>(String fileName)

The Task-based asynchronous pattern says that the names of Task-returning methods should end with Async, e.g. ReadJsonAsync.


if (String.IsNullOrEmpty(fileName)) return default(T);

Why return the default value here? Empty fileName seems like a bug that should be discovered as soon as possible, so a more reasonable (and common) response would be to throw an exception.


TaskEx.Run

Usually, calling Task.Run shouldn't be done in a library, it should be done by the user. That way, the user will understand that the method is not truly asynchronous. But this library seems to be specifically meant to be called from the UI thread, so I guess it's okay here.


I don't quite understand the need for the Mutex here. Do you really expect to access the same file multiple times at the same time from one application? That doesn't sound like a common situation to me.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ 1. Nice point. Improved. 2. Also nice one, fixed. 3. I just want to be a safe with calls from any thread, and actually user shouldn't know what's happening inside. He just calls await IsoStorageManager.WriteJsonAsync(fileName1, list); and is happy to know that his data is stored. 4. Just continuation of previous point. Mutex is defense against calling ReadJsonAsync() too fast after WriteJsonAsync(). I got sometimes an exceptions, while storing and reading of the large files, so i just want to be sure again that it is stable enough. Does that make sense? \$\endgroup\$ – Vitalii Vasylenko May 17 '14 at 16:26
  • \$\begingroup\$ @VitaliiVasylenko If the user uses await correctly, then I think there should be no need for that Mutex. \$\endgroup\$ – svick May 17 '14 at 20:33
  • \$\begingroup\$ Just recalled another reason for Mutex: blogs.msdn.com/b/andy_wigley/archive/2013/07/31/… . Sometimes (in the events, especially lifecycle events) file saving should be called synchronously, and that can collide with loading or another (async) saving. \$\endgroup\$ – Vitalii Vasylenko May 17 '14 at 20:39
  • \$\begingroup\$ @VitaliiVasylenko I think it's a very good idea to document all such reasons (probably in a comment), especially for code that might look useless. \$\endgroup\$ – svick May 17 '14 at 20:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.