Hiding ReaderWriterLockSlim housekeeping in a using statement

Fredrik Mörk has an interesting post on using extension methods to hide housekeeping code related to protecting access to a share resource using a ReaderWriterLockSlim.

I recommend reading the article, but the short of it is that the standard try, lock, operate, finally, unlock process is moved to an extension method and the operate step is passed in at the call-site as a delegate using a lambda.

I like this method and this post is mostly just a jam session with Fredrik, I don’t know if you’ll prefer this method to his, but I did enough to write this post.

The general idea is to create a proxy object that implements IDisposable that can be wrapped in a using statement; when created this proxy will take the lock, and when disposed it will release it. We’re taking advantage of the using statement’s guarantee that it will always call Dispose on its owned object when execution leaves its scope, whether by successful program flow or by a thrown exception.

First the proxy objects:

public class ReadLockProxy : IDisposable {
ReaderWriterLockSlim _rwlock;
public ReadLockProxy(ReaderWriterLockSlim rwlock) {
_rwlock = rwlock;
_rwlock.EnterReadLock();
}

public void Dispose() {
_rwlock.ExitReadLock();
}
}

public class WriteLockProxy : IDisposable {
ReaderWriterLockSlim _rwlock;
public WriteLockProxy(ReaderWriterLockSlim rwlock) {
_rwlock = rwlock;
_rwlock.EnterWriteLock();
}

public void Dispose() {
_rwlock.ExitWriteLock();
}
}

public class UpgradeableReadLockProxy : IDisposable {
ReaderWriterLockSlim _rwlock;
public UpgradeableReadLockProxy(ReaderWriterLockSlim rwlock) {
_rwlock = rwlock;
_rwlock.EnterUpgradeableReadLock();
}

public void Dispose() {
_rwlock.ExitUpgradeableReadLock();
}
}

These proxies are enough to make use of our pattern; a usage (to borrow Fredrik’s example) would look like this:


private static int GetFromQueueWithProxies() {
int result = -1;

using(new UpgradeableReadLockProxy(_lock))
{
if (_sharedResource.Count > 0)
{
using(new WriteLockProxy(_lock))
{
result = _sharedResource.Dequeue();
}
}
}

return result;
}

This code is arguably a tad cleaner than passing a lambda into an extension method, but as far as mental workload is concerned, the new operator and passing in the lock object don’t seem like an equitable trade-off. Lets also take advantage of some extension methods to make this code even more straight forward.


public static class LockExtensions {
public static ReadLockProxy UseReadLock(this ReaderWriterLockSlim rwlock) {
return new ReadLockProxy(rwlock);
}

public static WriteLockProxy UseWriteLock(this ReaderWriterLockSlim rwlock) {
return new WriteLockProxy(rwlock);
}

public static UpgradeableReadLockProxy UseUpgradeableReadLock(this ReaderWriterLockSlim rwlock) {
return new UpgradeableReadLockProxy(rwlock);
}
}

I think these are pretty self explanitory, they just wrap the creation of the proxies into extension methods; let’s take a look at a revised example:


private static int GetFromQueueUsingExtensions() {
int result = -1;

using(_lock.UseUpgradeableReadLock())
{
if (_sharedResource.Count > 0)
{
using(_lock.UseWriteLock())
{
result = _sharedResource.Dequeue();
}
}
}

return result;
}

Ah, much better, we are able to make our code more readable and much more succinct by taking advantage of two C# language features, using statements and extension methods.

I hope you enjoyed this jam session, I know I did; big thanks to Fredrik for the initial riff!

This entry was posted in .NET, Software Development, Technology and tagged . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

3 Comments

  1. Posted April 6, 2011 at 12:43 am | Permalink

    Nice! I like the cleaner layout, since you got rid of the lambdas and anonymous methods. Some might say that this is abuse of IDisposable, but personally I like the creative use of it.

    I sometimes think about doing something with IDisposable to help restore resources to a previous state for use with unit tests (or perhaps rather integration tests). It could make a snapshot of the resource when the object is created, and then restore the snapshot on Dispose. I have this feeling that the interface is somewhat underused.

  2. Posted April 6, 2011 at 1:21 pm | Permalink

    Thanks Fredrik. Once I saw the framework itself start using IDisposable for deterministic scoping (e.g. TransactionScope) I figured it was fair game! I’ve used it for a large number of similar scoping tasks.

  3. Posted June 16, 2011 at 8:11 pm | Permalink

    Cool! I was myself thinking about wrapping a ReaderWriterLockSlim in such ‘autolock/unlock’ class last night and using a ‘Using’ statement to perform the autolock/unlock action. Great minds think alike, it seems ;)

    (Hats off to you though for using extensions to make things cleaner and more elegant. Didn’t think of that)

    That said, is there any drawback about this method? Besides the obvious possible object proliferation (which is not a problem; RAM is cheap these days).

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>