Linking Cancellation Tokens

As i3arnon already answered, you can do this with CancellationTokenSource.CreateLinkedTokenSource(). I want to try to show a pattern of how to use such a token when you want to distinguish between cancellation of an overall task versus cancellation of a child task without cancellation of the overall task.

async Task MyAsyncTask(
    CancellationToken ct)
{
    // Keep retrying until the master process is cancelled.
    while (true)
    {
        // Ensure we cancel ourselves if the parent is cancelled.
        ct.ThrowIfCancellationRequested();

        using var childCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
        // Set a timeout because sometimes stuff gets stuck.
        childCts.CancelAfter(TimeSpan.FromSeconds(32));
        try
        {
            await DoSomethingAsync(childCts.Token);
        }
        // If our attempt timed out, catch so that our retry loop continues.
        // Note: because the token is linked, the parent token may have been
        // cancelled. We check this at the beginning of the while loop.
        catch (OperationCancelledException) when (childCts.IsCancellationRequested)
        {
        }
    }
}

When the Temporary Token expires it must NOT cancel the Master Token.

Note that MyAsyncTask()’s signature accepts CancellationToken rather than CancellationTokenSource. Since the method only has access to the members on CancellationToken, it cannot accidentally cancel the master/parent token. I recommend that you organize your code in such a way that the CancellationTokenSource of the master task is visible to as little code as possible. In most cases, this can be done by passing CancellationTokenSource.Token to methods instead of sharing the reference to the CancellationTokenSource.

I have not investigated, but there may be a way with something like reflection to forcibly cancel a CancellationToken without access to its CancellationTokenSource. Hopefully it is impossible, but if it were possible, it would be considered bad practice and is not something to worry about generally.


You want to use CancellationTokenSource.CreateLinkedTokenSource. It allows to have a "parent" and a "child" CancellationTokenSourcees. Here's a simple example:

var parentCts = new CancellationTokenSource();
var childCts = CancellationTokenSource.CreateLinkedTokenSource(parentCts.Token);

childCts.CancelAfter(1000);
Console.WriteLine("Cancel child CTS");
Thread.Sleep(2000);
Console.WriteLine("Child CTS: {0}", childCts.IsCancellationRequested);
Console.WriteLine("Parent CTS: {0}", parentCts.IsCancellationRequested);
Console.WriteLine();

parentCts.Cancel();
Console.WriteLine("Cancel parent CTS");
Console.WriteLine("Child CTS: {0}", childCts.IsCancellationRequested);
Console.WriteLine("Parent CTS: {0}", parentCts.IsCancellationRequested);

Output as expected:

Cancel child CTS
Child CTS: True
Parent CTS: False

Cancel parent CTS
Child CTS: True
Parent CTS: True


If all you have is a CancellationToken, instead of a CancellationTokenSource, then it is still possible to create a linked cancellation token. You would simply use the Register method to trigger the cancellation of the the (pseudo) child:

var child = new CancellationTokenSource();
token.Register(child.Cancel);

You can do anything you would typically do with a CancellationTokenSource. For example you can cancel it after a duration and even overwrite your previous token.

child.CancelAfter(cancelTime);
token = child.Token;