Friday 24 February 2012

IsolatedStorage and Thread Safety

In the documentation of the IsolatedStorageFile class, Microsoft say this:

Any instance members are not guaranteed to be thread safe.

Normally, members not being guaranteed to be thread safe means that when you have an object, the object cannot be shared across threads and have members called from more than one thread.

I suspect either that is not what is intended, or there’s a serious bug.

If you’ve followed my two previous posts today, you’ll notice that I’ve been trying to read from IsolatedStorage on a ThreadPool thread, and was debating whether I needed to create a separate IsolatedStorageFile for each thread.

Interepreting the statement in the documentation strictly implies that I should call GetUserStoreForApplication to get a new instance whenever I want to examine isolated storage.

Now, imagine my surprise when I fire up more than one thread pool thread, and get an exception when calling GetFileNames with a set of non-overlapping folders as follows:

System.IO.IsolatedStorage.IsolatedStorageException

An error occurred while accessing IsolatedStorage.at System.IO.IsolatedStorage.IsolatedStorageFile.EnsureStoreIsValid()
at System.IO.IsolatedStorage.IsolatedStorageFile.GetFileNames(String searchPattern)
at App.Model.DaySummary.BackgroundProcess()
at Microsoft.Phone.Reactive.Observable.<>c__DisplayClassa0.<>c__DisplayClassa2.<ToAsync>b__9f()
at Microsoft.Phone.Reactive.ThreadPoolScheduler.<>c__DisplayClass1.<Schedule>b__0(Object _)
at System.Threading.ThreadPool.WorkItem.WaitCallback_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadPool.WorkItem.doWork(Object o)
at System.Threading.Timer.ring()

So it now looks like I need to protect this with some kind of lock to prevent more than one object accessing any aspect of IsolatedStorage at a time.  Adding a lock around the creation of the IsolatedStorage object certainly prevents the error.

Addendum

Well, it looks like I had a bug.  When I rewrote my wrapper class for getting the IsolatedStorageFile via GetUserStoreForApplication, I inadvertently forgot to remove the static keyword from the variable holding it.  As a result, I was using the same object across threads.

In that situation, as you would expect, the object failed, as it is not guaranteed thread safe.  Removing the static keyword is all it took to fix the problem, so for the moment it looks like separate instances can be used at will on a single thread without regard to what else is accessing isolated storage (from a thread safety point anyway).

update March 1 2012

Quite a lot of discussion has happened on the AppHub forums on this since it was originally written, concerning thread safety of the entire platform.

If you’re interested, the whole discussion is here.

Asynchronous actions with RX

I was trying to come up with a syntactically tidy way to run some code in the ThreadPool.

Assume I have a function as follows that performs the work:

void PopulateIsolatedStorageList()
{
using (iso = IsolatedStorageFile.GetUserStoreForApplication())
{
string[] found = iso.ISO.GetDirectoryNames("Logs/*.*");
foreach (string s in found)
{
// do some time consuming processing here
}
}
}


First I attempted to create an Action delegate for the above, and then tried to do this:


Action b = this.PopulateIsolatedStorageList;
Observable.FromAsyncPattern(b.BeginInvoke, b.EndInvoke )().
ObserveOn(Scheduler.ThreadPool).Subscribe();



This did not work.  First, PopulateIsolatedStorageList ended up with a null this pointer.


Secondly, it seemed to run on the main thread in any case.





A bit of lateral thinking required


What are my goals?



  1. I want to run my action on a worker thread.

  2. I want to perform some completed action on the UI thread (namely OnPropertyChanged).

Why not create an enumeration of things I want to get done on the worker thread then?


Action[] a = { PopulateIsolatedStorageList};
a.ToObservable(Scheduler.ThreadPool).Subscribe
( action => action(),
      () => Deployment.Current.Dispatcher.BeginInvoke(() => UINotify() ));


Where UINotify() is the expression to perform on the main thread.

 

And surprisingly, it all works.

Update – It helps if you find the right method


Having searched some more I found another mechanism, which removes the need to create the array, and is probably the proper way to do it.


Observable.ToAsync(PopulateIsolatedStorageList)()
.Subscribe(
     _ => { },
() => Deployment.Current.Dispatcher.BeginInvoke(() => UINotify() ));


IsolatedStorage performance tests on WP7

The documentation on IsolatedStorage seems to be incomplete on the subject of when you should open and close the ApplicationStore, particularly since the only the public static members are thread safe.

It would appear that the best performance might be to open the IsolatedStorage using IsolatedStorageFile.GetUserStoreForApplication() once for the application, and share the instance, but what operations are allowed across multiple threads if you do?  Well, that isn’t clear.

So, the safest thing to do is to open one every time you need it, and dispose of it when finished.

I’ve already verified that each time you call GetUserStoreForApplication, a different object is returned, and built a harness that does this:

isf = IsolatedStorageFile.GetUserStoreForApplication();

int v = System.Environment.TickCount;

for (int i = 0;i<1000;i++)
{

CheckFolderBare("Shared");
}

int v2 = System.Environment.TickCount - v;
v = System.Environment.TickCount;

for (int i= 0;i<1000;i++)
{
CheckFolderSlow("Shared");
}
int v3 = System.Environment.TickCount - v;

MessageBox.Show(string.Format("Fast: {0}ms\nSlow {1}ms", v2, v3));


My Implementations of CheckFolderBare and CheckFolderSlow are this:


private bool CheckFolderBare(string s)
{
return isf.DirectoryExists(s);
}

private bool CheckFolderSlow(string s)
{
using (var isf2 = IsolatedStorageFile.GetUserStoreForApplication())
{
return isf2.DirectoryExists(s);
}
}

 

Not surprisingly, my initial suspicions were correct.  It isn’t particularly cheap to create and dispose of the file (taking about 1.3ms).  Timings were:

Fast: 2787ms

Slow: 4086ms

 

How about enumerating the folders?  To do this, I create a folder in IsolatedStorage named Logs, and created in that folder 7 additional folders, and then ran these functions 1000 times:

private string[] FoldersSlow()
{
using (var isf2 = IsolatedStorageFile.GetUserStoreForApplication())
{
return isf2.GetDirectoryNames("Logs/*.*");
}
}

private string[] FoldersFast()
{
return isf.GetDirectoryNames("Logs/*.*");
}



Results differed by about 2.5ms:


Fast: 7619ms
Slow: 9100ms


So, to make things more interesting, I decided to create and delete a folder on each operation, so on odd calls, I remove the folder Logs/Bogus and on even calls I created it.


Surprisingly, I ended up with times differing by 1.3ms, and in some cases, not showing any difference at all.


Fast: 16406ms
Slow: 17542ms


Verdict?


On balance, I’d say there is no real performance penalty by opening and closing the IsolatedStorage in a realistic usage scenario.

Monday 6 February 2012

HTC Surround Windows Phone 7 takes terrible photos

I took my phone away this weekend, and since it has a 5MP camera, I thought I’d take a picture while in Wales away from the snow.

Here’s how it turned out:

WP_000047

Admittedly I zoomed in a couple of times, but this is horrible.  If you click the above image, you can see the full horror of it.  The image for some reason came out at 686KB. 

I have a Canon A400, that is 3.2 MP and it’s images are usually on the order of 1.0MB.

I’m currently trying to find out why the compression effects are so bad.