From 6752867c1fb2aaac27080e64f9120fc492ed832a Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 20 Apr 2017 12:28:12 -0700 Subject: [PATCH] Acquire the user's selection up-front if we're on the UI thread for light-bulbs. There are circumstances where the editor will call into us on hte UI thread and will then block for results. Right now this can deadlock as we may have kicked off work to the BG that then awaits getting the selection back from the UI thread. To avoid that, if we're on the UI thread when we start, just go and get the selection and pass it along. --- .../Suggestions/SuggestedActionsSource.cs | 54 ++++++++++--------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/Suggestions/SuggestedActionsSource.cs b/src/EditorFeatures/Core/Implementation/Suggestions/SuggestedActionsSource.cs index 9f5f5345229..c035aa0b1cd 100644 --- a/src/EditorFeatures/Core/Implementation/Suggestions/SuggestedActionsSource.cs +++ b/src/EditorFeatures/Core/Implementation/Suggestions/SuggestedActionsSource.cs @@ -604,6 +604,25 @@ public async Task HasSuggestedActionsAsync(ISuggestedActionCategorySet req using (var asyncToken = provider.OperationListener.BeginAsyncOperation("HasSuggestedActionsAsync")) { + // Acquire the user's selection up front, before we do any async work, so that we can + // directly grab it when we're on the UI thread. That way we don't have any reentrancy + // blocking concerns if VS wants to block on this call (for example, if the user + // explicitly invokes the 'show smart tag' command). + TextSpan? selection = null; + if (IsForeground()) + { + // This operation needs to happen on UI thread because it needs to access textView.Selection. + selection = TryGetCodeRefactoringSelection(buffer, view, range); + } + else + { + await InvokeBelowInputPriority(() => + { + // This operation needs to happen on UI thread because it needs to access textView.Selection. + selection = TryGetCodeRefactoringSelection(buffer, view, range); + }).ConfigureAwait(false); + } + var document = await GetMatchingDocumentAsync(range.Snapshot, cancellationToken).ConfigureAwait(false); if (document == null) { @@ -614,7 +633,7 @@ public async Task HasSuggestedActionsAsync(ISuggestedActionCategorySet req return await HasFixesAsync(provider, document, range, cancellationToken).ConfigureAwait(false) || - await HasRefactoringsAsync(provider, document, buffer, view, range, cancellationToken).ConfigureAwait(false); + await HasRefactoringsAsync(provider, document, selection, cancellationToken).ConfigureAwait(false); } } @@ -655,11 +674,16 @@ public async Task HasSuggestedActionsAsync(ISuggestedActionCategorySet req private async Task HasRefactoringsAsync( SuggestedActionsSourceProvider provider, Document document, - ITextBuffer buffer, - ITextView view, - SnapshotSpan range, + TextSpan? selection, CancellationToken cancellationToken) { + if (!selection.HasValue) + { + // this is here to fail test and see why it is failed. + Trace.WriteLine("given range is not current"); + return false; + } + var workspace = document.Project.Solution.Workspace; var supportsFeatureService = workspace.Services.GetService(); @@ -667,28 +691,6 @@ public async Task HasSuggestedActionsAsync(ISuggestedActionCategorySet req provider._codeRefactoringService != null && supportsFeatureService.SupportsRefactorings(document)) { - TextSpan? selection = null; - if (IsForeground()) - { - // This operation needs to happen on UI thread because it needs to access textView.Selection. - selection = TryGetCodeRefactoringSelection(buffer, view, range); - } - else - { - await InvokeBelowInputPriority(() => - { - // This operation needs to happen on UI thread because it needs to access textView.Selection. - selection = TryGetCodeRefactoringSelection(buffer, view, range); - }).ConfigureAwait(false); - } - - if (!selection.HasValue) - { - // this is here to fail test and see why it is failed. - Trace.WriteLine("given range is not current"); - return false; - } - return await Task.Run( () => provider._codeRefactoringService.HasRefactoringsAsync( document, selection.Value, cancellationToken), -- GitLab