Possibly redundant use of Task.FromResult - c#

Section 10.2 within Concurrency in .NET provides the following listing for an Otherwise task combinator:
public static Task<T> Otherwise<T>(this Task<T> task, Func<Task<T>> orTask) =>
task.ContinueWith(async innerTask =>
{
if (innerTask.Status == TaskStatus.Faulted) return await orTask();
return await Task.FromResult<T>(innerTask.Result);
}).Unwrap();
(Confusingly, within the printed book, Otherwise is marked as async which fails to compile; however, not in the GH listing which compiles without issue)
My "understanding" is that if the anonymous continuation is called, it would suggest that the supplied innerTask has completed by that point, successfully or otherwise.
Assuming successful, it would also suggest that innerTask.Result would already be determined with no synchronous waiting required upon calling that member property.
On the mild assumption that above is sensible... What possible benefit is there from using return await Task.FromResult<T>(innerTask.Result) as opposed to just return innerTask.Result? (the latter of which also compiles).
Unless I'm missing something (quite likely)... It feels overly verbose.
Update:
Thank you to everyone who has contributed; I never anticipated this would get as much feedback as it has!
At one point, I was concerned about a situation where innerTask.Result could raise an exception and how that would be dealt with by the 2x return approaches above.
From my own testing since, I can see that if innerTask was cancelled and, hence innerTask.Status == TaskStatus.Cancelled, the combinator implementation as given would permit the accessing of innerTask.Result which would then raise an AggregateException.
However, there is no difference in how this (or any other exception I imagine) would be treated by either return approach from within an async function.
Given this, and the information provided, it appears as though the former approach offers nothing over and above the latter. If anything, it (IMHO) obscurs the intention and has sent me down a (albeit illuminating) rabbit hole.

What possible benefit is there from using return await Task.FromResult<T>(innerTask.Result) as opposed to just return innerTask.Result?
Absolutely zero benefit. That's the kind of code that your end up writing when you are struggling with a concept that you don't fully understand, and you reach to a version that compiles and works correctly. Your code might end up decorated with some useless cargo, which nonetheless is not harmful, so it might survive multiple code reviews and revisions.
Here is a better way to write the Otherwise extension method:
public static Task<T> Otherwise<T>(this Task<T> task, Func<Task<T>> onErrorTaskFactory)
{
return task.ContinueWith(innerTask =>
{
if (innerTask.IsFaulted) return onErrorTaskFactory();
return innerTask;
}, default, TaskContinuationOptions.DenyChildAttach |
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default).Unwrap();
}
Notice that the continuation is not async, and it returns the innerTask, not its result. With this version you get these benefits:
In case the onErrorTaskFactory() returns a Task that also fails, and it contains more that one exceptions (think of passing a lambda that return Task.WhenAll()), all the errors will be propagated, not just the first one.
In case the innerTask is canceled, the cancellation will be propagated without an additional internal try/catch. Throwing and catching exceptions in C# is quite expensive.
The code complies with the CA2008 guideline ("Do not create tasks without passing a TaskScheduler").
The onErrorTaskFactory will be invoked synchronously on the thread that completed the innerTask, avoiding a thread-switch.
These benefits are not ground-breaking. I would evaluate the original implementation in the book as quite decent. It doesn't have any terrible bugs or inefficiencies.

Related

Is there anything wrong with this shortcut for a "happy path" for Task / ContinueWith?

For some heavy caching / high performance path in my project I need to "cast" a Task<X> to a Task<Y> type - see here.
I used the ContinueWith solution, but I noticed that even when my original task is in a RanToCompletion state, the ContinueWith still returns a Task that is WaitingToRun.
var task = Task.FromResult("cheese");
var test = task.ContinueWith(x => (object)x);
Console.WriteLine(test.Status); // WaitingToRun
Console.ReadLine();
For reasons out of the scope of this question, this gives me some extra cost further down the line.
If I do the following, my very specific problem seems to be fixed and it will directly return a CompletedTask, otherwise it does the normal logic.
if ( task.IsCompletedSuccessfully )
return Task.FromResult((object)task.Result);
else
return task.ContinueWith(x => (object)x);
Is it safe to add this "happy path" to my logic?
Because this path is not in the normal ContinueWith logic I am afraid I am missing something and this will bite me when running in production in a high concurrent environment.
As is the case just about anytime ContinueWith comes up, the answer is to use await instead. Using ContinueWith correctly is really hard, and it's almost never the most effective solution to any given problem.
You've found one problem with ContinueWith, namely that without being really careful with your scheduler (or manually checking if the task is completed) you've ended up forcing work to be scheduled into a thread pool thread to run later when it just doesn't need to be. (That won't always happen, so you can't rely on that behavior, but it's possible, which is frankly, worse.)
Additionally, your ContinueWith code doesn't handle errors/cancellation properly. In your code cancelled tasks become errored instead, and errored tasks have their exception wrapped in an AggregateException.
Your code also has a bug in that you don't get the result from the task before casting it, and thus the result of your task is another task cast to an object, but that part is easily fixed by just adding in a Result call.
Here is a pre-await solution attempting to solve these problems, but in a post-await world, there's just no need.
public static async Task<TResult> Cast<TSource, TResult>(
this Task<TSource> task)
{
return (TResult)(object) await task.ConfigureAwait(false);
}
Not only is await a more effective syntax for scheduling continuations, but it's default behavior is much more likely to be correct in any given situation than the default behaviors of ContinueWith. It'll check if the task is completed and only schedule a continuation to run later if it's not, it doesn't wrap exceptions in an AggregateException, etc. The only default behavior that we don't want here is that by default await maintains the current synchronization context, which is often correct, but happens to not be needed for our method here.

Is it safe to use .Result in a task that IsCompleted? [duplicate]

I'm currently reading "Concurrency in C# Cookbook" by Stephen Cleary, and I noticed the following technique:
var completedTask = await Task.WhenAny(downloadTask, timeoutTask);
if (completedTask == timeoutTask)
return null;
return await downloadTask;
downloadTask is a call to httpclient.GetStringAsync, and timeoutTask is executing Task.Delay.
In the event that it didn't timeout, then downloadTask is already completed. Why is necessary to do a second await instead of returning downloadTask.Result, given that the task is already completed?
There are already some good answers/comments here, but just to chime in...
There are two reasons why I prefer await over Result (or Wait). The first is that the error handling is different; await does not wrap the exception in an AggregateException. Ideally, asynchronous code should never have to deal with AggregateException at all, unless it specifically wants to.
The second reason is a little more subtle. As I describe on my blog (and in the book), Result/Wait can cause deadlocks, and can cause even more subtle deadlocks when used in an async method. So, when I'm reading through code and I see a Result or Wait, that's an immediate warning flag. The Result/Wait is only correct if you're absolutely sure that the task is already completed. Not only is this hard to see at a glance (in real-world code), but it's also more brittle to code changes.
That's not to say that Result/Wait should never be used. I follow these guidelines in my own code:
Asynchronous code in an application can only use await.
Asynchronous utility code (in a library) can occasionally use Result/Wait if the code really calls for it. Such usage should probably have comments.
Parallel task code can use Result and Wait.
Note that (1) is by far the common case, hence my tendency to use await everywhere and treat the other cases as exceptions to the general rule.
This makes sense if timeoutTask is a product of Task.Delay, which I believe what it is in the book.
Task.WhenAny returns Task<Task>, where the inner task is one of those you passed as arguments. It could be re-written like this:
Task<Task> anyTask = Task.WhenAny(downloadTask, timeoutTask);
await anyTask;
if (anyTask.Result == timeoutTask)
return null;
return downloadTask.Result;
In either case, because downloadTask has already completed, there's a very minor difference between return await downloadTask and return downloadTask.Result. It's in that the latter will throw AggregateException which wraps any original exception, as pointed out by #KirillShlenskiy in the comments. The former would just re-throw the original exception.
In either case, wherever you handle exceptions, you should check for AggregateException and its inner exceptions anyway, to get to the cause of the error.

Split async method into two for code analysis?

I have code:
public async Task DeleteColorSchemeAsync(ColorScheme colorScheme)
{
if (colorScheme == null)
throw new ArgumentNullException(nameof(colorScheme));
if (colorScheme.IsDefault)
throw new SettingIsDefaultException();
_dbContext.ColorSchemes.Remove(colorScheme);
await _dbContext.SaveChangesAsync();
}
One code analyzer recommends me to split this method to 2 methods:
Split this method into two, one handling parameters check and the other handling the asynchronous code
Am I correct, when I split this code in the following way?
public async Task DeleteColorSchemeAsync(ColorScheme colorScheme)
{
if (colorScheme == null)
throw new ArgumentNullException(nameof(colorScheme));
if (colorScheme.IsDefault)
throw new SettingIsDefaultException();
await DeleteColorSchemeInternalAsync(colorScheme);
}
private async Task DeleteColorSchemeInternalAsync(ColorScheme colorScheme)
{
_dbContext.ColorSchemes.Remove(colorScheme);
await _dbContext.SaveChangesAsync();
}
What is different for the compiler? It sees two async methods, what is different from my first variant?
used code tool analyzator: sonarqube
Assuming you wanted to follow the code analysis advice, I would not make the first method async. Instead, it can just do the parameter validation, and then return the result of calling the second:
public Task DeleteColorSchemeAsync(ColorScheme colorScheme)
{
if (colorScheme == null)
throw new ArgumentNullException(nameof(colorScheme));
if (colorScheme.IsDefault)
throw new SettingIsDefaultException();
return DeleteColorSchemeInternalAsync(colorScheme);
}
private async Task DeleteColorSchemeInternalAsync(ColorScheme colorScheme)
{
_dbContext.ColorSchemes.Remove(colorScheme);
await _dbContext.SaveChangesAsync();
}
All that said, in my opinion there's not really a strong justification to split the method like that. The SonarQube's rule, Parameter validation in "async"/"await" methods should be wrapped is IMHO overly cautious.
The compiler uses the same kind of transformation on async methods as it does for iterator methods. With an iterator method, there is value in doing parameter validation in a separate method, because otherwise it won't be done until the caller tries to get the first element in the sequence (i.e. when the compiler-generated MoveNext() method is called).
But for async methods, all of the code in the method up to the first await statement, including any parameter validation, will be executed on the initial call to the method.
The SonarQube rule appears to be based on a concern that until the Task is observed, any exception generated in the async method won't be observed. Which is true. But the typical calling sequence of an async method is to await the returned Task, which will observe the exception immediately on completion, which of course occurs when the exception is generated, and will happen synchronously (i.e. the thread won't be yielded).
I admit that this is not hard-and-fast. For example, one might initiate some number of async calls, and then use e.g. Task.WhenAll() to observe their completions. Without immediate parameter validation, you would wind up starting all of the tasks before realizing that one of the calls was invalid. And this does violate the general principle of "fail-fast" (which is what the SonarQube rule is about).
But, on the other hand, parameter validation failures are almost always due to user code incorrectness. I.e. they don't happen because of data input problems, but rather because the code was written incorrectly. "Fail-fast" is a bit of a luxury in that context; what's more important, to me anyway, is that the code be written in a natural, easy-to-follow way, and I'd argue that keeping everything in one method achieves that goal better.
So in this case, the advice being given by SonarQube isn't necessary to follow. You can just leave your async method as a single method, the way you had it originally without harming the code. Even more so than the iterator method scenario (which has similar arguments pro and con), there is IMHO just as much reason to leave the validation in the async method as to remove it to a wrapper method.
But if you do choose to follow SonarQube's advice, the example I provided above is IMHO a better approach than what you have (and indeed, is more in line with the detailed advice on SonarQube's documentation).
I will note that in fact, there's an even simpler way to express the code:
public Task DeleteColorSchemeAsync(ColorScheme colorScheme)
{
if (colorScheme == null)
throw new ArgumentNullException(nameof(colorScheme));
if (colorScheme.IsDefault)
throw new SettingIsDefaultException();
_dbContext.ColorSchemes.Remove(colorScheme);
return _dbContext.SaveChangesAsync();
}
I.e. don't make the implementation async at all. Your code doesn't need async because there's only one await and it occurs at the very end of the method. Since your code doesn't actually need control returned to it, there's not actually any need to make it async. Just do all the synchronous stuff you need to do (including parameter validation), and then return the Task you'd otherwise have awaited.
And, I'll note as well, this approach addresses both the code analysis warning, and keeps the implementation simple, as a single method with parameter validation built-in. Best of both worlds. :)
Am I correct, when I split this code in the following way?
No. The correct way to split them would be like this:
public Task DeleteColorSchemeAsync(ColorScheme colorScheme)
{
if (colorScheme == null)
throw new ArgumentNullException(nameof(colorScheme));
if (colorScheme.IsDefault)
throw new SettingIsDefaultException();
return DeleteColorSchemeInternalAsync(colorScheme);
}
private async Task DeleteColorSchemeInternalAsync(ColorScheme colorScheme)
{
_dbContext.ColorSchemes.Remove(colorScheme);
await _dbContext.SaveChangesAsync();
}
(note that the entry method is not async in this case).
Or like this, using the newer local functions:
public Task DeleteColorSchemeAsync(ColorScheme colorScheme)
{
if (colorScheme == null)
throw new ArgumentNullException(nameof(colorScheme));
if (colorScheme.IsDefault)
throw new SettingIsDefaultException();
return DeleteColorSchemeAsync();
async Task DeleteColorSchemeAsync()
{
_dbContext.ColorSchemes.Remove(colorScheme);
await _dbContext.SaveChangesAsync();
}
}
The reason this rule exists is to make sure you throw as soon as possible on usage exceptions. If you don't split the logic and leave validation inside the async method, the exception will only be thrown when someone awaits your returned task, which may not happen immediately depending on usage.
One very common flow where throwing early would be beneficial, is when you want to fire multiple tasks concurrently and await for their completion. Since on this flow your await action happens after the tasks are fired, you'll get an exception that is potentially very far from the actual point of the call, making it unnecessarily harder to debug.
The accepted answer also proposes returning the task directly in cases where you have only one async operation to do and that is the result value, however that introduces significant problems when debugging code, since your method is omitted from the stacktrace, making it harder to navigate in the entire flow. Here is a video that discusses this problem in more depth:
https://youtu.be/Q2zDatDVnO0?t=327
Returning the task directly without awaiting it should only be done for extremely simple "relay"-type methods, where there is very little relevant logic in the parent method.
I'd advise following the rule at all times.

Waiting for the result of async method using Task<T>.Result() [duplicate]

I'm currently reading "Concurrency in C# Cookbook" by Stephen Cleary, and I noticed the following technique:
var completedTask = await Task.WhenAny(downloadTask, timeoutTask);
if (completedTask == timeoutTask)
return null;
return await downloadTask;
downloadTask is a call to httpclient.GetStringAsync, and timeoutTask is executing Task.Delay.
In the event that it didn't timeout, then downloadTask is already completed. Why is necessary to do a second await instead of returning downloadTask.Result, given that the task is already completed?
There are already some good answers/comments here, but just to chime in...
There are two reasons why I prefer await over Result (or Wait). The first is that the error handling is different; await does not wrap the exception in an AggregateException. Ideally, asynchronous code should never have to deal with AggregateException at all, unless it specifically wants to.
The second reason is a little more subtle. As I describe on my blog (and in the book), Result/Wait can cause deadlocks, and can cause even more subtle deadlocks when used in an async method. So, when I'm reading through code and I see a Result or Wait, that's an immediate warning flag. The Result/Wait is only correct if you're absolutely sure that the task is already completed. Not only is this hard to see at a glance (in real-world code), but it's also more brittle to code changes.
That's not to say that Result/Wait should never be used. I follow these guidelines in my own code:
Asynchronous code in an application can only use await.
Asynchronous utility code (in a library) can occasionally use Result/Wait if the code really calls for it. Such usage should probably have comments.
Parallel task code can use Result and Wait.
Note that (1) is by far the common case, hence my tendency to use await everywhere and treat the other cases as exceptions to the general rule.
This makes sense if timeoutTask is a product of Task.Delay, which I believe what it is in the book.
Task.WhenAny returns Task<Task>, where the inner task is one of those you passed as arguments. It could be re-written like this:
Task<Task> anyTask = Task.WhenAny(downloadTask, timeoutTask);
await anyTask;
if (anyTask.Result == timeoutTask)
return null;
return downloadTask.Result;
In either case, because downloadTask has already completed, there's a very minor difference between return await downloadTask and return downloadTask.Result. It's in that the latter will throw AggregateException which wraps any original exception, as pointed out by #KirillShlenskiy in the comments. The former would just re-throw the original exception.
In either case, wherever you handle exceptions, you should check for AggregateException and its inner exceptions anyway, to get to the cause of the error.

Returning result of async method without awaiting - bad idea?

I'm looking at some code written a while back that is making me very nervous. The general shape of the methods in questions is like this;
public Task Foo(...){
SyncMethod();
SyncMethod();
...
return AsyncMethod();
}
I realize I can just mark the method async and do an await on the last call, but...do I have to? Is this safe to use as is? The sync methods that are called before the last async method do not have asynchronous alternatives and the Foo method does not care about the result of the AsyncMethod, so it looks like it's ok to just return the awaitable to the caller and let the caller deal with it.
Also, FWIW, the code in question might be called from a thread that's running with an active ASP.NET context, so I'm getting that tingling feeling in my brain that there's some invisible evil that I'm not seeing here?
I realize I can just mark the method async and do an await on the last call, but...do I have to?
Actually, if you did this, the only change that it would have is that exceptions thrown by either of those synchronous methods would be wrapped into the returned Task, while in the current implementation the method would throw without ever successfully returning a Task. The actual effect of what work is done synchronously and what work is done asynchronously is entirely unaffected.
Having said that, both of the options you've mentioned are worrisome. Here you have a method that appears to be asynchronous, meaning someone calling it expects it to return more or less right away, but in reality the method will run synchronously for some amount of time.
If your two synchronous methods are really fast and as a result, you're confident that this very small amount of synchronous work won't be noticeable to any of your callers, then that's fine. If, however, that work will (even potentially) take a non-trivial amount of time to solve, then you have a problem on your hands.
Having actually asynchronous alternatives for those methods would be best, but as a last resort, until you have a better option, often the best you can manage to do is to await Task.Run(() => SyncMethod()); for those methods (which of course means the method now needs to be marked as async).

Categories