microsoft / vs-threading

The Microsoft.VisualStudio.Threading is a xplat library that provides many threading and synchronization primitives used in Visual Studio and other applications.
Other
995 stars 147 forks source link

VSTHRD103 is produced inside of ContinueWith when passing task.Result as an argument for async function. #1125

Open MaximTrushin opened 1 year ago

MaximTrushin commented 1 year ago

VS 2022 v17.4.2:

Steps to Reproduce:

Try to compile the following code:

...

Task<Expr.Nullable<int>> r1 = TestAsyncDelayAsync(15).ContinueWith((Task<Expr.Nullable<int>> task) => {
    return TestAsyncDelayAsync(task.**Result**.Value + 10);
}, TaskScheduler.Default).Unwrap();

var r2 = TestAsyncDelayAsync(15).ContinueWith((Task<Expr.Nullable<int>> task) => {
                return TestDelay(task.Result.Value + 10);
            }, TaskScheduler.Default);
...
...

public async Task<Expr.Nullable<int>> TestAsyncDelayAsync(int delayMilliseconds) {
    await Task.Delay(TimeSpan.FromMilliseconds(delayMilliseconds));
    return new Expr.Nullable<int>(delayMilliseconds);
}

public Expr.Nullable<int> TestDelay(int delayMilliseconds) {
    return new Expr.Nullable<int>(delayMilliseconds);
}

The assignment to r1 produces the VSTHRD103, but the assignment to r2 doesn't.

Expected Behavior: The warning must not be produced because the task is always completed when the lambda defined in ContinueWith is called.

Actual Behavior: VSTHRD103 is produced inside of ContinueWith when passing task.Result as an argument for async function.

AArnott commented 1 year ago

tip, @maximtrushin: use 3 backticks in a row around your multi-line code snippets so github formats them properly. Adding a cs after the opening 3 backticks adds syntax colorization.

AArnott commented 1 year ago

Can you include the actual emitted warning? I'm surprised that VSTHRD103 is doing this because I thought that was the job of VSTHRD002.
In any case though, there are several ContinueWith scenarios. This one seems similar to #1123.

MaximTrushin commented 1 year ago

This is the emitted warning:

[VSTHRD103] Result synchronously blocks. Use await instead.

For some reason, I can't reproduce the issue when using a small console application or library assembly. I see it happening only in bigger projects.

manfred-brands commented 2 months ago

I still see this in version 17.11.20 and can repeat it with a small class:

namespace VSTHRD103;

public sealed class VSTHRD103
{
    public static Task<Thing> CreateAsync() => Task.FromResult(new Thing());

    public static Task<Thing> CreateWithNameAsync() => CreateAsync().ContinueWith(t => t.Result.EnsureNameAsync("Name"), TaskScheduler.Default).Unwrap();
}

public sealed class Thing
{
    public string? Name { get; private set; }

    public Task<Thing> EnsureNameAsync(string name)
    {
        Name = name;
        return Task.FromResult(this);
    }
}

image

manfred-brands commented 2 months ago

Your unit test with a ContinueWith: task.ContinueWith(t => { Console.WriteLine(t.Result); }); bails out because the method is an Action not a Func returning Task.

I don't see any code in the actual Analyzer making exemptions when called from inside a ContinueWith