Since no one here gave a better answer I'll do it myself.
The code I provided in the question is, in my opinion, most cohesive and also general enough to be recycled in other cases.
You want to use . It allows to have a "parent" and a "child" CancellationTokenSource.CreateLinkedTokenSourcees. Here's a simple example:CancellationTokenSource
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: FalseCancel parent CTS
Child CTS: True Parent CTS: True
Cancellation token is a light weight struct. Its copied by value. And it is meant to be passed down to separate levels of the functions.
So It is okay to pass cancellation token as much as you want.
But make sure your cancellation is always tied with a single operation. If the operation is cancelled, it is cancelled. You cant revert the cancellation and re use the token.
As long as you keep that in mind for the design, it seems your way of passing the cancellation token is correct.
And the other option you have is to create a separate cancellation token. To do so you will need to create a new CancellationTokenSource. This is a disposable object. So you will have to carefully dispose it after its usage is ended. And it is not light weight compared to CancellationToken. Therefore if you are not logically starting a new operation that can be cancelled, I do not recommend to use a new CancellationTokenSource.
For more information have a look at documentation
Important parts extracted from documentation,
1)
The object that invokes one or more cancelable operations, for example by creating new threads or tasks, passes the token to each operation. Individual operations can in turn pass copies of the token to other operations. At some later time, the object that created the token can use it to request that the operations stop what they are doing.
2)
In the new cancellation framework, cancellation refers to operations, not objects. The cancellation request means that the operation should stop as soon as possible after any required cleanup is performed. One cancellation token should refer to one "cancelable operation," however that operation may be implemented in your program. After the IsCancellationRequested property of the token has been set to true, it cannot be reset to false. Therefore, cancellation tokens cannot be reused after they have been canceled.
3)
The CancellationTokenSource class implements the IDisposable interface. You should be sure to call the CancellationTokenSource.Dispose method when you have finished using the cancellation token source to free any unmanaged resources it holds
The method was in fact well called, but then had a null value, causing a Exception not caught.System.Web.HttpContext.Current
By replacing with , the code is working successfully.System.Web.Hosting.HostingEnvironment.MapPath
Try this:
async Task A(CancellationToken ct)
{
using (var timoutCts = new CancellationTokenSource(10000))
{
using (var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(ct, timoutCts.Token))
{
await B(combinedCts.Token);
}
}
}
As the documentation states, you need to call the method from the token source, not the token itself. Note the example code in the CancellationToken Struct documentation:Cancel()
// Define the cancellation token.
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;
...
source.Cancel();
how can I, in possession of only a CancellationToken, cancel it?
Without a reference to the source you cannot cancel the token, this is by design.
As a flawed workaround, when given a , you can create a new instance of the token source, assign its token to the provided token, and cancel the new source:CancellationToken
// Define the cancellation token.
CancellationTokenSource newSource = new CancellationTokenSource();
existingToken = newSource.Token;
...
newSource.Cancel();
// "existingToken" is cancelled hereafter
...but this will only affect downstream consumers of the token. Any entities with the token prior to updating the reference will still have the original, uncancelled token.
But do note that if you're creating the token to track tasks, then you do have the source, so this shouldn't be an issue.