Always create TaskCompletionSource<T> with TaskCreationOptions.RunContinuationsAsynchronously

Please follow this template to help us specify this new rule:

  • description of the Rule. It should answer questions “why is there an issue?”,
    TaskCompletionSource is an important building block for libraries trying to adapt things that are not inherently awaitable to be awaitable via a Task. It is also commonly used to build higher-level operations (such as batching and other combinators) on top of existing asynchronous APIs. By default, Task continuations will run inline on the same thread that calls Try/Set(Result/Exception/Canceled). As a library author, this means having to understand that calling code can resume directly on your thread. This is extremely dangerous and can result in deadlocks, thread-pool starvation, corruption of state (if code runs unexpectedly) and more.
    Always use TaskCreationOptions.RunContinuationsAsynchronously when creating the TaskCompletionSource. This will dispatch the continuation onto the thread pool instead of executing it inline.

  • snippet of Noncompliant Code
    This example does not use TaskCreationOptions.RunContinuationsAsynchronously when creating the TaskCompletionSource:

public Task<int> DoSomethingAsync()
{
var tcs = new TaskCompletionSource<int>();
var operation = new LegacyAsyncOperation();
operation.Completed += result =>
{
// Code awaiting on this task will resume on this thread!
tcs.SetResult(result);
};
return tcs.Task;
}
  • snippet of Compilant Code (fixing the above noncompliant code)
    This example uses TaskCreationOptions.RunContinuationsAsynchronously when creating the TaskCompletionSource:
public Task<int> DoSomethingAsync()
{
var tcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
var operation = new LegacyAsyncOperation();
operation.Completed += result =>
{
// Code awaiting on this task will resume on a different thread-pool thread
tcs.SetResult(result);
};
return tcs.Task;
}

:bulb:NOTE: There are 2 enums that look alike. TaskCreationOptions.RunContinuationsAsynchronously and TaskContinuationOptions.RunContinuationsAsynchronously. Be careful not to confuse their usage.

  • type : Bug

Guidelines:
We want to add as many valuable rules as possible. Thus we have guidelines to help us see the value of a rule and decide if it should be implemented. Please read them before submitting your rule:

  • Is the rule useful for a developer.
    • If the rule is a Bug, Code Smell or Vulnerability it should ask the developer to fix a real problem. It shouldn’t raise warnings asking for a manual review.

I notice that most of the text here seems to be copied from AspNetCoreDiagnosticScenarios/AsyncGuidance.md at master · davidfowl/AspNetCoreDiagnosticScenarios · GitHub .

A different opinion about this can be found at "Always create `TaskCompletionSource<T>` with `TaskCreationOptions.RunContinuationsAsynchronously`" may be detrimentally overcorrecting. · Issue #72 · davidfowl/AspNetCoreDiagnosticScenarios · GitHub .

Using TaskCreationOptions.RunContinuationsAsynchronously is certainly safer, but it’s also slower and it may not be necessary. So I don’t think it asks a developer to fix a real problem in all cases.