Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,33 +40,55 @@ public AsynchronousOperationListener(string featureName, bool enableDiagnosticTo
TrackActiveTokens = Debugger.IsAttached || enableDiagnosticTokens;
}

public async Task<bool> Delay(TimeSpan delay, CancellationToken cancellationToken)
[PerformanceSensitive(
"https://github.com/dotnet/roslyn/pull/58646",
Constraint = "Cannot use async/await because it produces large numbers of first-chance cancellation exceptions.")]
public Task<bool> Delay(TimeSpan delay, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (cancellationToken.IsCancellationRequested)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add some comments on why we'd go though the trouble to implement it this way?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added [PerformanceSensitive] with a text constraint.

return Task.FromCanceled<bool>(cancellationToken);

var expeditedDelayCancellationToken = _expeditedDelayCancellationTokenSource.Token;
if (expeditedDelayCancellationToken.IsCancellationRequested)
{
// The operation is already being expedited
return false;
return SpecializedTasks.False;
}

using var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, expeditedDelayCancellationToken);
var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, expeditedDelayCancellationToken);

var delayTask = Task.Delay(delay, cancellationTokenSource.Token);
await delayTask.NoThrowAwaitableInternal(false);

if (delayTask.Status != TaskStatus.RanToCompletion)
if (delayTask.IsCompleted)
{
cancellationToken.ThrowIfCancellationRequested();

// The cancellation only occurred due to a request to expedite the operation.
// Don't use try-catch to reduce the number of first chance exceptions.
if (expeditedDelayCancellationToken.IsCancellationRequested)
return false;
cancellationTokenSource.Dispose();
if (delayTask.Status == TaskStatus.RanToCompletion)
return SpecializedTasks.True;
else if (cancellationToken.IsCancellationRequested)
return Task.FromCanceled<bool>(cancellationToken);
else
return SpecializedTasks.False;
}

return true;
// Handle continuation in a local function to avoid capturing arguments when this path is avoided
return DelaySlow(delayTask, cancellationTokenSource, cancellationToken);

static Task<bool> DelaySlow(Task delayTask, CancellationTokenSource cancellationTokenSourceToDispose, CancellationToken cancellationToken)
{
return delayTask.ContinueWith(
task =>
{
cancellationTokenSourceToDispose.Dispose();
if (task.Status == TaskStatus.RanToCompletion)
return SpecializedTasks.True;
else if (cancellationToken.IsCancellationRequested)
return Task.FromCanceled<bool>(cancellationToken);
else
return SpecializedTasks.False;
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default).Unwrap();
}
}

public IAsyncToken BeginAsyncOperation(string name, object? tag = null, [CallerFilePath] string filePath = "", [CallerLineNumber] int lineNumber = 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,13 @@ public Task<Compilation> GetCompilationAsync(SolutionState solution, Cancellatio
// so use a ConditionalWeakTable.
return SpecializedTasks.FromResult(compilation);
}
else if (cancellationToken.IsCancellationRequested)
{
// Handle early cancellation here to avoid throwing/catching cancellation exceptions in the async
// state machines. This helps reduce the total number of First Chance Exceptions occurring in IDE
// typing scenarios.
return Task.FromCanceled<Compilation>(cancellationToken);
}
else
{
return GetCompilationSlowAsync(solution, cancellationToken);
Expand Down