When should you check Task.IsFaulted? - c#

I found this code snippet (simplified version provided):
using System;
using System.Threading.Tasks;
namespace TaskTest
{
class Program
{
static void Main(string[] args)
{
var task = SendMessage();
task.Wait();
if (task.IsFaulted) // Never makes it to this line
{
Console.WriteLine("faulted!");
}
Console.Read();
}
private static async Task SendMessage()
{
await Task.Run(() => throw new Exception("something bad happened"));
}
}
}
I'm sure this is a bug since task.Wait(); throws and there is no catch block.
Now I'm wondering when you would need to use task.IsFaulted?

The Task is throwing the exception, not the Task.Wait().
But there is a subtle way that exceptions get bubbled up when using Task.Wait() vs Task.GetAwaiter().GetResult()
You should probably use
task.GetAwaiter().GetResult();
See this question for a good explanation of how the different Syncronous ways to wait work with exceptions.
Task.IsFaulted is useful when using await Task.WhenAny(); or any other time where you want to check the status of a Task without awaiting it, eg from another synchronization context.
I often find myself using Task.IsCompleted | Faulted | Successful to determine what feedback to give a user in a WinForms scenario.

Here is an example where the IsFaulted could be useful. Let's say that you have started two concurrent tasks, the task1 and task2, and after both of them have completed (Task.WhenAll) you want to handle the exception of each task individually. In that case the ex in the catch (Exception ex) is not useful to you, because it contains only one of the possible exceptions, and the exception is dissociated from the originating task. So you could do something like this:
try
{
await Task.WhenAll(task1, task2);
}
catch when (task1.IsFaulted || task2.IsFaulted)
{
if (task1.IsFaulted) HandleException("Task 1", task1.Exception.InnerException);
if (task2.IsFaulted) HandleException("Task 2", task2.Exception.InnerException);
}
When a task IsFaulted, it is guarantied that its Exception property will not be null. This property returns an AggregateException, with an InnerException also practically guarantied to not be null. That's because it is practically impossible to transition a task to a faulted state, without providing at least one Exception object. And the InnerException contains the first of the InnerExceptions that are stored inside an AggregateException.
In this particular example only the failure case in handled. If none of the tasks is faulted, and at least one is canceled, the TaskCanceledException will propagate.

When you await a Task asynchronously the program constantly switches between the calling context and the context of the awaited Task.(this is over generalized)
This means in SendMessage(); the program runs everything before the await call with the Main context, runs the awaited call in a Task, which may or may not run on another thread, and switched back to the original context of Main.
Because you awaited the Task within SendMessage(); the Task can properly bubble up errors to the calling context, which in this case is Main which halts the program.
Both .Wait() and await bubble errors back to the calling context.
In your example of you removed the .Wait();, the Task would run parallel (run synchronously in it's own context on another thread) and no errors would be able to bubble back to Main.
Think of it like you are cooking a two course meal. You could cook it asynchronously by constantly walking between two cooking stations and doing tasks at each a little at a time. Alternatively you could have a friend cook the other meal in parallel with you.
When you cook both meals yourself you will know immediately if you've burned your steak. But if you have your friend cook the steak, but he sucks at cooking steak, you won't know he's burned the steak until you check his work(.IsFaulted).

Related

Test run stops when doing several multi-threaded tests in a row

I've got a class with a static ConcurrentQueue. One class receives messages and puts them on the queue, whilst a different thread on this class reads them from that queue and processes them one at a time. That method is aborted with a cancellationtoken.
The method that empties the queue looks like this:
public async Task HandleEventsFromQueueAsync(CancellationToken ct, int pollDelay = 25)
{
while (true)
{
if (ct.IsCancellationRequested)
{
return;
}
if(messageQueue.TryDequeue(out ConsumeContext newMessage))
{
handler.Handle(newMessage);
}
try
{
await Task.Delay(pollDelay, ct).ConfigureAwait(true);
}
catch (TaskCanceledException)
{
return;
}
}
}
My testing methods look like this:
CancellationToken ct = source.Token;
Thread thread = new Thread(async () => await sut.HandleEventsFromQueueAsync(ct));
thread.Start();
EventListener.messageQueue.Enqueue(message1);
EventListener.messageQueue.Enqueue(message2);
await Task.Delay(1000);
source.Cancel(false);
mockedHandler.Verify(x => x.Handle(It.IsAny<ConsumeContext>()), Times.Exactly(2));
So I start my dequeueing method in its own thread, with a fresh cancellation token. Then I enqueue a couple of messages, give the process a second to handle them, and then use source.Cancel(false) to put an end to the thread and make the method return. Then I check that the handler was called the right number of times. Of course I'm testing this in a couple variations, with different message types and different times when I abort the dequeueing method.
The issue is that when I run any of my tests individually, they all succeed. But when I try to run them as a group, Visual Studio does not run every test. There's no error message, and the tests it does run succeed fine, but the run just stops after the second test.
I do not have an idea why this happens. My tests are all identical in structure. I'm aborting the dequeueing thread properly every time.
What could compel Visual Studio to stop a test run, without throwing any kind of error?
You are passing an async lambda to the Thread constructor. The Thread constructor doesn't understand async delegates (does not accept a Func<Task> argument), so you end up with an async void lambda. Async void methods should be avoided for anything that it's not an event handler. What happens in your case is that the explicitly created thread is terminated when the code hits the first await, and the rest of the body runs in ThreadPool threads. It seems that the code never fails with an exception, otherwise the process would crash (this is the default behavior of async void methods).
Suggestions:
Use a Task instead of a Thread. This way you'll have something to await before exiting the test.
CancellationToken ct = source.Token;
Task consumerTask = Task.Run(() => sut.HandleEventsFromQueueAsync(ct));
EventListener.messageQueue.Enqueue(message1);
EventListener.messageQueue.Enqueue(message2);
await Task.Delay(1000);
source.Cancel(false);
await consumerTask; // Wait the task to complete
mockedHandler.Verify(x => x.Handle(It.IsAny<ConsumeContext>()), Times.Exactly(2));
Consider using a BlockingCollection or an asynchronous queue like a Channel instead of a ConcurrentQueue. Polling is an awkward and inefficient technique. With a blocking or async queue you'll not be obliged to do loops waiting for new messages to arrive. You'll be able to enter a waiting state, and notified instantly when a new message arrives.
Configure the awaiting with ConfigureAwait(false). ConfigureAwait(true) is the default and does nothing.
Consider propagating cancellation by throwing an OperationCanceledException. This is the standard way of propagating cancellation in .NET. So instead of:
if (ct.IsCancellationRequested) return;
...it is preferable to do this:
ct.ThrowIfCancellationRequested();
I have solved my own issue. Turns out that the newly created thread threw an exception, and when threads throw exceptions those are ignored, but they still stop the unit test from happening. After fixing the issue causing the exception, the tests work fine.

Waiting for a Task conditionally

For an async method that returns a Task<bool>, I need to take some actions when the method completes. The async method looks like this:
async Task<bool> EntryExists(string entry)
{
return await Task.Run(() => call_that_returns_bool());
}
I call it and attach a continuation task to it to perform follow-up actions:
EntryExists(my_entry).ContinueWith(t =>
{
if(t.Result) ...
});
However, I now need to conditionally wait for chained task to complete. Means depending upon a parameter, I either need to return immediately to the caller, or wait till the tasks finish. I change the above code to look like this:
var T = EntryExists(my_entry).ContinueWith(t =>
{
if(t.Result) ...
});
if(wait) T.Wait(); //wait is my parameter
Running this, the code gets stuck forever at T.Wait() when wait parameter is true, as if T never kicked off. I then tried the following:
var T = EntryExists(my_entry).ContinueWith(t =>
{
if(t.Result) ...
});
T.Start();
if(wait) T.Wait();
Upon which it tells me that
Start may not be called on a continuation task
I know I'm missing something basic, but after been coding for the last 15 hours, my brain isn't helping much. Can someone point out what I need to do?
You shouldn't block on async code. In short, you're very likely to be blocking the thread that the async method needs to return to (in this case you are, as it's the UI thread). The solution is to use async-await all the way through, rather than trying to run it synchronously. If you absolutely have to present a synchronous method, then at least provide as simpler wrapper as possible, rather than mixing ContinueWith and async-await.
Your outer call can be rewritten as:
async Task<{something}> MakeCallAndContinue()
{
try
{
await EntryExists(my_entry);
// additional continuation code
}
catch (Exception e)
{
// handle in some way
}
}
var task = MakeCallAndContinue();
if (wait) await task;
It's unusual to want to start a task and then not await it, which is what you're doing if wait is false. The error handler I've put in the above code is to ensure that you don't get an exception thrown on to an unawaited task. If you did that, then it would be thrown out somewhere else, and probably kill your process if you haven't declared a global handler.
You won't be able to use the above in your WPF command as-is, because it's async. However, you can have async WPF command handlers, as explained here. If you did want to do it synchronously then you would need to call .Wait(), but as Stephen Cleary explains in my first link, you have to use ConfigureAwait(false) on all awaited tasks all the way down in order to prevent one of them from trying to return to the occupied UI thread, and deadlocking.

How to make sure a task is started and safely start it if not?

I get an IEnumerable<Task> tasks from somewhere that I do not control. I don't know if the tasks are manually created using new Task, Task.Run, or if they are a result of an async method call async Task DoSomethingAsync().
If I do await Task.WhenAll(tasks), I risk hanging indefinitely because maybe one or more of the tasks are not started.
I can't do tasks.ForEach(t => t.Start()), because then I will get an InvalidOperationException "Start may not be called on a promise-style task" if it's from an async method call (already started).
I can't do await Task.WhenAll(tasks.Select(t => Task.Run(async () => await t))) because each t still does not start just by awaiting it.
I assume the solution has something to do with checking each task's Status and Start() based on that, but I also assume that it can be tricky because that status could change at any time, right? If this is still the way to go, which statuses would be correct to check and what threading issues should I worry about?
Non working case example:
//making an IEnumerable as an example, remember I don't control this part
Task t = new Task( () => Console.WriteLine("started"));
IEnumerable<Task> tasks = new[] {t};
//here I receive the tasks
await Task.WhenAll(tasks);//waits forever because t is not started
Working case example:
//calls the async function, starting it.
Task t = DoSomethingAsync();
IEnumerable<Task> tasks = new[] {t};
//here I receive the tasks and it will complete because the task is already started
await Task.WhenAll(tasks);
async Task DoSomethingAsync() => Console.WriteLine("started");
If for whatever reason you cannot change the code to not return unstarted tasks, you can check Status and start task if it has Created status:
if (task.Status == TaskStatus.Created)
task.Start();
All other task statues indicate that task is either completed, running, or being scheduled, so you don't need to start tasks in that statuses.
Of course in theory this introduces race condition, because task can be started right between your check and Start call, but, as correctly pointed by Servy in comments - if there ever is race condition here - that means another party (which created that task) is also trying to start it. Even if you handle exception (InvalidOperationException) - another party is unlikely to do that, and so will get exception while trying to start their own task. So only one side (either you, or code that created that task) should be trying to start it.
That said - much better than doing this is to ensure you might never get unstarted task in the first place, because it's just bad design to return such tasks to external code, at least without explicitly indicating that (while it's for some use cases ok to use unstarted task internally).

How do I start not-awaited background tasks in an async method?

I'm struggling with how to perform some very-long-running background processing in a world of async methods.
Using the vocabulary from Stephen Cleary's blog, I'm interested in kicking off a "delegate task" after await-ing a "promise task". I want to return the promised value as soon as it's available, and let the delegate task continue on in the background.
To handle exceptions in the un-await-ed delegate task, I'll use an exception-handling continuation modeled after the one described here.
public async Task<int> ComplexProcessAsync()
{
...
int firstResult = await FirstStepAsync();
// THE COMPILER DOESN'T LIKE THIS
Task.Run(() => VeryLongSecondStepIDontNeedToWaitFor(firstResult)).LogExceptions();
return firstResult;
}
Since VeryLongSecondStep...() could take many minutes, and its results are observable purely by side-effect (e.g. records appearing in a database), I reeeaaalllyy don't want to await it.
But as pointed out, the compiler doesn't like this. It warns "Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call."
I could ignore the warning, but I get the feeling I'm not doing this in "correct" way. So what is the correct way?
Thanks!
I could ignore the warning, but I get the feeling I'm not doing this in "correct" way. So what is the correct way?
The compiler is warning you that you're not awaiting the task. From my experience, >99% of the time the compiler is correct, the code is wrong because it's missing an await. So this is one of the rare situations where you know what you're doing and want to live dangerously. :)
In this case, you can explicitly assign the task to an unused variable:
var _ = Task.Run(() => VeryLongSecondStepIDontNeedToWaitFor(firstResult));
The compiler is intelligent enough to understand that you're using this variable to silence the "missing await" warning.
On a side note, I do not recommend ContinueWith at all; in the async world, it's just a more dangerous version of await. So I'd write LogExceptions something like this:
public static async Task LogExceptions(this Task task)
{
try
{
await task.ConfigureAwait(false);
}
catch (Exception ex)
{
LogException(ex);
}
}
Since this is within an ASP.NET Web API app I suggest you use HostingEnvironment.QueueBackgroundWorkItem. Stephen Cleary also has a post about it.
HostingEnvironment.QueueBackgroundWorkItem(ct =>
{
try
{
// your code
}
catch (Exception ex)
{
// handle & log exception
}
});
https://msdn.microsoft.com/en-us/library/dn636893(v=vs.110).aspx
http://blog.stephencleary.com/2014/06/fire-and-forget-on-asp-net.html
The compiler is trying to protect you from mistakes because it's really easy to forget the await when you actually need to await something, but in this case you really don't need the await, so you can ignore the warning.
However, in this case I think it's a better idea to run the task on some thread from the threadpool, because if you're running it on the main thread like you're doing now, you might cause your application to be non-responsive for new incoming requests/events.
I'd like to suggest you to move VeryLongSecondStepIDontNeedToWaitFor() out of the ComplexProcessAsync() method.
ComplexProcessAsync() itself returns a value, that is being used by at least two tasks (one of them is Task.Run(() => VeryLongSecondStepIDontNeedToWaitFor(firstResult))).
You can combine these tasks via Task.WhenAny():
var result = await ComplexProcessAsync();
await Task.WhenAny(VeryLongSecondStepIDontNeedToWaitFor(firstResult), YourSecondMethod(firstResult))
where YourSecondMethod(firstResult) is the method that uses the ComplexProcessAsync() result.

What is the proper way to delay with cancellation support inside of a task execution delegate?

I don't see any specific mention either on MSDN or here about how to accomplish this. The use case is somewhat obscure, but still valid I suspect.
var cancel = new CancellationTokenSource();
var task = Task.Factory.StartNew(() => { Task.Delay(1000, cancel.Token).Wait(); }, cancel.Token);
cancel.CancelAfter(100);
task.Wait();
The above code will attempt to cancel a task that contains a detached child delay task after 100 milliseconds, and wait for the task to complete which will generate an AggregateException (due to the cancellation). The problem with this is that the task becomes faulted instead of cancelled. This is expected behaviour because the delay task is not attached to the parent task, even though both share the same cancellation token.
My question relates specifically to how you would go about attaching a Task.Delay to a task that is already running. Is it possible to even do this if you had access to the parent task? If it isn't possible, or not possible without access to the parent task instance what is the proper way to handle this scenario?
The best work around I could come up with was to wrap the delay task's Wait in a try/finally block, and explicitly attempt to bubble up the task cancellation.
try { Task.Delay(1000, cancel.Token).Wait(); } finally { cancel.Token.ThrowIfCancellationRequested(); }
While effective, it doesn't feel quite right, but I'm not sure if there is a better way to accomplish this. The desired outcome is that the parent task goes to Canceled instead of Faulted if cancellation occurs. So if the genesis of the cancellation occurs in a detached child task, the parent task should still transition to Canceled.
NOTE: I left out async/await here on purpose, just because it doesn't appear to change the problem or the result. If that's not the case please provide an example.
So, a task is considered canceled when an OperationCanceledException is thrown and uncaught inside it and its associated CancellationToken is canceled.
In your case the exception being thrown is AggregateException that contains a TaskCanceledException (which is an OperationCanceledException) instead of the TaskCanceledException directly.
There's a simple way to fix that. Instead of synchronously blocking with Task.Wait which wraps any exceptions in an AggregateException wrapper you can use task.GatAwaiter().GetResult(). That is what await uses in async-await. It throws the original exception and if there were multiple ones it throws the first:
var cancel = new CancellationTokenSource();
var task = Task.Factory.StartNew(() => { Task.Delay(1000, cancel.Token).GetAwaiter().GetResult(); }, cancel.Token);
cancel.CancelAfter(100);
task.Wait();
If you would have used async-await you wouldn't have this issue as unlike Task.Wait it throws the TaskCanceledException itself:
var cancel = new CancellationTokenSource();
var task = Task.Run(() => Task.Delay(1000, cancel.Token), cancel.Token);
cancel.CancelAfter(100);
task.Wait();
I assume this is just an example. Real production code shouldn't resembles this as you're blocking synchronously on an asynchronous operation which in turn blocks synchronously on an asynchronous operation.

Categories