rspec/rules/S4462/rule.adoc

80 lines
2.8 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

== Why is this an issue?
Making blocking calls to ``++async++`` methods transforms code that was intended to be asynchronous into a blocking operation. Doing so can cause deadlocks and unexpected blocking of context threads.
According to the MSDN documentation:
____
The root cause of this deadlock is due to the way ``++await++`` handles contexts. By default, when an incomplete ``++Task++`` is awaited, the current “context” is captured and used to resume the method when the ``++Task++`` completes. This “context” is the current ``++SynchronizationContext++`` unless its null, in which case its the current ``++TaskScheduler++``. GUI and ASP.NET applications have a ``++SynchronizationContext++`` that permits only one chunk of code to run at a time. When the ``++await++`` completes, it attempts to execute the remainder of the ``++async++`` method within the captured context. But that context already has a thread in it, which is (synchronously) waiting for the ``++async++`` method to complete. Theyre each waiting for the other, causing a deadlock.
____
[frame=all]
[cols="^1,^1,^1"]
|===
|To Do This …|Instead of This …|Use This
|Retrieve the result of a background task|``++Task.Wait++``, ``++Task.Result++`` or ``++Task.GetAwaiter.GetResult++``|``++await++``
|Wait for any task to complete|``++Task.WaitAny++``|``++await Task.WhenAny++``
|Retrieve the results of multiple tasks|``++Task.WaitAll++``|``++await Task.WhenAll++``
|Wait a period of time|``++Thread.Sleep++``|``++await Task.Delay++``
|===
=== Noncompliant code example
[source,text]
----
public static class DeadlockDemo
{
private static async Task DelayAsync()
{
await Task.Delay(1000);
}
// This method causes a deadlock when called in a GUI or ASP.NET context.
public static void Test()
{
// Start the delay.
var delayTask = DelayAsync();
// Wait for the delay to complete.
delayTask.Wait(); // Noncompliant
}
}
----
=== Compliant solution
[source,text]
----
public static class DeadlockDemo
{
private static async Task DelayAsync()
{
await Task.Delay(1000);
}
public static async Task TestAsync()
{
// Start the delay.
var delayTask = DelayAsync();
// Wait for the delay to complete.
await delayTask;
}
}
----
=== Exceptions
* Main methods of Console Applications are not subject to this deadlock issue and so are ignored by this rule.
* ``++Thread.Sleep++`` is also ignored when it is used in a non-``++async++`` method.
* Calls chained after ``++Task.Run++`` or ``++Task.Factory.StartNew++`` are ignored because they don't suffer from this deadlock issue
== Resources
* https://msdn.microsoft.com/en-us/magazine/jj991977.aspx[Async/Await - Best Practices in Asynchronous Programming]