I answered you can guarantee that the using
statement will always call Dispose
and I stand corrected, I was wrong.
There is a potential race condition with the using
statement that doesn't guarantee disposing and I've put together a console app illustrating this (which isn't hard or trivial).
I was correct when showing how the IL generates using
like so:
var session = new Session(); //If this causes an error or abort happens during initialization then we don't enter try
//If abort is called here then we never enter try
//In either case above we may have undisposed resources initialized at this point
try
{
//do stuff
}
finally
{
session.Dispose();
}
However; note the comments where I show the race condition that may occur if aborted before entering try
.
Here is a console app written just to prove this point. The first works as expected but if you add the commented out code //thread.Abort()
when we initialize R
then you will see it init but never dispose :/
using System;
using System.Threading;
namespace Question_Answer_Console_App
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Start Main");
Thread thread = null;
thread = new Thread(new ThreadStart(() =>
{
Console.WriteLine("Thread Started");
using (var r = new R(thread))
{
Console.WriteLine($"Using {nameof(R)}");
}
}));
thread.Start();
thread.Join();
Console.WriteLine("End Main");
Console.ReadKey();
}
}
public class R : IDisposable
{
public R(Thread thread)
{
Console.WriteLine($"Init {nameof(R)}");
//thread.Abort();
}
public void Dispose()
{
Console.WriteLine($"Disposed {nameof(R)}");
}
}
}
Output with //thread.Abort()
commented out:
Start Main
Thread Started
Init R
Using R
Disposed R
End Main
Output with thread.Abort()
not commented out:
Start Main
Thread Started
Init R
End Main