Through the Discord community, I received the question of how to safely execute third-party code in C# that is susceptible to blocking using a Timeout to cancel the task.
Concurrency is always a complicated topic, in practically all languages. C# is not only no exception but is even a good example. The reason is that over the years it has incorporated different functionalities but, logically, for compatibility reasons, the previous ones have not been removed.
This means that .NET has a large number of tools for managing concurrency but, at the same time, a large number of tutorials, codes, and examples have become obsolete for not using the latest tools and are outdated.
As we said, a common example is having to deal with a task that is susceptible to getting blocked. For this, we usually want to use a timeout, that is, a time after which we cancel the task if it has not finished.
For this, we have different mechanisms. The most common will be to use a CancellationToken. However, this requires that the code of the executed task is designed to use this mechanism. But in the case where we have to work with legacy code or third-party libraries, it is possible that we do not have the option to use a CancellationToken directly.
So let’s look at one of the possible techniques to address this situation, where we have to deal with code that is susceptible to blocking, that we cannot modify, and that we want to handle with a Timeout.
Let’s try with an example. First, let’s create our “blocking code”. For this, we create a new library, and inside it we simply put the following,
namespace ClassLibrary1
{
public static class Testme
{
public static void DoMagic()
{
while (true)
{
Thread.Sleep(1000);
}
}
}
}
That is, we have only created a method called ‘DoMagic’, which as we see uses a while(true) and a wait to permanently block execution. We have put ourselves in the worst-case scenario, which is that the third-party code (in our case, simulated) does not use async, nor Task, nor any asynchronous mechanism. It is simply a blocking method, old school.
To test it, we create a new console application, and replace the code with the following,
Console.WriteLine("Starting");
ClassLibrary1.Testme.DoMagic();
Console.WriteLine("Finally");
We run the code and verify that, as expected, ‘Starting’ is displayed but ‘Finally’ is never shown because the code gets blocked in our (/(disastrous) simulated third-party library.
To solve this conveniently, we create a class with extension methods for Tasks,
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
public static class TaskExtensions
{
public static async Task RunWithTimeout(this Task task, TimeSpan timeout)
{
using (var timeoutCancellationTokenSource = new CancellationTokenSource())
{
var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token));
if (completedTask == task)
{
timeoutCancellationTokenSource.Cancel();
return;
}
else
{
throw new TimeoutException("The operation has timed out.");
}
}
}
public static async Task<TResult> RunWithTimeout<TResult>(this Task<TResult> task, TimeSpan timeout)
{
using (var timeoutCancellationTokenSource = new CancellationTokenSource())
{
var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token));
if (completedTask == task)
{
timeoutCancellationTokenSource.Cancel();
return await task;
}
else
{
throw new TimeoutException("The operation has timed out.");
}
}
}
}
}
With this, we could run our task with a TimeOut by doing
using ConsoleApp1;
Console.WriteLine("Starting");
var task = Task.Factory.StartNew(() => { ClassLibrary1.Testme.DoMagic(); });
try
{
await task.RunWithTimeout(TimeSpan.FromSeconds(3));
}
catch (TimeoutException ex)
{
Console.WriteLine("Timeout");
}
Console.WriteLine("Finally");
If we run the code we will see that “Starting” is displayed and, after 3 seconds, “Timeout” and “Finally”. That is, our Timeout is working correctly.

