# Stop a task (CancellationToken) and complete one by hand (TaskCompletionSource)

> C#'s cancellation, with C#'s meaning. A CancellationTokenSource hands out a Token; cancel the source and every task that was handed the token stops — its waits and its outbound calls with it — and awaiting it raises TaskCanceledException. A TaskCompletionSource<T> is a task you finish yourself, with SetResult, SetException or SetCanceled.

<!-- id: function-cancellation · area: function · stability: stable · html: https://osysharp.com/reference/function/cancellation/ -->

## Summary        {#summary}
Make a `CancellationTokenSource`, hand its `Token` to the work you start, and call `Cancel()` when you no longer want
it. A task that was handed the token stops — even if it never reads the token itself — and awaiting it raises
`TaskCanceledException`, which `catch (OperationCanceledException)` takes.

```osy syntax
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));   // cancels itself after five seconds
var work = Fetch(url, cts.Token);
try { return await work; }
catch (OperationCanceledException) { return "gave up"; }
```

## Signature      {#signature}
```osy syntax
new CancellationTokenSource()                    // cancelled when you say so
new CancellationTokenSource(TimeSpan delay)      // …or by itself after delay (milliseconds work too)
CancellationTokenSource.CreateLinkedTokenSource(a, b)   // cancelled when any of the tokens is
cts.Token · cts.Cancel() · cts.CancelAfter(delay) · cts.IsCancellationRequested · cts.Dispose()
CancellationToken.None                           // a token nothing can cancel
token.IsCancellationRequested · token.CanBeCanceled · token.ThrowIfCancellationRequested()

new TaskCompletionSource<T>()                    // a task you finish yourself (no <T>: a Task with no result)
tcs.Task                                         // the ONE task it finishes
tcs.SetResult(v) · tcs.SetException(ex) · tcs.SetCanceled()          // a second one raises InvalidOperationException
tcs.TrySetResult(v) · tcs.TrySetException(ex) · tcs.TrySetCanceled() // …these answer false instead
```

## Description    {#description}

### What stops when a token is cancelled?   {#observed}
- **A task that was handed the token.** A method started as a task with a token among its arguments stops when the
  token is cancelled: its own waits end, and so do the outbound calls it is making. It does not have to check the
  token for that to happen.
- **`Task.Delay(delay, token)`** ends at once with `TaskCanceledException`.
- **Code that checks.** `token.ThrowIfCancellationRequested()` raises `OperationCanceledException` once the token is
  cancelled; `token.IsCancellationRequested` reads it.

- **A call awaited in place.** `await Work(url, token)` — a method handed the token as an argument stops too: its waits
  and its outbound calls end, and the `await` raises `TaskCanceledException`, though it never read the token itself.
- **`Task.Run(() => …, token)`** with the token already cancelled never starts the work: the task is cancelled. Once
  started, it stops when the token is.
- **A `Parallel` loop** given `new ParallelOptions { CancellationToken = token }` starts no body once it is cancelled
  and raises `OperationCanceledException` ([Do the same work for many items at once (Parallel, SemaphoreSlim)](https://osysharp.com/reference/function/parallel/)).

### Which exceptions does cancelling raise?   {#exceptions}
`OperationCanceledException`, and `TaskCanceledException`, which derives from it — so `catch (OperationCanceledException)`
takes both, as in C#. Awaiting a task that was cancelled raises `TaskCanceledException`.

### When does a TaskCompletionSource help?   {#tcs}
When a task's answer comes from somewhere other than the method that made it — one task finishes it, another waits
for it. `tcs.Task` is the same task every time you read it. Awaiting it before anything can finish it would wait
forever in C#; here, when nothing still running could finish it, the `await` raises `InvalidOperationException`
saying so.

### Can a class hold a token or a source?   {#fields}
Yes — `private CancellationTokenSource? stop;`, `public SemaphoreSlim Gate = new SemaphoreSlim(3);` and a
`CancellationToken` field are ordinary class fields, held for as long as the object lives (never stored in a row).

### Can a source or a completion source cross a pause?   {#suspend}
A token can — it is carried as whether it was cancelled. A `CancellationTokenSource`, a `TaskCompletionSource` and a
`SemaphoreSlim` hold timers and waiting tasks, which cannot be written into a pause (a call into the browser, a
workflow wait), so holding one across it is refused by name: make it after the pause instead.

## Examples       {#examples}
```osy title="stop the tasks that lost a race" test app=function-cancellation
async Task<int> Answer(int n, CancellationToken token) {
  await Task.Delay(TimeSpan.FromMilliseconds(n * 100), token);
  return n;
}

async Task<int> Fastest() {
  var cts = new CancellationTokenSource();
  var first = await Task.WhenAny(Answer(1, cts.Token), Answer(5, cts.Token), Answer(9, cts.Token));
  cts.Cancel();                 // the other two stop now, instead of running to their end
  return await first;
}
```

```osy title="a loop that checks its token" test app=function-cancellation
int CountUntilCancelled(CancellationToken token) {
  var n = 0;
  while (n < 1000) {
    token.ThrowIfCancellationRequested();
    n++;
  }
  return n;
}

string Run() {
  var cts = new CancellationTokenSource();
  cts.Cancel();
  try { return $"{CountUntilCancelled(cts.Token)}"; }
  catch (OperationCanceledException) { return "stopped"; }
}
```

```osy title="a task finished by another task" test app=function-cancellation
async Task Produce(TaskCompletionSource<string> answer) {
  await Task.Delay(TimeSpan.FromMilliseconds(20));
  answer.SetResult("ready");
}

async Task<string> Consume() {
  var answer = new TaskCompletionSource<string>();
  var producing = Produce(answer);
  var value = await answer.Task;
  await producing;
  return value;
}
```

## See also       {#see-also}
- [async / await and Task](https://osysharp.com/reference/function/async-await/) — starting a task, and what a task may do
- [Wait for several tasks (Task.WhenAll, Task.WhenAny)](https://osysharp.com/reference/function/task-whenall/) — racing tasks, and what happens to the losers
- [Wait in place (Task.Delay)](https://osysharp.com/reference/function/task-delay/) — a wait that a token can end
- [Do the same work for many items at once (Parallel, SemaphoreSlim)](https://osysharp.com/reference/function/parallel/) — `Parallel.ForEachAsync` hands each body a token
