As you have no catch block, there is no guarantee that finally will be executed. From MSDN - try-finally (C# Reference) and "Locks and exceptions do not mix" (Eric Lippert)
Within a handled exception, the associated finally block is guaranteed
to be run. However, if the exception is unhandled, execution of the
finally block is dependent on how the exception unwind operation is
triggered. That, in turn, is dependent on how your computer is set up.
And from the link that is then mentioned (Unhandled Exception Processing In The CLR) there are then various considerations which could mean you end up with a terminated thread. I don't honestly know if that would leave you with a lock on the lock object though.
If you wanted to ensure that:
- you release the lock; but
- you don't want to handle the exception at this level, instead you want it handled by a higher level exception handler
Then do:
TheXmlType xml = null;
Monitor.Enter(Lock);
bool inLock = true;
try {
...
xml = filter.Xml; // put this here in case it throws an exception
inLock = false; // set this here in case monitor.exit is to
// throw an exception so we don't do the same all over again in the catch block
Monitor.Exit(Lock);
return xml; // this is fine here, but i would normally put it outside my try
}
catch (Exception) {
if (inLock) Monitor.Exit(Lock);
throw;
}
But, please note: don't use catch (Exception)
to hide exceptions, this is only ok if you are re-throwing the exception. People also recommend you have a single return
statement, and usually this would be outside your try block.
EDIT:
Confirmed with a test program, and from MSDN - Exceptions in Managed Threads
Starting with the .NET Framework version 2.0, the common language
runtime allows most unhandled exceptions in threads to proceed
naturally. In most cases this means that the unhandled exception
causes the application to terminate.
So, if you don't handle your exception, your application will crash (and you won't have to worry about the lock). If you do handle it, then your original code would execute it's finally block and you are ok.
EDIT 2: Test code updated as it did not properly illustrate a non-firing finally:
class Program
{
static void Main(string[] args) {
Program p =new Program();
p.Start();
Console.WriteLine("done, press enter to finish");
Console.ReadLine();
}
private readonly object SyncRoot = new object();
ManualResetEvent mre = new ManualResetEvent(false);
private void Start() {
/*
* The application will run the thread, which throws an exception
* While Windows kicks in to deal with it and terminate the app, we still get
* a couple of "Failed to lock" messages
* */
Thread t1 = new Thread(SetLockAndTerminate);
t1.Start();
mre.WaitOne();
for (int i = 0; i < 10; i++) {
if (!Monitor.TryEnter(this.SyncRoot, 1000)) {
Console.WriteLine("Failed to lock");
}
else {
Console.WriteLine("lock succeeded");
return;
}
}
Console.WriteLine("FINALLY NOT CALLED");
}
public int CauseAnOverflow(int i)
{
return CauseAnOverflow(i + 1);
}
public void SetLockAndTerminate() {
Monitor.Enter(this.SyncRoot);
Console.WriteLine("Entered");
try {
mre.Set();
CauseAnOverflow(1); // Cause a stack overflow, prevents finally firing
}
finally {
Console.WriteLine("Exiting");
Monitor.Exit(this.SyncRoot);
}
}
}