NDepend

Improve your .NET code quality with NDepend

On replacing Thread.Abort() in .NET 6, .NET 5 and .NET Core

On replacing Thread.Abort() in .NET 6 .NET 5 .NET Core

Thread.Abort() is not supported in .NET 5 / .NET Core

We are actually migrating the NDepend analysis and reporting to .NET 5 and figured out that there is no equivalent for the infamous API Thread.Abort(). By the time ASP.NET Core was created, CancellationToken had become the safe and widely accepted alternative to Thread.Abort(), so there was no need to implement it in the first version of .NET Core. Interestingly enough Thread.Abort() is a .NET Standard API but it throws NotSupportedException in .NET 5 / .NET Core context.

Thread.Abort() used to work when carefully handled

There is no discussion: Thread.Abort() is a dangerous API that throws ThreadAbortException at any random point deep in the call stack. Nevertheless production logs show that when carefully implemented Thread.Abort() doesn’t provoke any crash nor state corruption. Careful usage of Thread.Abort() is not-trivial and implies some events to prevent edge cases to happen. But for more than a decade it worked (and still works) fine for thousands of real users. Till now we applied the tenet if it ain’t broke, don’t fix it.

CancellationToken cannot handle preemptive cancellation scenarios

CancellationToken is nowadays the safe way to implement cancelable operations. But it is not a replacement for Thread.Abort(): it only supports co-operative cancellation scenarios, where the cancellable processing is responsible for periodically checking if it has been cancelled.

However CancellationToken doesn’t support preemptive cancellation scenarios, made possible by the Thread.Abort() API. Preemptive cancellation is useful to cancel long-running operation involving code that we do not own or that makes awkward to periodically check for cancellation. We have a few such timeout scenarios, the most notable being the execution of code queries compiled on the fly: how to inject periodically checks in a generated LINQ query? We will inject calls to such method CheckTimeOut() (below) in the generated LINQ query but the whole challenge will be to do it in a way that won’t harm performances:

Eric Lippert advised in this stackoverflow answer to handle such scenario with a dedicated child process that can be safely killed if needed. Unfortunately this suggestion does not apply well to our needs since both the query execution context and the query result aren’t lightweight in many scenarios.

As a consequence some significant work is now required to refactor our tricky but reliable preemptive cancellation implementation into a well-designed co-operative cancellation implementation.

Using CancellationToken to timeout a synchronous processing

Usually CancellationToken is used when the cancelable operation is processed asynchronously on a worker thread of the CLR thread pool. But in our scenarios the cancellable processing is executed synchronously on the main caller thread. To do so a thread from the CLR pool used to be responsible for calling Thread.Abort() against the main thread upon timeout. It sounds dangerous but again, when done carefully with some events it works seamlessly.

Hopefully CancellationToken can be used to timeout a synchronous processing and here is how. Thanks to the CancellationTokenSource(TimeSpan) constructor this implementation doesn’t even involve any secondary thread to watch for timeout. Undoubtedly this code is way cleaner than anything based on Thread.Abort() 🙂

My dad being an early programmer in the 70's, I have been fortunate to switch from playing with Lego, to program my own micro-games, when I was still a kid. Since then I never stop programming.

I graduated in Mathematics and Software engineering. After a decade of C++ programming and consultancy, I got interested in the brand new .NET platform in 2002. I had the chance to write the best-seller book (in French) on .NET and C#, published by O'Reilly and also did manage some academic and professional courses on the platform and C#.

Over my consulting years I built an expertise about the architecture, the evolution and the maintenance challenges of large & complex real-world applications. It seemed like the spaghetti & entangled monolithic legacy concerned every sufficiently large team. As a consequence, I got interested in static code analysis and started the project NDepend in 2004.

Nowadays NDepend is a full-fledged Independent Software Vendor (ISV). With more than 12.000 client companies, including many of the Fortune 500 ones, NDepend offers deeper insight and full control on their application to a wide range of professional users around the world.

I live with my wife and our twin kids Léna and Paul in the beautiful island of Mauritius in the Indian Ocean.