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
Comments
No comments posted yet.