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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<span>/// <summary></span> <span>/// Virtual Metronome.</span> <span>/// </summary></span> <span>/// </span> <span>/// <remarks></span> <span>/// Objects from this class acts as virtual metronomes. If connected</span> <span>/// to a beat detector, it can be used to determine the tempo (in</span> <span>/// beats per minute) of a signal. It can also be used in manual mode</span> <span>/// by calling <see cref="Tap"/> method. For more details, see the</span> <span>/// Beat detection sample application which comes together with the</span> <span>/// framework.</span> <span>/// </remarks></span> <span>/// </span> <span>public</span> <span>class</span> Metronome : IDisposable { <span>// ...</span> <span>private</span> Timer timeUp; <span>private</span> Timer metronome; <span>// ...</span> } |
Then, implement the required Dispose method, but do not write the dispose code right away. Instead, write:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<span>/// <summary></span> <span>/// Performs application-defined tasks associated with</span> <span>/// freeing, releasing, or resetting unmanaged resources.</span> <span>/// </summary></span> <span>/// </span> <span>public</span> <span>void</span> Dispose() { Dispose(<span>true</span>); GC.SuppressFinalize(<span>this</span>); } <span>/// <summary></span> <span>/// Releases unmanaged and - optionally - managed resources</span> <span>/// </summary></span> <span>/// </span> <span>/// <param name="disposing"><c>true</c> to release both managed</span> <span>/// and unmanaged resources; <c>false</c> to release only unmanaged</span> <span>/// resources.</param></span> <span>///</span> <span>protected</span> <span>virtual</span> <span>void</span> Dispose(<span>bool</span> disposing) { <span>if</span> (disposing) { <span>// free managed resources</span> <span>if</span> (timeUp != <span>null</span>) { timeUp.Dispose(); timeUp = <span>null</span>; } <span>if</span> (metronome != <span>null</span>) { metronome.Dispose(); metronome = <span>null</span>; } } } |
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:
1 2 3 4 5 6 7 8 9 |
<span>/// <summary></span> <span>/// Releases unmanaged resources and performs other cleanup operations before the</span> <span>/// <see cref="Metronome"/> is reclaimed by garbage collection.</span> <span>/// </summary></span> <span>/// </span> ~Metronome() { Dispose(<span>false</span>); } |
Disposing unmanaged code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<span>/// <summary></span> <span>/// Represents a PCM sound discrete signal (measured in time).</span> <span>/// A real discrete-time signal is defined as any real-valued </span> <span>/// function of the integers.</span> <span>/// </summary></span> <span>/// </span> <span>/// <remarks></span> <span>/// <para></span> <span>/// In signal processing, sampling is the reduction of a continuous</span> <span>/// signal to a discrete signal. A common example is the conversion</span> <span>/// of a sound wave (a continuous-time signal) to a sequence of samples</span> <span>/// (a discrete-time signal).</para></span> <span>/// </span> <span>/// <para></span> <span>/// A sample refers to a value or set of values at a point in time </span> <span>/// and/or space.</para></span> <span>///</span> <span>/// </remarks></span> <span>/// </span> <span>public</span> <span>class</span> Signal : IDisposable { <span>// ...</span> <span>private</span> <span>byte</span>[] rawData; <span>private</span> IntPtr ptrData; <span>private</span> GCHandle handle; <span>private</span> SampleFormat format; <span>private</span> <span>int</span> channels; <span>private</span> <span>int</span> sampleRate; <span>private</span> <span>int</span> length; <span>// ...</span> } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<span>/// <summary></span> <span>/// Performs application-defined tasks associated with freeing, </span> <span>/// releasing, or resetting unmanaged resources.</span> <span>/// </summary></span> <span>/// </span> <span>public</span> <span>void</span> Dispose() { Dispose(<span>true</span>); GC.SuppressFinalize(<span>this</span>); } <span>/// <summary></span> <span>/// Releases unmanaged resources and performs other cleanup operations </span> <span>/// before the <see cref="Signal"/> is reclaimed by garbage collection.</span> <span>/// </summary></span> <span>/// </span> ~Signal() { Dispose(<span>false</span>); } <span>/// <summary></span> <span>/// Releases unmanaged and - optionally - managed resources</span> <span>/// </summary></span> <span>/// </span> <span>/// <param name="disposing"><c>true</c> to release both managed</span> <span>/// and unmanaged resources; <c>false</c> to release only unmanaged</span> <span>/// resources.</param></span> <span>///</span> <span>protected</span> <span>virtual</span> <span>void</span> Dispose(<span>bool</span> disposing) { <span>if</span> (disposing) { <span>// free managed resources</span> } <span>// free native resources</span> <span>if</span> (handle.IsAllocated) { handle.Free(); ptrData = IntPtr.Zero; } } |
1 |
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.