rspec/rules/S5034/rule.adoc

58 lines
2.9 KiB
Plaintext
Raw Normal View History

== Why is this an issue?
2021-01-27 13:42:22 +01:00
``++ValueTask<TResult>++`` 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
2021-02-02 15:02:10 +01:00
2021-01-27 13:42:22 +01:00
``++ValueTask++`` and ``++ValueTask<TResult>++`` should *never* be used in the following ways as it could result in a race condition:
2021-01-27 13:42:22 +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 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.
2020-06-30 12:50:28 +02:00
2021-01-27 13:42:22 +01:00
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
2021-02-02 15:02:10 +01:00
2021-01-27 13:42:22 +01:00
This rule raises an issue when the following operations are performed on a ``++ValueTask / ValueTask<TResult>++`` instance:
2020-06-30 12:50:28 +02:00
* Awaiting the instance multiple times.
2021-01-27 13:42:22 +01:00
* Calling ``++AsTask++`` multiple times.
* Using ``++.Result++`` or ``++.GetAwaiter().GetResult()++`` multiple times
* Using ``++.Result++`` or ``++.GetAwaiter().GetResult()++`` when the operation has not yet completed
2020-06-30 12:50:28 +02:00
* Using more than one of these ways to consume the instance.
=== Noncompliant code example
2020-06-30 12:50:28 +02:00
2022-02-04 17:28:24 +01:00
[source,text]
2020-06-30 12:50:28 +02:00
----
ValueTask<int> vt = SomeValueTaskReturningMethodAsync();
int result = await vt;
int result2 = await vt; // Noncompliant, variable is awaited multiple times
int value = SomeValueTaskReturningMethodAsync().GetAwaiter().GetResult(); // Noncompliant, uses GetAwaiter().GetResult() when it's not known to be done
----
=== Compliant solution
2020-06-30 12:50:28 +02:00
2022-02-04 17:28:24 +01:00
[source,text]
2020-06-30 12:50:28 +02:00
----
int result = await SomeValueTaskReturningMethodAsync();
int result = await SomeValueTaskReturningMethodAsync().ConfigureAwait(false);
Task<int> t = SomeValueTaskReturningMethodAsync().AsTask();
----
=== Exceptions
2020-06-30 12:50:28 +02:00
2021-01-27 13:42:22 +01:00
This rule does not raise any issue when a ``++ValueTask / ValueTask<TResult>++`` is awaited multiple time in a loop.
2020-06-30 12:50:28 +02:00
== Resources
2020-06-30 12:50:28 +02:00
* https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask-1[ValueTask<TResult> official documentation]
* 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]