Why is this an issue?
ValueTask<TResult> provides a value type that wraps a Task<TResult> and the corresponding TResult.
It was introduced in .NET Core 2.0 to optimize
memory allocation when functions return their results synchronously.
Using ValueTask and ValueTask<TResult> in the following ways is discouraged as it could result in a race
condition:
- Calling
awaitmultiple times on aValueTask/ValueTask<TResult>. The wrapped object may have been reused by another operation. This differs fromTask/Task<TResult>, on which you can await multiple times and always get the same result. - Calling
awaitconcurrently on aValueTask/ValueTask<TResult>. The underlying object is not thread safe. What’s more, it has the same effect as awaiting multiple times aValueTask/ValueTask<TResult>. This again differs fromTask/Task<TResult>, which support concurrentawait. - Using
.Resultor.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.
This rule raises an issue when the following operations are performed on a ValueTask/ValueTask<TResult> instance
unless it happens in a loop:
- Awaiting the instance multiple times.
- Calling
AsTaskmultiple times. - Using
.Resultor.GetAwaiter().GetResult()multiple times - Using
.Resultor.GetAwaiter().GetResult()when the operation has not yet completed - Using of these ways to consume the instance multiple times.
How to fix it
Code examples
Noncompliant code example
ValueTask<int> vt = ComputeAsync();
int result = await vt;
result = await vt; // Noncompliant, variable is awaited multiple times
int value = ComputeAsync()).GetAwaiter().GetResult(); // Noncompliant, uses GetAwaiter().GetResult() when it's not known to be done
Compliant solution
ValueTask<int> vt = ComputeAsync();
int result = await vt;
int value = await ComputeAsync().AsTask();
Resources
Documentation
Standards
- STIG Viewer - Application Security and Development: V-222567 - The application must not be vulnerable to race conditions.