Friday, 21 September 2012

The Mistery of Failing Tests - Or how to implement IDisposable correctly

Some days ago my Visual Studio tests started failing randomly. Curiously, the same tests which failed in a previous run would succeed if ran again. There seemed to be nothing wrong with the tests, except for the message "The agent process was stopped while the test was running" appearing the Test Results window.









It turns out this message tends to appear whenever an object throws an exception inside its destructor.



Luckly, only about a dozen classes in Accord.NET do need to implement finalizers. After a few searches, it turned down the symptoms were being caused by a NullReferenceException being thrown in one of those classes.



The solution is to always implement the Disposable pattern correctly.




Disposing managed code correctly


To do so, first, implement IDisposable. Consider, for example, the Metronome class, which makes use of timers:







    /// <summary>
/// Virtual Metronome.
/// </summary>
///
/// <remarks>
/// Objects from this class acts as virtual metronomes. If connected
/// to a beat detector, it can be used to determine the tempo (in
/// beats per minute) of a signal. It can also be used in manual mode
/// by calling <see cref="Tap"/> method. For more details, see the
/// Beat detection sample application which comes together with the
/// framework.
/// </remarks>
///
public class Metronome : IDisposable
{
// ...

private Timer timeUp;
private Timer metronome;

// ...
}



Then, implement the required Dispose method, but do not write the dispose code right away. Instead, write:






        /// <summary>
/// Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.
/// </summary>
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
///
/// <param name="disposing"><c>true</c> to release both managed
/// and unmanaged resources; <c>false</c> to release only unmanaged
/// resources.</param>
///
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// free managed resources
if (timeUp != null)
{
timeUp.Dispose();
timeUp = null;
}
if (metronome != null)
{
metronome.Dispose();
metronome = null;
}
}
}



Always check if a member is not null before disposing it. Alternatively, after disposing, always set it to null so you can't accidentaly dispose it twice. The GC.SuppressFinalize instructs the Garbage Collector that the object has already been disposed, so it won't be required to call its finalizer. The GC will be trusting you have already cleaned the room; so don't fail on it!



Finally, write the finalizer as:







        /// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="Metronome"/> is reclaimed by garbage collection.
/// </summary>
///
~Metronome()
{
Dispose(false);
}






Disposing unmanaged code



The disposal of unmanaged code follows exactly the same pattern as above, but some care is required when disposing unmanaged resources. Consider the Signal class, which represents an audio signal in PCM format:








    /// <summary>
/// Represents a PCM sound discrete signal (measured in time).
/// A real discrete-time signal is defined as any real-valued
/// function of the integers.
/// </summary>
///
/// <remarks>
/// <para>
/// In signal processing, sampling is the reduction of a continuous
/// signal to a discrete signal. A common example is the conversion
/// of a sound wave (a continuous-time signal) to a sequence of samples
/// (a discrete-time signal).</para>
///
/// <para>
/// A sample refers to a value or set of values at a point in time
/// and/or space.</para>
///
/// </remarks>
///
public class Signal : IDisposable
{
// ...

private byte[] rawData;
private IntPtr ptrData;
private GCHandle handle;

private SampleFormat format;

private int channels;
private int sampleRate;
private int length;

// ...
}





Note that this class uses an unmanaged memory block to store the audio signal. To dispose it properly, the Disposable pattern has to be implemented as it follows:







        /// <summary>
/// Performs application-defined tasks associated with freeing,
/// releasing, or resetting unmanaged resources.
/// </summary>
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

/// <summary>
/// Releases unmanaged resources and performs other cleanup operations
/// before the <see cref="Signal"/> is reclaimed by garbage collection.
/// </summary>
///
~Signal()
{
Dispose(false);
}

/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
///
/// <param name="disposing"><c>true</c> to release both managed
/// and unmanaged resources; <c>false</c> to release only unmanaged
/// resources.</param>
///
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// free managed resources
}

// free native resources
if (handle.IsAllocated)
{
handle.Free();
ptrData = IntPtr.Zero;
}
}





Here we have almost the same pattern. However, note that unmanaged code should always be freed, even if Dispose has not been called explictly. So the code for releasing unmanaged resources is executed unconditionally in the Dispose(bool) method.

No comments:

Post a Comment