1

In my C# application (unit test, actually) I want to be able to launch an operation and set a timeout. If the timeout elapses and my operation has not terminated, I want to:

  1. Throw an exception.
  2. Include a stack trace of the thread running the operation.
  3. If reasonably convenient, cancel the operation.

I can accomplish point #1 using the method described in this answer by Lawrence Johnston: https://stackoverflow.com/a/22078975/8557153

How can I accomplish point 2? I have a reference to a Task, but I know that Task objects do not have stack traces per se. A Task just a data structure of some kind. Only threads have stack traces. But is there any way to get a stack trace from the thread running the task, if any? Or will I need to completely rewrite the body of my task to make this possible?

Point 3 is optional. It is acceptable to have the task continue unawaited in the background, but it would be nice to abort it.

Claus Appel
  • 379
  • 1
  • 4
  • 13
  • 1
    This is not possible in general. Consider a task that is awaiting `Task.Delay()`: In that case, a `Timer` will have been created to continue after the delay, and there will be no code running for which to obtain a stack trace. The same goes for many other cases, such as awaiting I/O - [there is no thread](https://blog.stephencleary.com/2013/11/there-is-no-thread.html)! – Matthew Watson Aug 29 '19 at 07:38

1 Answers1

2

Use cancellation tokens. It will do all 3 things you want:

  1. When tasks are cancelled, they throw a TaskCancelledException.
  2. The exception will have a stack trace.
  3. The task will be cancelled.

You can read about it here, but here's a quick example:

var tokenSource = new CancellationTokenSource();

//start a task
var task = Task.Delay(TimeSpan.FromMinutes(1), tokenSource.Token);

//cancel the task
tokenSource.Cancel();

try {
    //once you await it, you will see the exception
    await task;
} catch (TaskCanceledException e) {
    //inspect e.StackTrace
}
Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
  • Thanks! I assume that for this to work, I need to rewrite the code that runs in the task to call `ct.ThrowIfCancellationRequested()` regularly (like in the example in the article you linked)? – Claus Appel Aug 30 '19 at 08:12
  • 1
    Yes, and pass the token to any async methods that you call. – Gabriel Luci Aug 30 '19 at 11:37