Showing posts with label c#. Show all posts
Showing posts with label c#. Show all posts

Wednesday, December 31, 2008

Code Around C#'s Using Statement to Release Unmanaged Resources

uppose you've obtained unmanaged resources in a class constructor during object construction and the created object was provided to a using statement. However, the resources were not released at the end of the using statement's usage block. Instead, they were released during garbage collection, as you may expect. Sorting through the application's IL code with the DbgCLR debugger, you find that an exception was thrown from the class constructor. So, not only was the object not fully constructed, the Dispose() method was not called when the unmanaged resources were expected to be released. Thus, they were not released and contributed to the resource consumption.


Resource consumption may impede your application's performance metrics or may even prevent it from working at all.


Add try/catch blocks in the constructor to release unmanaged resources.

Finding the Solution
I created a small application to test the using statement's behavior and to investigate different ways to release the unmanaged resources acquired by the failed constructor.

Here's the code for the application:


static void Main(string[] args)
{
using(A a = new A()) // A : IDisposable implements the dispose pattern
{ //point1
. . .
} //point2
. . .
}
And here's the stripped IL code:

.locals init ([0] class MyApplication.A a)
IL_0000: newobj instance void MyApplication.A::.ctor()
IL_0005: stloc.0
.try
{
IL_0006: leave.s IL_0012
} // end .try
finally
{
IL_0008: ldloc.0
IL_0009: brfalse.s IL_0011
IL_000b: ldloc.0
IL_000c: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0011: endfinally
} // end handler
The IL code shows that, according to the C# specification, if an exception is thrown between point1 and point2, Dispose() is called on the instance of A and necessary clean up is performed. This clean up includes the release of unmanaged resources.

When the exception was thrown from the A() constructor, the above finally block was not executed. This is why the code should be updated to release the allocated resources that can be leaked.

Initial Attempts
My first strategy was to write an explicit try/catch block around the using statement and, from there, call a.Dispose(). Of course, this garnered a CS0246 compile time error because "the type or namespace name ‘a’ could not be found." Therefore, I decided to declare the a variable before the try block. The code compiled, but resulted in a NullReferenceException exception because "Object reference not set to an instance of an object."

Checking if a is not null before calling a.Dispose() eliminates the runtime error, but also abolishes the call to a.Dispose() because a, in this case, will definitely be null. Any other exercises that involved moving try\catch blocks and instantiating type a did not help either. Thus, it became evident that the mix of an explicit try\catch block was not getting along with the implicit try\finally block when both of them call a.Dispose().

An alternative solution to the problem was to add a try/catch block inside the A() constructor to guard places that may throw an exception. This guarding method releases previously acquired unmanaged resources in the constructor's catch block after the point of failure. It was a solution. I released unmanaged resources inside the failed constructor, rethrew the exception, and used the try\catch explicit block around the using statement.

http://www.devx.com/dotnet/Article/39023

Custom Search
Powered By Blogger