Tuesday, April 21, 2009

Synchronization and unit testing

There are a number of different ways to synchronize access to resources in .net, the most common ones are probably the Monitor class (used by the lock-keyword) and the ReaderWriterLock (or ReaderWriterLockSlim) classes, others are semaphores and mutex’s. When using TDD to develop you want to be able to test that your code is actually locking the resources it should but you don’t want to rely on spinning off threads to check if resources are locked or not in your unit tests (because it would actually make them not unit tests  but rather integration tests).What I’ve done is to create an interface that is called ISynchronizationManager and the public members of this interface I’ve actually more or less stolen from the ReaderWriterLockSlim class.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Legend.Threading
{
    /// <summary>
    /// Represents a manager that synchronizes
    /// access to a resource.
    /// </summary>
    public interface ISynchronizationManager
        : IDisposable 
    {
        /// <summary>
        /// Enters the read lock.
        /// </summary>
        void EnterReadLock();

        /// <summary>
        /// Exits the read lock.
        /// </summary>
        void ExitReadLock();

        /// <summary>
        /// Enters a synchonization managed section in
        /// write mode.
        /// </summary>
        void EnterWriteLock();

        /// <summary>
        /// Exits the write lock.
        /// </summary>
        void ExitWriteLock();

        /// <summary>
        /// Enters an upgradable read lock.
        /// </summary>
        void EnterUpgradableReadLock();

        /// <summary>
        /// Exits an upgradable read lock.
        /// </summary>
        void ExitUpgradableReadLock();

        /// <summary>
        /// Gets the recursion policy set on the lock.
        /// </summary>
        LockRecursionPolicy RecursionPolicy { get; }
    }
}

This lets me write unit tests that tests the interaction with this interface that I can easily mock with a mocking framework of choice (that’s Rhino Mocks to me). An example of how a test  in NUnit could look:

[Test]
public void should_acquire_write_lock_on_timerSynchronizationManager()
{
    scheduler.Schedule(voidAction, currentTime.AddMinutes(1));
    currentTime = currentTime.AddMinutes(1);

    timer.Raise(x => x.Elapsed += null, timer, EventArgs.Empty);

    synchronizationManager.AssertWasCalled(x => x.EnterWriteLock(), x => x.Repeat.Twice());
}

For production I have a couple of different implementations of this interface internally using monitors, ReaderWriterLockSlim and other ways of synchronizing. What’s nice it that I can inject the ISynchronizationManager as a dependency in the constructor of a class that needs to be synchronized and use an IoC container to register the synchronization manager to use.

I also created some extension methods for the ISynchronizationManager that lets you lock on it like this:

ISynchronizationManager manager = GetSynchronizationManager();
using (manager.AcquireReadLock())
{ 
    // read something from your shared resource...
}

To see this in action check out the Legend.Threading.Scheduler-class and the tests of it in the Legend project.

No comments:

Post a Comment