tabs ↹ over ␣ ␣ ␣ spaces

by Jiří {x2} Činčura

Empty try with finally

10 Jun 2019 .NET, .NET Core, C#

The finally block has a little unknown feature, that frankly isn’t even remotely needed for regular day-to-day development, but you already know content on this blog is usually little geeky.

The finally block is guaranteed to run completely. In other words, it cannot be interrupted using regular outside code (you can still interrupt it yourself). This is important, because there’s a little nasty exception named ThreadAbortException. This can erupt basically anywhere anytime. But for some very reliable pieces of code, like synchronization primitives, this isn’t what you want to hear.

Related to this are also constrained execution regions or CriticalFinalizerObject. Moreover, .NET Core doesn’t support Thread.Abort anymore (and JIT is smart about it).

Putting these two together, this is solvable. And it’s nicely visible in i.e. CoreCLR or CoreFX. To find these instances I wrote a small tool using Roslyn. It’s basically going through all the files and checking for empty try block with non-empty finally block. Here’s the skeleton of the code.

var unit = SyntaxFactory.ParseCompilationUnit(File.ReadAllText(file), options: parseOptions);
foreach (var node in unit.DescendantNodes().Where(n => n.IsKind(SyntaxKind.FinallyClause)))
{
	var finallySyntax = node as FinallyClauseSyntax;
	var trySyntax = finallySyntax.Parent as TryStatementSyntax;
	if (!trySyntax.Block.Statements.Any() && finallySyntax.Block.Statements.Any())
	{
		// bingo
	}
}

CoreCLR

So, what we can find? In CoreCLR (tests excluded) there’s only few instances in System.Private.CoreLib.

In ProducerConsumerQueues.cs and SemaphoreSlim.cs it’s “regular” stuff to ensure the data structures are not corrupted. In ThreadLocal.cs, it’s not about data structures per se, but just about making sure the IDs are not leaking aka lost. Finally, the Exception.CoreCLR.cs, which I think is most interesting. The comment explains it all.

Take a lock to ensure only one thread can restore the details at a time against this exception object that could have multiple ExceptionDispatchInfo instances associated with it.

We do this inside a finally clause to ensure ThreadAbort cannot be injected while we have taken the lock. This is to prevent unrelated exception restorations from getting blocked due to TAE.

CoreFX

In CoreFX I decided to skip System.Data.OleDb, System.Data.Odbc, System.Data.SqlClient and tests, because I’m interested more in low-level pieces. And as you’d expect there’s bit more occurrences. Here’s the complete list and I’ll just focus on few interesting below it.

First (ab)use of finally that caught my eye is in SharedPerformanceCounter.cs, because I don’t usually think about shared state across processes. Another interesting is in FileSystemWatcher.Linux.cs, talking about the tradeoffs done to make the FileSystemWatcher on Linux work.

Not exactly usage of finally block, but interesting nonetheless is object I found while looking at PinnedObject.cs named CriticalDisposableObject.cs (it’s not public). It derives from well known (public) CriticalFinalizerObject and provides a simple template. The Release method reminds me all of the different ways that were used decade or more ago for “disposing” – Release, Dispose, Close, Free, etc. methods, some of them still in place today (yes, I’m looking at you Stream class).

Closing

It’s always nice to read other – preferably smarter – people’s code. And that’s what this was all about, with a small focus on specific shape of code to maybe discover something thought-provoking.

Profile Picture Jiří Činčura is an independent developer focusing on data and business layers, language constructs, parallelism and databases. Specifically Entity Framework, asynchronous and parallel programming, cloud and Azure. He's Microsoft Most Valuable Professional and you can read his articles, guides, tips and tricks at www.tabsoverspaces.com.