IanG on Tap

Ian Griffiths in Weblog Form (RSS 2.0)

Blog Navigation

April (2018)

(1 item)

August (2014)

(1 item)

July (2014)

(5 items)

April (2014)

(1 item)

March (2014)

(1 item)

January (2014)

(2 items)

November (2013)

(2 items)

July (2013)

(4 items)

April (2013)

(1 item)

February (2013)

(6 items)

September (2011)

(2 items)

November (2010)

(4 items)

September (2010)

(1 item)

August (2010)

(4 items)

July (2010)

(2 items)

September (2009)

(1 item)

June (2009)

(1 item)

April (2009)

(1 item)

November (2008)

(1 item)

October (2008)

(1 item)

September (2008)

(1 item)

July (2008)

(1 item)

June (2008)

(1 item)

May (2008)

(2 items)

April (2008)

(2 items)

March (2008)

(5 items)

January (2008)

(3 items)

December (2007)

(1 item)

November (2007)

(1 item)

October (2007)

(1 item)

September (2007)

(3 items)

August (2007)

(1 item)

July (2007)

(1 item)

June (2007)

(2 items)

May (2007)

(8 items)

April (2007)

(2 items)

March (2007)

(7 items)

February (2007)

(2 items)

January (2007)

(2 items)

November (2006)

(1 item)

October (2006)

(2 items)

September (2006)

(1 item)

June (2006)

(2 items)

May (2006)

(4 items)

April (2006)

(1 item)

March (2006)

(5 items)

January (2006)

(1 item)

December (2005)

(3 items)

November (2005)

(2 items)

October (2005)

(2 items)

September (2005)

(8 items)

August (2005)

(7 items)

June (2005)

(3 items)

May (2005)

(7 items)

April (2005)

(6 items)

March (2005)

(1 item)

February (2005)

(2 items)

January (2005)

(5 items)

December (2004)

(5 items)

November (2004)

(7 items)

October (2004)

(3 items)

September (2004)

(7 items)

August (2004)

(16 items)

July (2004)

(10 items)

June (2004)

(27 items)

May (2004)

(15 items)

April (2004)

(15 items)

March (2004)

(13 items)

February (2004)

(16 items)

January (2004)

(15 items)

Blog Home

RSS 2.0

Writing

Programming C# 5.0

Programming WPF

Other Sites

Interact Software

C# 'lock' Keyword and JIT Hacks

Tuesday 12 April, 2005, 10:20 AM

I just read this article by Joe Duffy about dealing with asynchronous exceptions.

He provides an interesting recommendation:

"Start paired operations inside a try block when possible."

In this context, 'paired operations' are ones where if the first operation executes, you always want the second one to execute too. A good example would be lock acquisition and release - if you acquire a lock, you will want to be releasing it at some point. This is exactly the scenario in which you use the C# using statement, or a try...finally pair of blocks.

Joe's recommendation says that the first operation of the pair should be inside the try block. In the context of a using statement, that means inside the block - the using statement expands the block of code you supply into a try block, and it generates the finally block for you.

The reason for putting the first of the paired operations into the try block is entirely to deal with the possibility of asynchronous exceptions. (Principally thread aborts, which as you know, are evil. But they are a fact of life at AppDomain shutdown time.) To see why putting the code outside of the try block could be a problem, consider this example:

First();
// What if thread abort occurs here?
try
{
    DoStuff();
}
finally
{
    Last();
}

Asynchronous exceptions can occur at any time. If one occurs on the comment line above, then clearly the First() method will have been executed, but the Last() will not have - the exception occurred before the try block was entered, so the finally block will never run.

Joe's article recommends this instead:

try
{
    // (2)
    First();
    // (1)
    DoStuff();
}
finally
{
    Last();
}

This solves the problem of the previous example. If an asynchronous exception occurs immediately after First() runs on line (1), Last() still runs.

But what if an exception occurs on line (2)? Or if one occurs in the middle of First()? In this case Last() will run even though First() may not have run, or may have run only partially. So to use this idiom, you need to make sure that the second of your paired operations behaves correctly if it is called even when the first operation was not.

Compiler Support

The most inconvenient aspect of trying to follow this style is that there is no compiler support. We often get the using keyword to generate finally blocks for us whenever deterministic cleanup is required, but the using statement puts the initialisation code before the try block. The only way to stick to Joe's recommendation here is to go back to coding try...finally blocks by hand - the using statement can't help us here.

Joe points out that his recommendation "deviate[s] from guidance given in the past". And the using block construct is based on the old guidance not this new guidance. But curiously, Joe goes on to recommend that you use using or lock blocks. (And to be fair, he also calls out the fact that this appears to contradict the previous recommendation.)

The lock block turns out to be particularly interesting. By all rights it should suffer from the same problem as the using statement because it generates very similar code. In particular, it puts the lock acquisition outside of the try block. But apparently there is a "JIT hack" which recognizes when you are using a lock statement (or equivalent code; it looks for the pattern of code that lock generates, so it is not really a C#-specific thing. It will work with VB.NET's SyncLock too, for example.). The JIT guarantees that for any code that uses this construct, it will eliminate any window between acquiring the lock and entering the try block!

This makes me slightly queasy for a couple of reasons. First, I don't really like the magic special case handling of this in the JIT. But more importantly, I'm not a fan of the lock statement. I've written about this a few times before, but in short, my main problem with lock is that you can't specify a timeout. Blocking indefinitely means your program system is irretrievably hosed if it deadlocks.

So I don't use lock. But using apparently doesn't get this special treatment, as far as I can tell. (And it probably shouldn't - what if I'm writing code that depends on the current behaviour?)

Joe tells us that this isn't really a big deal, because this only really matters for thread abort scenarios. Those should really only ever happen to you at AppDomain shutdown time. (If you use them for anything else, stop doing that!) And in that particular case, critical finalizers in Whidbey provide a 'good enough' form of cleanup.

It still leaves me feeling rather uncomfortable. The fact that they felt the need to build in this hack for the lock keyword makes it hard to believe it when Joe says this "shouldn't be a concern to you". That and the new experimental idioms Joe illustrates towards the end of the article - if they are considering these things, doesn't that mean this is important? And the observation near the end that there is no good solution for the IDisposable idiom doesn't fill me with warmth.

I think the most important thing to take away is the "Never initiate asynchronous thread aborts in the Framework" rule. Or, as I've always said: Thread.Abort is evil.

Copyright © 2002-2024, Interact Software Ltd. Content by Ian Griffiths. Please direct all Web site inquiries to webmaster@interact-sw.co.uk