How to cancel a task from a TaskCompletionSource? - c#

I'm trying to create an async ProducerConsumerCollection and for that, I'm using this msdn page (http://msdn.microsoft.com/en-us/library/hh873173.aspx (bottom of the page)).
I'm now trying to add a timeout, here is what I do :
public async Task<T> TakeWithTimeout(int timeout)
{
Task<T> takeTask = this.Take();
if (timeout <= 0 || takeTask == await Task.WhenAny(this.tasks.Take(), Task.Delay(timeout)))
{
return await takeTask;
}
else
{
// Timeout
return default(T);
}
}
}
The problem with this code is that, in case of timeout, it does not cancel the task created by the Take() method.
Since this task has been "created" by the TaskCompletionSource, I cannot give it a cancellationToken?
So, how to proceed to cancel it and properly implement this Take with timeout ?
Thanks :)

Writing a cancel-safe async-friendly producer/consumer collection is non-trivial. What you need to do is change Take to accept a CancellationToken as a parameter, and it should register a handler so that when it is cancelled the TaskCompletionSource is cancelled.
I highly recommend you use BufferBlock<T>, which has cancellation support built-in.
If you can't use TPL Dataflow (e.g., you're working in a PCL or have target platforms unsupported by Dataflow), then you can use the producer/consumer collections in my open-source AsyncEx library (such as AsyncProducerConsumerQueue or AsyncCollection). These are both based on AsyncLock and AsyncConditionVariable, a design I describe briefly on my blog (which does not get into the cancellation details). The key behind supporting cancellation in a producer/consumer collection with this design is to support cancellation in AsyncConditionVariable.WaitAsync; once your condition variable type supports cancellation, then your collection will easily support it, too.

I'm just going to post my solution to the question How to cancel a task from a TaskCompletionSource because that is what I needed myself.
I'm guessing this could be used for your specific need, but it's not tied to a specific timeout functionality, so this is a general solution (or so I hope).
This is an extension method:
public static async Task WaitAsync<T>(this TaskCompletionSource<T> tcs, CancellationToken ctok)
{
CancellationTokenSource cts = null;
CancellationTokenSource linkedCts = null;
try {
cts = new CancellationTokenSource();
linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, ctok);
var exitTok = linkedCts.Token;
Func<Task> listenForCancelTaskFnc = async () => {
await Task.Delay(-1, exitTok).ConfigureAwait(false);
};
var cancelTask = listenForCancelTaskFnc();
await Task.WhenAny(new Task[] { tcs.Task, cancelTask }).ConfigureAwait(false);
cts.Cancel();
} finally {
if(linkedCts != null) linkedCts.Dispose();
}
}
Usage:
async Task TestAsync(CancellationToken ctok) {
var tcs = new TaskCompletionSource<bool>();
if (somethingOrTheOther) {
tcs.TrySetResult(true);
}
await tcs.WaitAsync(ctok);
}
The idea is to have a supervisory async Task waiting essentially forever until it is cancelled, which we can use to 'exit early' in case the TaskCompletionSource is not yet satisfied, but we need to exit anyhow due to a cancel request.
The supervisory Task is guaranteed to be cancelled at the end of WaitAsync regardless how it falls out of the WhenAny. Either the TaskCompletionSource is satisfied with a result, and WhenAny completes, briefly leaving the supervisory sleeper task in tact until the next line where cts.Cancel() is called, or it was cancelled with the exitToken, which is a combined token of the passed in ctok or the internal one cts.Token.
Anyhow, I hope this makes sense -- please let me know if this code has any problems...

Related

C#: How to correctly execute auto-generated methods from web service with a timeout in an async method?

I need to call some methods from a web service, specified in WSDL. VisualStudio created the corresponding methods for me, including async variants. The documentation specifies (different) timeouts for those methods, some of them could take several minutes. So what is the best way to implement this?
I have two approaches, but I'm not sure which one is better, or if I should do something completely different:
Variant 1: use generated async method and task.Wait instead of await:
public async Task<ResultType> MyMethod1Async()
{
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(60000);
Task<ResultType> task = MyWebService.getSomeObjectAsync();
task.Wait(cts.Token);
return task.Result;
}
Variant 2: execute generated synchronous method in Task.Run:
public async Task<ResultType> MyMethod2Async()
{
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(60000);
Task<ResultType> task = Task.Run(
() => MyWebService.getSomeObject(),
cts.Token);
return await task;
}
Neither option will do what you want.
Variant 1 will block on task.Result regardless of any timeout. Variant 2 will not be cancelled once the method has started running
If the async task does not support cancellation, the best you can do is to return to the caller when the timeout is reached, and let the task continue in the background, any eventual result will be ignored. For example:
public async Task<ResultType> MyMethodAsync<T>(TimeSpan timeout)
{
var task = SomeAsyncMethod<ResultType>();
var timeoutTask = Task.Delay(timeout);
var completedTask = await Task.WhenAny(task, timeoutTask);
if (completedTask == timeoutTask)
{
// Handle timeout somehow
throw new TimeoutException("...");
}
return task.Result;
}
This is obviously not appropriate for compute bound tasks, and if it is possible to use real support for cancellation it should be used.

Concise way to await a canceled Task?

I find myself writing code like this a lot:
try
{
cancellationTokenSource.Cancel();
await task.ConfigureAwait(false); // this is the task that was cancelled
}
catch(OperationCanceledException)
{
// Cancellation expected and requested
}
Given that I requested the cancellation, it is expected and I'd really like the exception to be ignored. This seems like a common case.
Is there a more concise way to do this? Have I missed something about cancellation? It seems like there should be a task.CancellationExpected() method or something.
There is a built-in mechanism, the Task.WhenAny method used with a single argument, but it's not very intuitive.
Creates a task that will complete when any of the supplied tasks have completed.
await Task.WhenAny(task); // await the task ignoring exceptions
if (task.IsCanceled) return; // the task is completed at this point
var result = await task; // can throw if the task IsFaulted
It is not intuitive because the Task.WhenAny is normally used with at least two arguments. Also it is slightly inefficient because the method accepts a params Task<TResult>[] tasks argument, so on every invocation an array is allocated in the heap.
I don't think there is anything built-in, but you could capture your logic in extension methods (one for Task, one for Task<T>):
public static async Task IgnoreWhenCancelled(this Task task)
{
try
{
await task.ConfigureAwait(false);
}
catch (OperationCanceledException)
{
}
}
public static async Task<T> IgnoreWhenCancelled<T>(this Task<T> task)
{
try
{
return await task.ConfigureAwait(false);
}
catch (OperationCanceledException)
{
return default;
}
}
Then you can write your code simpler:
await task.IgnoreWhenCancelled();
or
var result = await task.IgnoreWhenCancelled();
(You might still want to add .ConfigureAwait(false) depending on your synchronization needs.)
I assume whatever task is doing uses CancellationToken.ThrowIfCancellationRequested() to check for cancellation. That throws an exception by design.
So your options are limited. If task is an operation you wrote, you could make it not use ThrowIfCancellationRequested() and instead check IsCancellationRequested and end gracefully when needed. But as you know, the task's status won't be Canceled if you do that.
If it uses code you didn't write, then you don't have a choice. You'll have to catch the exception. You can use extension methods to avoid repeating code (Matt's answer), if you want. But you'll have to catch it somewhere.
The cancellation pattern available in C# in called cooperative cancellation.
This basically means that, in order to cancel any operation, there should be two actors which need to collaborate. One of them is the actor requesting the cancellation and the other is the actor listening to cancellation requests.
In order to implement this pattern you need an instance of CancellationTokenSource, which is an object that you can use in order to get an instance of CancellationToken. The cancellation is requested on the CancellationTokenSource instance and is propagated to the CancellationToken.
The following piece of code shows you this pattern in action and hopefully clarifies your doubt about cancellation:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp2
{
public static class Program
{
public static async Task Main(string[] args)
{
using (var cts = new CancellationTokenSource())
{
CancellationToken token = cts.Token;
// start the asyncronous operation
Task<string> getMessageTask = GetSecretMessage(token);
// request the cancellation of the operation
cts.Cancel();
try
{
string message = await getMessageTask.ConfigureAwait(false);
Console.WriteLine($"Operation completed successfully before cancellation took effect. The message is: {message}");
}
catch (OperationCanceledException)
{
Console.WriteLine("The operation has been canceled");
}
catch (Exception)
{
Console.WriteLine("The operation completed with an error before cancellation took effect");
throw;
}
}
}
static async Task<string> GetSecretMessage(CancellationToken cancellationToken)
{
// simulates asyncronous work. notice that this code is listening for cancellation
// requests
await Task.Delay(500, cancellationToken).ConfigureAwait(false);
return "I'm lost in the universe";
}
}
}
Pay attention to the comment and notice that all the 3 outputs for the program are possible.
There is no way to predict which of them will be the actual program result.
The point is that when you await for the task completion you don't know what actually is going to happen. The operation may succeeds or fails before the cancellation took effect, or maybe the cancellation request can be observed by the operation before it runs to completion or fails for an error. From the calling code point of view, all these outcomes are possible and you have no way to make a guess. You need to handle all cases.
So, basically, your code is correct and you are handling the cancellation the way you should.
This book is an excellent reference to learn these things.
My final solution was to create an extension method as suggested by Matt Johnson-Pint. However, I return a boolean indicating whether the task was canceled as shown in Vasil Oreshenski's answer.
public static async Task<bool> CompletionIsCanceledAsync(this Task task)
{
if (task.IsCanceled) return true;
try
{
await task.ConfigureAwait(false);
return false;
}
catch (OperationCanceledException)
{
return true;
}
}
This method has been fully unit tested. I picked the name to be similar to the WaitForCompletionStatus() method in the ParallelExtensionsExtras sample code and the IsCanceled property.
If you are expecting the task to be cancelled BEFORE the await you should check the state of the cancellation token source.
if (cancellationTokenSource.IsCancellationRequested == false)
{
await task;
}
EDIT: As mentioned in the comments this won't do any good if the task is cancelled while awaited.
EDIT 2: This approach is overkill because it acquires additional resource - in hot path this may have performance hit. (i am using SemaphoreSlim but you can use another sync. primitive with the same success)
This is an extension method over existing task. The extension method will return new task which holds information if the original task was cancelled.
public static async Task<bool> CancellationExpectedAsync(this Task task)
{
using (var ss = new SemaphoreSlim(0, 1))
{
var syncTask = ss.WaitAsync();
task.ContinueWith(_ => ss.Release());
await syncTask;
return task.IsCanceled;
}
}
Here is a simple usage:
var cancelled = await originalTask.CancellationExpectedAsync();
if (cancelled) {
// do something when cancelled
}
else {
// do something with the original task if need
// you can acccess originalTask.Result if you need
}
How it works:
Overall it waits for the original task to complete and returns information if was cancelled. The SemaphoraSlim is usually used to limit the access to some resource(expensive) but in this case i am using it to await until the original task has finished.
Notes:
It does not returns the original task. So if you need something that has been returned from it you should inspect the original task.

How do I cancel task waiting for a blocking call

I am a beginner in UWP application in c#. I need to cancel a task in when a blocking call is going on. Please refer to the code snippet below for better understanding.
TimeSpan timeSpan = TimeSpan.FromMilliseconds(10000);
CancellationToken token = new CancellationTokenSource(timeSpan).Token;
await Task.Run(() =>
{
//This is a blocking call
Task.Delay(11002).Wait();
}, token);
As I know the if I do the blocking call like Task.Delay(11002).Wait(token); then the Delay task is canceled but my requirement is to cancel the parent task. Because I to do a sync socket write in the task.
Hope my problem is understandable to everyone.
Thanks
I'd recommend creating the cancellation token source outside of the method you're going to use it and passing it in as Neil mentioned in his comment.
Here's an example showing how you can do this and how you can cancel it.
private async Task ParentAsync()
{
TimeSpan timeSpan = TimeSpan.FromMilliseconds(10000);
CancellationTokenSource cts = new CancellationTokenSource(timeSpan);
await ExecuteAsync(cts);
cts.Cancel(); // This will cause the execution to cancel.
}
private async Task ExecuteAsync(CancellationTokenSource cts)
{
await Task.Run(() =>
{
//This is a blocking call
Task.Delay(11002).Wait();
}, cts.Token);
}
If you're looking to cancel the execution of the parent method, you follow the same steps you've already done for the child method using the token to handle the cancellation of the Task.
Hope this helps!

Immediately cancelling blocking operation with timeout

I have a blocking operation that reads from a queue, but it can take a timeout. I can easily convert this to an "async" operation:
public async Task<IMessage> ReceiveAsync(CancellationToken cancellationToken)
{
return await Task.Run(() =>
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
// Try receiving for one second
IMessage message = consumer.Receive(TimeSpan.FromSeconds(1));
if (message != null)
{
return message;
}
}
}, cancellationToken).ConfigureAwait(false);
}
Aborting a thread is generally considered bad practice since you can leak resources, so the timeout seems like the only way to cleanly stop a thread. So I have three questions:
What is a generally accepted timeout value for "immediate" cancellation?
For libraries that provide built-in async methods, does immediate cancellation truly exist or do they also use timeouts and loops to simulate it? Maybe the question here is how would you make use of software interrupts and if these also have to do some sort of polling to check if there are interrupts, even if it's at the kernel/CPU level.
Is there some alternate way I should be approaching this?
Edit: So I may have found part of my answer with Thread.Interrupt() and then handling ThreadInterruptedException. Is this basically a kernel-level software interrupt and as close to "immediate" as we can get? Would the following be a better way of handling this?
public async Task<IMessage> ReceiveAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var completionSource = new TaskCompletionSource<IMessage>();
var receiverThread = new Thread(() =>
{
try
{
completionSource.SetResult(consumer.Receive());
}
catch (ThreadInterruptedException)
{
completionSource.SetCanceled();
}
catch (Exception ex)
{
completionSource.SetException(ex);
}
});
cancellationToken.Register(receiverThread.Interrupt);
receiverThread.Name = "Queue Receive";
receiverThread.Start();
return await completionSource.Task.ConfigureAwait(false);
}
It depends on your specific needs. A second could be immediate for some and slow for others.
Libraries (good ones) which provide async API do so from the bottom up. They usually don't wrap blocking (synchronous) operations with a thread to make them seem asynchronous. They use TaskCompletionSource to create truly async methods.
I'm not sure what you mean by queue (the built-in Queue in .Net doesn't have a Receive method) but you should probably be using a truly async data structure like TPL Dataflow's BufferBlock.
About your specific code sample.
You are holding up a thread throughout the entire operation (that's async over sync) which is costly. You could instead try to consume quickly and then wait asynchronously for the timeout to end, or for the CancellationToken to be cancelled.
There's also no point in using another thread with Task.Run. You can simply have the async lambda be the content of ReceiveAsync:
public async Task<IMessage> ReceiveAsync(CancellationToken cancellationToken)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
// Try receiving for one second
IMessage message;
if (!consumer.TryReceive(out message))
{
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
}
if (message != null)
{
return message;
}
}
}
If your queue implements IDisposable a different (harsher) option would be to call Dispose on it when the CancellationToken is cancelled. Here's how.

How to implement cancellation of shared Task:s in C#

All, here is a question about design/best practices of a complex case of canceling Task:s in C#. How do you implement cancellation of a shared task?
As a minimal example, lets assume the following; we have a long running, cooperatively cancellable operation 'Work'. It accepts a cancellation token as argument and throws if it has been canceled. It operates on some application state and returns a value. Its result is independently required by two UI components.
While the application state is unchanged, the value of the Work function should be cached, and if one computation is ongoing, a new request should not start a second computation but rather start waiting for a result.
Either of the UI components should be able to cancel it's Task without affecting the other UI components task.
Are you with me so far?
The above can be accomplished by introducing an Task cache that wraps the real Work task in TaskCompletionSources, whose Task:s are then returned to the UI components. If a UI component cancels it's Task, it only abandons the TaskCompletionSource Task and not the underlying task. This is all good. The UI components creates the CancellationSource and the request to cancel is a normal top down design, with the cooperating TaskCompletionSource Task at the bottom.
Now, to the real problem. What to do when the application state changes? Lets assume that having the 'Work' function operate on a copy of the state is not feasible.
One solution would be to listen to the state change in the task cache (or there about). If the cache has a CancellationToken used by the underlying task, the one running the Work function, it could cancel it. This could then trigger a cancellation of all attached TaskCompletionSources Task:s, and thus both UI components would get Canceled tasks. This is some kind of bottom up cancellation.
Is there a preferred way to do this? Is there a design pattern that describes it some where?
The bottom up cancellation can be implemented, but it feel a bit weird. The UI-task is created with a CancellationToken, but it is canceled due to another (inner) CancellationToken. Also, since the tokens are not the same, the OperationCancelledException can't just be ignored in the UI - that would (eventually) lead to an exception being thrown in the outer Task:s finalizer.
Here's my attempt:
// the Task for the current application state
Task<Result> _task;
// a CancellationTokenSource for the current application state
CancellationTokenSource _cts;
// called when the application state changes
void OnStateChange()
{
// cancel the Task for the old application state
if (_cts != null)
{
_cts.Cancel();
}
// new CancellationTokenSource for the new application state
_cts = new CancellationTokenSource();
// start the Task for the new application state
_task = Task.Factory.StartNew<Result>(() => { ... }, _cts.Token);
}
// called by UI component
Task<Result> ComputeResultAsync(CancellationToken cancellationToken)
{
var task = _task;
if (cancellationToken.CanBeCanceled && !task.IsCompleted)
{
task = WrapTaskForCancellation(cancellationToken, task);
}
return task;
}
with
static Task<T> WrapTaskForCancellation<T>(
CancellationToken cancellationToken, Task<T> task)
{
var tcs = new TaskCompletionSource<T>();
if (cancellationToken.IsCancellationRequested)
{
tcs.TrySetCanceled();
}
else
{
cancellationToken.Register(() =>
{
tcs.TrySetCanceled();
});
task.ContinueWith(antecedent =>
{
if (antecedent.IsFaulted)
{
tcs.TrySetException(antecedent.Exception.GetBaseException());
}
else if (antecedent.IsCanceled)
{
tcs.TrySetCanceled();
}
else
{
tcs.TrySetResult(antecedent.Result);
}
}, TaskContinuationOptions.ExecuteSynchronously);
}
return tcs.Task;
}
It sounds like you want a greedy set of task operations - you have a task result provider, and then construct a task set to return the first completed operation, ex:
// Task Provider - basically, construct your first call as appropriate, and then
// invoke this on state change
public void OnStateChanged()
{
if(_cts != null)
_cts.Cancel();
_cts = new CancellationTokenSource();
_task = Task.Factory.StartNew(() =>
{
// Do Computation, checking for cts.IsCancellationRequested, etc
return result;
});
}
// Consumer 1
var cts = new CancellationTokenSource();
var task = Task.Factory.StartNew(() =>
{
var waitForResultTask = Task.Factory.StartNew(() =>
{
// Internally, this is invoking the task and waiting for it's value
return MyApplicationState.GetComputedValue();
});
// Note this task cares about being cancelled, not the one above
var cancelWaitTask = Task.Factory.StartNew(() =>
{
while(!cts.IsCancellationRequested)
Thread.Sleep(25);
return someDummyValue;
});
Task.WaitAny(waitForResultTask, cancelWaitTask);
if(cancelWaitTask.IsComplete)
return "Blah"; // I cancelled waiting on the original task, even though it is still waiting for it's response
else
return waitForResultTask.Result;
});
Now, I haven't fully tested this, but it should allow you to "cancel" waiting on a task by cancelling the token (and thus forcing the "wait" task to complete first and hit the WaitAny), and allow you to "cancel" the computation task.
The other thing is to figure out a clean way of making the "cancel" task wait without horrible blocking. It's a good start I think.

Categories