Answer: Debugging a resource leak
As it turns out, there are a LOT of issues with this code:
public class QueueActions : IDisposable
{
UnmanagedDatabaseConnection database;
public string Name { get; private set; }
public class QueueActions( UnmanagedDatabaseConnectionFactory factory)
{
database = factory.Create();
database.Open(()=> Name = database.ReadName());
}
// assume proper GC finalizer impl
public void Dispose()
{
database.Dispose();
}
}
And the code using this:
using(var factory = CreateFactory())
{
ThreadPool.QueueUserWorkItem(()=>
{
using(var actions = new QueueActions(factory))
{
actions.Send("abc");
}
});
}
To begin with, what happens if we close the factory between the first and second lines in QueueActions constructors?
We already have an unmanaged resource, but when we try to open it, we are going to get an exception. Since the exception is thrown from the constructor, it will NOT invoke the usual using logic, and the code will not be disposed.
Furthermore, and the reason for the blog post about it. Dispose itself can also fail.
Here is the actual stack trace that caused this blog post:
Microsoft.Isam.Esent.Interop.EsentErrorException: Error TermInProgress (JET_errTermInProgress, Termination in progress)
at Microsoft.Isam.Esent.Interop.Api.Check(Int32 err) in Api.cs: line 1492
at Microsoft.Isam.Esent.Interop.Api.JetCloseTable(JET_SESID sesid, JET_TABLEID tableid) in Api.cs: line 372
at Microsoft.Isam.Esent.Interop.Table.ReleaseResource() in D:\Work\esent\EsentInterop\Table.cs: line 97
at Microsoft.Isam.Esent.Interop.EsentResource.Dispose() in EsentResource.cs: line 63
at Rhino.Queues.Storage.AbstractActions.Dispose() in AbstractActions.cs: line 146