Locking asynchronous file operations in Jerry Nixon’s StorageHelper

Refering to
Jerry Nixon’s blog and
StorageHelper and
Stephen Toub’s AsyncLock

When I was writing my first Windows 8 app store application, I was also for the first time using the async/await
feature introduced in .net 4.5.

Using async/await makes a lot of things very simple to write, easy to read later and keeps the application’s user interface (UI) responsive. But in some scenarios a re-entrance situation arises that you are not normally confronted with when doing things just one after the other.

I think file operations are a good example for this re-entrance; particularly when they are executed from within event  handlers.
When handling events that get triggered from the UI, like for example a button click,  it is fairly common to disable
the button as one of the first statements in the event handler and enable it again when the event handler returns, at least in asynchronous mode. This pattern works fine as long as there is no other button that will call the same code, or for that matter code that uses the same resources, like a file. (I am not talking about MVVM patterns here, so I ignore how buttons are disabled via MVVM.)

One problem with calling things asynchronously is, that while awaiting an asynchronous function call, other stuff may happen and you have little idea what that other stuff will be.

A situation I came across is this:

·        A Windows app store application has two frames.

·        There is no given sequence in which these frames
are called.

·        Both frames need some data that comes from a
file.

·        The data needs to be written back into that file
when the frames close (are navigated from).

·        Both frames use the same data and therefore the
same file.

Typically, I would put the loading and saving of the data into OnNavigatedTo/From event handlers for each frame.

In the pre-async/await times, if you don’t worry about a “little” delay for the saving and loading, putting these calls directly into the event handlers is no problem, it just slows down the UI going from one frame to the next. But if the file access takes more time, the delay becomes an issue.  Also, the situation in Windows 8 app store apps is, that there are no synchronous versions of file access functions. So you are forced (by design) to do it asynchronously to keep the UI responsive, hence the event handlers become async.

The signature of the event handlers however is void and the event handlers will not be awaited by the framework. So they become -what Jerry Nixon calls-  “FireAndForget” as soon as the signature is changed to async and an await is added to the function body:  They return when the first await within the functions body is hit. When navigating from one frame to the other, the saving and the loading code may overlap and throw an ACCESSDENIED exception.

In the pre-async/await days you could have used a thread or task to do the file access asynchronously and keep the UI responsive. With the async/await feature this is not necessary anymore, because now you can just use the async file access functions and await them. In the pre-async/await days it would have been a common approach to put a lock around the file access to handle the situation that two threads try to execute the same code and access the same file at the same time.  With async/await locking is still an issue, but trying to put a lock around an await function call will give an error, saying that this is not allowed. Also a lock would not work in a re-entrance scenario, because the lock is still on the same thread (the UI thread in the case of event handlers).

To get around this problem I used the AsyncLock from Stephen Toub (http://blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266988.aspx Building Async Coordination Primitives, Part 6: AsyncLock) to lock via a using block around any file access the StorageHelper functions perform. Please note that my contribution here is rather small.


First I bring in the two classes from Stephen Toub’s articles:

AsyncSemaphore and AsyncLock

Then in Jerry Nixon’s StorageHelper class I add a member:

private static readonly AsyncLock m_lock = new AsyncLock();

this is my only lock that I then use to synchronize all file access.

Writing a file for example:

using (await m_lock.LockAsync())

                {

                                   // now Jerry’s code

                    // create file

var _File =
await CreateFileAsync(key,
location,

CreationCollisionOption.ReplaceExisting);

                    // convert to string

                    var _String = Serialize(value);

                    // save string to file

                    await Windows.Storage.FileIO.WriteTextAsync(_File, _String);

                    // result

   ret=await FileExistsAsync(key,location);

}

and reading a file

using (await m_lock.LockAsync())

                {

                                  // now Jerry’s code

                    //fetch file

                    var _File = await GetIfFileExistsAsync(key,location);

                    if (_File == null)

                        returndefault(T);

                    // read content

                    var _String = await FileIO.ReadTextAsync(_File);

                    // convert to obj

                    var _Result = Deserialize<T>(_String);

                    return _Result;

                }

The using also takes care about the possibility of an exception being thrown, so the AsyncLock m_lock will be released if this should happen.

Please note that disabling parts of the UI in both frames may still be needed because – as already outlined above – the OnNavigatedTo event handler becomes “FireAndForget” when async and await are added to the definition. So as long as the fired and forgotten event handler has not done its work, the data it is supposed to read, may not be consistent.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s