How to catch CancellationToken.ThrowIfCancellationRequested - c#

When this section of code is executed
cancellationToken.ThrowIfCancellationRequested();
The try catch block doesn't handle the exception.
public EnumerableObservable(IEnumerable<T> enumerable)
{
this.enumerable = enumerable;
this.cancellationSource = new CancellationTokenSource();
this.cancellationToken = cancellationSource.Token;
this.workerTask = Task.Factory.StartNew(() =>
{
try
{
foreach (var value in this.enumerable)
{
//if task cancellation triggers, raise the proper exception
//to stop task execution
cancellationToken.ThrowIfCancellationRequested();
foreach (var observer in observerList)
{
observer.OnNext(value);
}
}
}
catch (AggregateException e)
{
Console.Write(e.ToString());
}
}, this.cancellationToken);
}

AggregateExceptions are thrown when a possible multitude of exceptions during asynchronous operations occured. They contain all exceptions that were raised e.g. in chained Tasks (via .ContinueWith) or within cascaded async/await calls.
As #Mitch Stewart pointed out, the correct exception-type to handle would be OperationCancelledException in your example.

Since ThrowIfCancellationRequested() throws an exception of type OperationCanceledException, you must catch OperationCanceledException or one of its base classes.
https://msdn.microsoft.com/en-us/library/system.operationcanceledexception(v=vs.110).aspx

Related

C# Throw OperationCanceledException from inside CancellationToken.Register

I have a long running operation that I want to cancel after, say 5 secs. Unfortunately, polling for IsCancellationRequested is not possible (long story).
I used the code below to throw an OperationCanceledException inside the cancellation callback. I wanted to catch the exception in the main thread and handle it so that I can exit the application completely.
This doesn't seem to work properly as this results in an unhandled exception and the application doesn't terminate gracefully.
Any help is appreciated. Thanks!
void TestTimeOut()
{
var cts = new CancellationTokenSource();
cts.CancelAfter(5000);
try
{
var task = Task.Run(() => LongRunningOperation(cts.Token));
task.ContinueWith(t => Console.WriteLine("Operation cancelled"), TaskContinuationOptions.OnlyOnFaulted);
task.Wait();
}
catch (AggregateException e)
{
//Handle
}
}
void LongRunningOperation(CancellationToken token)
{
CancellationTokenRegistration registration = token.Register(
() =>
{
throw new OperationCanceledException(token);
});
using (registration)
{
// long running operation here
}
}
Your code has many No-No-es, but I guess you just using it as a demo for your problem.
The Solution is TaskCompletionSource, My Demo is ugly too, too many layers of Task.Run(), if you use Async, you should async all the way down. So don't use it in PRD, study TaskCompletionSource yourself and figure out a better solution.
static void LongRunningOperation(CancellationToken token)
{
TaskCompletionSource<int> tcs1 = new TaskCompletionSource<int>();
token.Register(() => { tcs1.TrySetCanceled(token); });
Task.Run(() =>
{
// long running operation here
Thread.Sleep(10000);
tcs1.TrySetResult(0);
}, token);
tcs1.Task.Wait();
}
You are catching an AggregateException but actuall throwing an OperationCanceledException which will not be caught.
Change to catch all types of exceptions such as
catch (Exception ex) { ... }
to resolve.

C# Task.WhenAll handling Exceptions

I need help with a simple Task.WhenAll C# code. I have upto 50 different tasks being fired at the same time, however, some of these calls could return an error message.
I am trying to write the exception handling code, so that I can process the ones that worked (which it does), but capture the ones that errored, so I can perform additional code against them.
There is AggregateException, but is there a way to see which call/inputs that created that exception?
I'm unable to share the actual code due to strict company policy, but an example is as follows:
List<ListDetails> libs = getListDetails();
var tasks = new Task<List<ItemDetails>>[libs.Count];
for (int i = 0; i < libs.Count; i++)
{
tasks[i] = getListItems(libs[i].ServerRelativeUrl, libs[i].ListId);
}
try
{
await Task.WhenAll(tasks);
}
catch(AggregateException aex)
{
//Capture which Server RelativeUrls and ListIDs that failed.
}
You can query the original tasks after waiting:
var myTasks = ...;
try {
await Task.WhenAll(myTasks);
}
catch (AggregateException) {
//swallow everything
}
foreach (var task in myTasks) {
if (myTask.Status == RanToCompletion)
...
}
You should check the InnerExceptions property, or you can use AggregateException.Flatten method as shown in the docs:
try {
task1.Wait();
}
catch (AggregateException ae) {
foreach (var e in ae.Flatten().InnerExceptions) {
if (e is CustomException) {
Console.WriteLine(e.Message);
}
else {
throw;
}
}
}
This will give you all the exceptions that are thrown by the child tasks but unfortunately there is no way to know which task threw a particular exception. You will need to investigate the stack traces.
You should make a array of async tasks then when you are going to add each task to array you should place a anonymous function with lambda expression in ContinueWith(){}. Here is an example :
var tasks = new List<Task>();
tasks.add(t.ContinueWith(o =>
{
if(o.IsFaulted){
Console.WriteLine(o.Exception?.InnerException);
return;
}
Console.WriteLine(o.Result);
}));
tasks.WhenAll().ContinueWith(o=>{
Console.WriteLine("All Done!")
});

Getting the completed task Id using Task.WaitAny()

I know that my goal can be achieved by using Task.WhenAny() but I will not deal with async await if I can avoid it, in order to prevent deadlocks. I have following code:
try
{
Task.WaitAny(this.Tasks.ToArray());
foreach (var task in this.Tasks)
{
if (task.IsFaulted || task.IsCanceled)
{
if (task.Exception.InnerException is OperationCanceledException)
{
}
}
}
}
catch (OperationCanceledException o)
{
// Handling cancelled tasks
}
catch (Exception e)
{
// Handling faulted tasks
}
And I for instance want to know exactly the id of my task, which has faulted or the Id of my task which has been cancelled. I have tried to do so as shown in the try block above, but this is not a solution since it also will throw an exception for tasks that has been cancelled before. Can I obtain a solution for this problem using Task.WaitAny() ?.
From the documentation of Task.WaitAny:
Return Value
Type: System.Int32
The index of the completed Task object in the tasks array.
So you can do this:
var taskIndex = Task.WaitAny(this.Tasks.ToArray());
var task = this.Tasks[taskIndex];

Rethrowing exception in Task doesn't make the Task to go to faulted state

Consider following scenario
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(2));
var startNew = Task.Factory.StartNew(() =>
{
var currentThread = Thread.CurrentThread;
try
{
using (cancellationTokenSource.Token.Register(currentThread.Abort))
new AutoResetEvent(false).WaitOne(Timeout.InfiniteTimeSpan);
}
catch (ThreadAbortException abortException)
{
throw new TimeoutException("Operation timeouted", abortException);
}
}, cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Current);
startNew.ContinueWith(val => Console.WriteLine("Cancellation handled"), TaskContinuationOptions.OnlyOnCanceled);
startNew.ContinueWith(val => Console.WriteLine("Fault handled"), TaskContinuationOptions.OnlyOnFaulted);
startNew.ContinueWith(val => Console.WriteLine("Ran to completion handled"), TaskContinuationOptions.OnlyOnRanToCompletion);
Putting aside all the discussion that aborting threads is evil, why does this code doesn't make the task to go to Faulted state? However removing catch block or calling
Thread.ResetAbort()
seems to do the trick
It is about how thread abort works:
try {
try {
try {
Thread.CurrentThread.Abort();
} catch(Exception e) {
Console.WriteLine(e.GetType());
throw new Exception();
}
} catch(Exception e) {
Console.WriteLine(e.GetType());
}
} catch(Exception e) {
Console.WriteLine(e.GetType());
}
This code prints:
System.Threading.ThreadAbortException
System.Exception
System.Threading.ThreadAbortException
So, when your custom exception would be handled, ThreadAbortException whould be re-thrown.
ThreadAbortException is a special exception that can be caught by application code, but is re-thrown at the end of the catch block unless ResetAbort is called. MSDN
Now let us look to some source:
/// <summary>
/// Executes the task. This method will only be called once, and handles bookeeping associated with
/// self-replicating tasks, in addition to performing necessary exception marshaling.
/// </summary>
private void Execute()
{
if (IsSelfReplicatingRoot)
{
ExecuteSelfReplicating(this);
}
else
{
try
{
InnerInvoke();
}
catch (ThreadAbortException tae)
{
// Don't record the TAE or call FinishThreadAbortedTask for a child replica task --
// it's already been done downstream.
if (!IsChildReplica)
{
// Record this exception in the task's exception list
HandleException(tae);
// This is a ThreadAbortException and it will be rethrown from this catch clause, causing us to
// skip the regular Finish codepath. In order not to leave the task unfinished, we now call
// FinishThreadAbortedTask here.
FinishThreadAbortedTask(true, true);
}
}
catch (Exception exn)
{
// Record this exception in the task's exception list
HandleException(exn);
}
}
}
As you can see, there is special codepath for ThreadAbortException case to transit task to the faulted state. As you hide ThreadAbortException by TimeoutException, that special codepath are not taken. So when regular codepath handle exception by recording it in the task's exception list, ThreadAbortException would be re-thrown, which prevent correct task transition to the faulted state.

Exception is not caught at Cancelation of Task.Run

I have a class Worker which is doing some work (with simulated workload):
public class Worker
{ ...
public void DoWork(CancellationToken ct)
{
for (int i = 0; i < 10; i++)
{
ct.ThrowIfCancellationRequested();
Thread.Sleep(2000);
}
}
Now I want to use this method in a Task.Run (from my Windows Forms App,at button-click) which can be cancelled:
private CancellationTokenSource _ctSource;
try
{
Task.Run(() =>
{
_worker.DoWork(_ctSource.Token);
},_ctSource.Token);
}
catch (AggregateException aex)
{
String g = aex.Message;
}
catch (OperationCanceledException ex)
{
String g = ex.Message;
}
catch (Exception ex)
{
String g = ex.Message;
}
But when the task is started, I can't cancel it with _ctSource.Cancel();
I get an error in visual studio that the OperationCanceledException is not handled!
But I surrounded the Task.Run Call in a try-catch-clause! The Exception which ocurrs in the Worker object should thrown up or not?
What is the problem?
Your Task.Run call creates the task and then returns immediately. It doesn't ever throw. But the task it creates may fail or be canceled later on.
You have several solutions here:
Use await:
await Task.Run(...)
Attach a continuation depending on the failure/cancellation case:
var task = Task.Run(...);
task.ContinueWith(t => ..., TaskContinuationOptions.OnlyOnCanceled);
task.ContinueWith(t => ..., TaskContinuationOptions.OnlyOnFaulted);
Attach a single continuation on failure:
Task.Run(...).ContinueWith(t => ..., TaskContinuationOptions.NotOnRanToCompletion);
The solution you can/should use depends on the surrounding code.
You need to new the token
private CancellationTokenSource _ctSource = new CancellationTokenSource();
Why are throwing an expectation in DoWork?
Exception from one thread don't bubble up another thread that started the thread.
Cancellation in Managed Threads
If a parallel Task throws an exception it'll return execution and will have it's Exception property (as an AggregateException, you should check for its InnerException) set (and either its IsCanceled or IsFaulted property set to true). Some minimal sample code from a project of mine which escalates the exception to the main thread:
var t = new Task(Initialize);
t.Start();
while (!t.IsCompleted && !t.IsFaulted)
{
// Do other work in the main thread
}
if (t.IsFaulted)
{
if (t.Exception != null)
{
if(t.Exception.InnerException != null)
throw t.Exception.InnerException;
}
throw new InvalidAsynchronousStateException("Initialization failed for an unknown reason");
}
If you use a CancellationTokenSource it should be easy to enhance this to check for IsCanceled (instead of IsFaulted)
You can also use Task.Wait() instead of the while loop... in my project and in that precise case it seemed more appropiate to use the while loop, but you need to wait for the Task to end in one way or another.
If you use Task.Run() you can use a .ContinueWith(Task) which will have the original task passed in (where you can check for IsFaulted or IsCanceled), or have it run only on faulted execution, at your will.

Categories

Resources