Working in High Performance C#, I always find it incredible that people seem to think that they do not have to worry about object lifetimes. The arguement is simple, “GC will handle it.”
The GC is not magic. The GC handles Memory Management.
This means that for a C# developer, you MUST handle the lifetime of the following items manually (this list is not exhaustive).
- Event Handlers
- Unmanaged Objects
- File Handlers
- Network Connections and Sockets
I talk about this because; recently, I had a strange bug that was absolutely destroying our build process.
NUnit Agent was complaining that an Exception occured when unloading AppDomain.
Unhandled Exception: NUnit.Engine.NUnitEngineException: Exception encountered unloading AppDomain Agent Process was terminated successfully after error. at NUnit.Engine.Runners.ProcessRunner.Dispose(Boolean disposing) at NUnit.Engine.Runners.AbstractTestRunner.Dispose() at NUnit.Engine.Runners.TestExecutionTask.Execute() at NUnit.Engine.Runners.ParallelTaskWorkerPool.ProcessTasksProc() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart()
Turns out, the issue was that a certain Reuters class was not being properly disposed.
You see the Reuters class registers callbacks for market events. When NUnit tries to unload the AppDomain, it throws an exception because the Reuters service has a reference to a callback handle in our AppDomain, keeping the AppDomain alive.
A simple using block fixed this bug.