Title: C# Task Ignoring Cancellation timeout
Tags: c#;async-await;task-parallel-library;task
Question: I'm trying to write a wrapper for arbitrary code that will cancel (or at least stop waiting for) the code after a given timeout period.
I have the following test and implementation
[Test]
public void Policy_TimeoutExpires_DoStuff_TaskShouldNotContinue()
var cts = new CancellationTokenSource();
var fakeService = new Mock<IFakeService&gt;();
IExecutionPolicy policy = new TimeoutPolicy(new ExecutionTimeout(20), new DefaultExecutionPolicy());
Assert.Throws<TimeoutException&gt;(async () =&gt; await policy.ExecuteAsync(() =&gt; DoStuff(3000, fakeService.Object), cts.Token));
fakeService.Verify(f=&gt;f.DoStuff(),Times.Never);
and the "DoStuff" method
private static async Task DoStuff(int sleepTime, IFakeService fakeService)
await Task.Delay(sleepTime).ConfigureAwait(false);
var result = await Task.FromResult("bob");
var test = result + "test";
fakeService.DoStuff();
And the implementation of IExecutionPolicy.ExecuteAsync
public async Task ExecuteAsync(Action action, CancellationToken token)
var cts = new CancellationTokenSource();//TODO: resolve ignoring the token we were given!
var task = _decoratedPolicy.ExecuteAsync(action, cts.Token);
cts.CancelAfter(_timeout);
try
await task.ConfigureAwait(false);
catch(OperationCanceledException err)
throw new TimeoutException("The task did not complete within the TimeoutExecutionPolicy window of" + _timeout + "ms", err);
What should happen is that that the test method attempts to take >3000ms and the timeout should occur at 20ms, but this is not happening. Why does my code not timeout as expected?
As requested - the decoratedPolicy is as follows
public async Task ExecuteAsync(Action action, CancellationToken token)
token.ThrowIfCancellationRequested();
await Task.Factory.StartNew(action.Invoke, token);
Comment: To solve your TODO just create a [linked cancelation token source](https://msdn.microsoft.com/en-us/library/dd642252(v=vs.110).aspx) `var cts = CancellationTokenSource.CreateLinkedTokenSource(token);`. That creates a "Child source" that can be canceled but does not cancel the passed in token, it also cancels itself if the passed in token is canceled. EDIT: one last note. Linked token sources is one place where it is a good idea to wrap the token source in a `using` statement, otherwise you may get some long lived objects that can't be GC'ed

Comment: by monitoring what do you mean exactly? Sorry - this is a first foray into this area.
This is the current internal "decorated policy"
public async Task ExecuteAsync(Action action, CancellationToken token)
{
await Task.Factory.StartNew(action.Invoke, token);
}

Comment: nice! I hadn't even got to thinking about that TODO, but thank you :)

Comment: Are you monitoring the token inside `_decoratedPolicy.ExecuteAsync(action, cts.Token);`?


Here is another answer: You are calling Assert.Throws(Action action) and your anonymous async method is casted to async void. The method will be called asynchronously with Fire&amp;Forget semantics without throwing exception.
However the process would likely crash shortly after due to the uncatched exception in an async void method.
You should call ExecuteAsync synchronously:
[Test]
public void Policy_TimeoutExpires_DoStuff_TaskShouldNotContinue()
var cts = new CancellationTokenSource();
var fakeService = new Mock<IFakeService&gt;();
IExecutionPolicy policy = new TimeoutPolicy(new ExecutionTimeout(20), new DefaultExecutionPolicy());
Assert.Throws<AggregateException&gt;(() =&gt; policy.ExecuteAsync(() =&gt; DoStuff(3000, fakeService.Object), cts.Token).Wait());
fakeService.Verify(f=&gt;f.DoStuff(),Times.Never);
or use an async test method:
[Test]
public async Task Policy_TimeoutExpires_DoStuff_TaskShouldNotContinue()
var cts = new CancellationTokenSource();
var fakeService = new Mock<IFakeService&gt;();
IExecutionPolicy policy = new TimeoutPolicy(new ExecutionTimeout(20), new DefaultExecutionPolicy());
try
await policy.ExecuteAsync(() =&gt; DoStuff(3000, fakeService.Object), cts.Token);
Assert.Fail("Method did not timeout.");
catch (TimeoutException)
{ }
fakeService.Verify(f=&gt;f.DoStuff(),Times.Never);

Here is another answer: If I understand correctly, you're trying to support timeout for a method which doesn't supports timeout / cancellation.
Typically this is done with a starting a timer with required timeout value. If the timer fires first, then you can throw exception. With TPL, you can use Task.Delay(_timeout) instead of a timer.
public async Task ExecuteAsync(Action action, CancellationToken token)
var task = _decoratedPolicy.ExecuteAsync(action, token);
var completed = await Task.WhenAny(task, Task.Delay(_timeout));
if (completed != task)
throw new TimeoutException("The task did not complete within the TimeoutExecutionPolicy window of" + _timeout + "ms");
Note: This doesn't stops the execution of _decoratedPolicy.ExecuteAsync method rather it ignores it.
If your method do support cancellation(but not in a timely manner) then it is better to cancel the Task after the timeout. You can do it by creating a Linked Token.
public async Task ExecuteAsync(Action action, CancellationToken token)
using(var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token))
var task = _decoratedPolicy.ExecuteAsync(action, linkedTokenSource.Token);
var completed = await Task.WhenAny(task, Task.Delay(_timeout));
if (completed != task)
{
linkedTokenSource.Cancel();//Try to cancel the method
throw new TimeoutException("The task did not complete within the TimeoutExecutionPolicy window of" + _timeout + "ms");
}
Comment for this answer: Linked tokens are one spot where it is kind of a good idea to put the CancellationTokenSource in a `using` statement because it [unregisters the link](http://referencesource.microsoft.com/mscorlib/R/4a9bdf1a803d40bc.html) during the dispose. (You should also use it if you use the constructor that takes in a `TimeSpan` or you do a action that uses the internal `WaitHandle`.
Comment for this answer: But his methods *do* support cancellation.
Comment for this answer: But then you're simply ignoring the execution, nothing is really cancelled. If he's in charge of implementing the API, he should let the token go through where it's needed, rather then discarding the task.
Comment for this answer: @YuvalItzchakov Even if it did, there is no guarantee that it will respond in a timely manner. I read his question as it doesn't support cancellation even though it takes the `CancellationToken`
Comment for this answer: @YuvalItzchakov You're right. I've not cancelled the original task earlier. Now amended my answer to cancel the task still following the same technique.
Comment for this answer: @ScottChamberlain You're right. I wrapped it inside using statement.

Here is another answer: I've decided to answer my own question here, as while each of the listed answers solved something that I needed to do, they did not identify the root cause of this issue. Many, many thanks to: Scott Chamberlain, Yuval Itzchakov,Sriram Sakthivel, Jeff Cyr. All advice gratefully received.
Root cause/Solution:
await Task.Factory.StartNew(action.Invoke, token);
which you see above in my "decorated policy" returns a Task and await waits only for the outer task. Replacing it with
await Task.Run(async () =&gt; await action.Invoke());
gets the correct result.
My code was suffering from a combination of Gotcha #4 and Gotcha #5 from an excellent article on C# async gotchas
The entire article (as well as answers posted to this question) has really improved my overall understanding.
Comment for this answer: except that the change above fixed the functionality, and the change from Assert.Throws made zero difference. Whether I've interpreted the exact reason correctly or not, the original did not allow calls to return
Comment for this answer: I think you are missing something here, the gotcha about `Task.Factory.StartNew` is when you pass a Func delegate, in your case you are passing an Action so the await will return after the action is executed. You cannot await an Action like you did in your answer.
I think the root cause is really about Assert.Throws which does not handle async methods.

Here is another answer: Using CancellationToken means that you're doing cooperative cancellation. Setting CancellationTokenSource.CancelAfter will transition the underlying token to a canceled state after the specified amount of time, but if that token isn't monitored by the calling async method, then nothing will happen.
In order for this to actually generate a OperationCanceledException, you need to call cts.Token.ThrowIfCancellationRequested inside _decoratedPolicy.ExecuteAsync.
For example:
// Assuming this is _decoratedPolicy.ExecuteAsync
public async Task ExecuteAsync(Action action, CancellationToken token)
// This is what creates and throws the OperationCanceledException
token.ThrowIfCancellationRequested();
// Simulate some work
await Task.Delay(20);
In order to actually cancel the token, you need to monitor it at all points where work is executed and execution may timeout. If you cannot make that guarantee, then defer to @SriramSakthivel answer where the actual Task is being discarded, rather than being actually canceled.
Comment for this answer: hmm  still does not work though - I've included my decorated method above. Would my "do stuff" method need to accept and monitor the token too?
Comment for this answer: @PaulDevenney All relevant participants need to monitor the token. If `DoStuff` is the `Action` you pass around, then yes.

{'source': 'StackExchangeDataset', 'subset': 'stackoverflow.com', 'metadata': {'AnswerCount': 4, 'CommentCount': 4, 'ContentLicense': 'CC BY-SA 3.0', 'CreationDate': '2015:00:05/20/15', 'FavoriteCount': None, 'PostTypeId': 1, 'Tags': '<c#><async-await><task-parallel-library><task>', 'Title': 'C# Task Ignoring Cancellation timeout', 'ViewCount': 2207, 'id': 30351553, 'user_AboutMe': '', 'user_AccountId': 1799599.0, 'user_DisplayName': 'Paul Devenney', 'user_Reputation': 849.0, 'user_Views': 62.0, 'user_id': 1638254.0}}