rspec/rules/S5034/rule.adoc

58 lines
3.1 KiB
Plaintext
Raw Permalink Normal View History

== Why is this an issue?
`ValueTask<TResult>` provides a value type that wraps a https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1[Task<TResult>] and the corresponding `TResult`. It was introduced in .NET Core 2.0 https://devblogs.microsoft.com/dotnet/understanding-the-whys-whats-and-whens-of-valuetask[to optimize memory allocation] when functions return their results synchronously.
2020-06-30 12:50:28 +02:00
Using `ValueTask` and `ValueTask<TResult>` in the following ways is discouraged as it could result in a race condition:
2021-02-02 15:02:10 +01:00
* Calling `await` multiple times on a `ValueTask`/`ValueTask<TResult>`. The wrapped object may have been reused by another operation. This differs from `Task`/`Task<TResult>`, on which you can await multiple times and always get the same result.
* Calling `await` concurrently on a `ValueTask`/`ValueTask<TResult>`. The underlying object is not thread safe. What's more, it has the same effect as awaiting multiple times a `ValueTask`/`ValueTask<TResult>`. This again differs from `Task`/`Task<TResult>`, which support concurrent `await`.
* Using `.Result` or `.GetAwaiter().GetResult()` without checking if the operation is completed. `IValueTaskSource`/`IValueTaskSource<TResult>` implementations are not required to block until the operation completes. On the other hand, `Task`/`Task<TResult>` blocks the call until the task completes.
It is recommended to use `ValueTask`/`ValueTask<TResult>` either by calling `await` on the function returning it, optionally calling `ConfigureAwait(false)` on it, or by calling `.AsTask()` on it.
2020-06-30 12:50:28 +02:00
This rule raises an issue when the following operations are performed on a `ValueTask`/`ValueTask<TResult>` instance unless it happens in a loop:
2020-06-30 12:50:28 +02:00
* Awaiting the instance multiple times.
* Calling `AsTask` multiple times.
* Using `.Result` or `.GetAwaiter().GetResult()` multiple times
* Using `.Result` or `.GetAwaiter().GetResult()` when the operation has not yet completed
* Using of these ways to consume the instance multiple times.
== How to fix it
2020-06-30 12:50:28 +02:00
=== Code examples
2020-06-30 12:50:28 +02:00
==== Noncompliant code example
2020-06-30 12:50:28 +02:00
[source,csharp,diff-id=1,diff-type=noncompliant]
2020-06-30 12:50:28 +02:00
----
ValueTask<int> vt = ComputeAsync();
2020-06-30 12:50:28 +02:00
int result = await vt;
result = await vt; // Noncompliant, variable is awaited multiple times
2020-06-30 12:50:28 +02:00
int value = ComputeAsync()).GetAwaiter().GetResult(); // Noncompliant, uses GetAwaiter().GetResult() when it's not known to be done
2020-06-30 12:50:28 +02:00
----
==== Compliant solution
2020-06-30 12:50:28 +02:00
[source,csharp,diff-id=1,diff-type=compliant]
2020-06-30 12:50:28 +02:00
----
ValueTask<int> vt = ComputeAsync();
int result = await vt;
2020-06-30 12:50:28 +02:00
int value = await ComputeAsync().AsTask();
2020-06-30 12:50:28 +02:00
----
== Resources
2020-06-30 12:50:28 +02:00
=== Documentation
2020-06-30 12:50:28 +02:00
* https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask[ValueTask]
* https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask-1[ValueTask<TResult>]
* https://blogs.msdn.microsoft.com/dotnet/2018/11/07/understanding-the-whys-whats-and-whens-of-valuetask[Understanding the Whys, Whats, and Whens of ValueTask]
=== Standards
* STIG Viewer - https://stigviewer.com/stig/application_security_and_development/2023-06-08/finding/V-222567[Application Security and Development: V-222567] - The application must not be vulnerable to race conditions.