'TaskCanceledException: Cancel or timeout?

I only want to use CancellationTokenSource for timeout and cancellation handling.

How to distinguish if a TaskCanceledException occured due to a timeout or due to manual cancellation?

Here is a simplified example. In the real program I neither know if CancellationTokenSource .CancelAfter() was used nor if someone called CancellationTokenSource.Cancel()

static CancellationTokenSource cts = new CancellationTokenSource();

static void Main(string[] args)
{
    Task.Run(async () =>
    {
        try
        {
            await SomeClass.DoSomething(cts.Token);
        }
        catch (TaskCanceledException ex)
        {
            //How to find out if the exception occured due to timeout or a call to cts.Cancel()
        }
    });

    while (true)
    {
        Thread.Sleep(100);

        if (someCondition)
            cts.Cancel();
    }
}

public class SomeClass
{
    public static async Task DoSomething(CancellationToken ct)
    {
        using (var innerCts = CancellationTokenSource.CreateLinkedTokenSource(ct))
        {
            innerCts.CancelAfter(1000);

            //Simulate some operation
            await Task.Delay(10000, innerCts.Token);
        }
    }
}

thanks

Tom



Solution 1:[1]

AFAIK this is the most commonly used pattern:

Task.Run(async () =>
{
    try
    {
        await SomeClass.DoSomething(cts.Token);
    }
    catch (OperationCanceledException) when (cts.IsCancellationRequested)
    {
        // cts cancellation occurred
    }
    catch (OperationCanceledException)
    {
        // Timeout occurred
    }
});

Another idea is to change the implementation of the SomeClass.DoSomething method, assuming that you are allowed to do it, so that in case of timeout it throws a TimeoutException instead of an OperationCanceledException.

public static async Task DoSomething(CancellationToken cancellationToken)
{
    using var innerCts = new CancellationTokenSource(millisecondsDelay: 1000);
    using var linkedCts = CancellationTokenSource
        .CreateLinkedTokenSource(cancellationToken, innerCts.Token);

    try
    {
        // Simulate some operation
        await Task.Delay(10000, linkedCts.Token);
    }
    catch (OperationCanceledException) when (innerCts.IsCancellationRequested)
    {
        throw new TimeoutException();
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Theodor Zoulias