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:
This code prints:
So, when your custom exception would be handled,
ThreadAbortException
whould be re-thrown.Now let us look to some source:
As you can see, there is special codepath for
ThreadAbortException
case to transit task to the faulted state. As you hideThreadAbortException
byTimeoutException
, 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.