diff --git a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs index f5f3677f9ebcf..2c926b8403ff9 100644 --- a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs +++ b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs @@ -193,7 +193,11 @@ private ValueTask<VoidResult> ProcessEventChangeAsync(ImmutableSegmentedList<boo /// </param> private async Task<VoidResult> RecomputeTagsAsync(bool highPriority, CancellationToken cancellationToken) { - await _dataSource.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); +#pragma warning disable VSTHRD004 // Await SwitchToMainThreadAsync + await _dataSource.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken).NoThrowAwaitable(); +#pragma warning restore VSTHRD004 // Await SwitchToMainThreadAsync + if (cancellationToken.IsCancellationRequested) + return default; // if we're tagging documents that are not visible, then introduce a long delay so that we avoid // consuming machine resources on work the user isn't likely to see. ConfigureAwait(true) so that if @@ -247,7 +251,11 @@ await _visibilityTracker.DelayWhileNonVisibleAsync( var bufferToChanges = ProcessNewTagTrees(spansToTag, oldTagTrees, newTagTrees, cancellationToken); // Then switch back to the UI thread to update our state and kick off the work to notify the editor. - await _dataSource.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); +#pragma warning disable VSTHRD004 // Await SwitchToMainThreadAsync + await _dataSource.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken).NoThrowAwaitable(); +#pragma warning restore VSTHRD004 // Await SwitchToMainThreadAsync + if (cancellationToken.IsCancellationRequested) + return default; // Once we assign our state, we're uncancellable. We must report the changed information // to the editor. The only case where it's ok not to is if the tagger itself is disposed. diff --git a/src/EditorFeatures/Core/Workspaces/ITextBufferVisibilityTracker.cs b/src/EditorFeatures/Core/Workspaces/ITextBufferVisibilityTracker.cs index 2b4098c123adb..61b4e118ab904 100644 --- a/src/EditorFeatures/Core/Workspaces/ITextBufferVisibilityTracker.cs +++ b/src/EditorFeatures/Core/Workspaces/ITextBufferVisibilityTracker.cs @@ -80,7 +80,12 @@ async Task DelayWhileNonVisibleWorkerAsync() if (cancellationToken.IsCancellationRequested) return; - await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); +#pragma warning disable VSTHRD004 // Await SwitchToMainThreadAsync + await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken).NoThrowAwaitable(); +#pragma warning restore VSTHRD004 // Await SwitchToMainThreadAsync + if (cancellationToken.IsCancellationRequested) + return; + if (service.IsVisible(subjectBuffer)) return; diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs index 17d76febb3871..213a6e09b4053 100644 --- a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs @@ -230,7 +230,7 @@ private async Task ProcessQueueAsync() } // wait for all pending tasks to complete their cancellation, ignoring any exceptions - await Task.WhenAll(concurrentlyExecutingTasksArray.Select(kvp => kvp.Key)).NoThrowAwaitableInternal(captureContext: false); + await Task.WhenAll(concurrentlyExecutingTasksArray.Select(kvp => kvp.Key)).NoThrowAwaitable(captureContext: false); } Debug.Assert(!concurrentlyExecutingTasks.Any(), "The tasks should have all been drained before continuing"); diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/TaskExtensions.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/TaskExtensions.cs deleted file mode 100644 index 2ce0886ee3c75..0000000000000 --- a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/TaskExtensions.cs +++ /dev/null @@ -1,138 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.CommonLanguageServerProtocol.Framework -{ - internal static partial class TaskExtensions - { - // Following code is copied from Microsoft.VisualStudio.Threading.TplExtensions (renamed to avoid ambiguity) - // https://github.com/microsoft/vs-threading/blob/main/src/Microsoft.VisualStudio.Threading/TplExtensions.cs - - /// <summary> - /// Returns an awaitable for the specified task that will never throw, even if the source task - /// faults or is canceled. - /// </summary> - /// <param name="task">The task whose completion should signal the completion of the returned awaitable.</param> - /// <param name="captureContext">if set to <c>true</c> the continuation will be scheduled on the caller's context; <c>false</c> to always execute the continuation on the threadpool.</param> - /// <returns>An awaitable.</returns> - public static NoThrowTaskAwaitable NoThrowAwaitableInternal(this Task task, bool captureContext = true) - { - return new NoThrowTaskAwaitable(task, captureContext); - } - - /// <summary> - /// An awaitable that wraps a task and never throws an exception when waited on. - /// </summary> - public readonly struct NoThrowTaskAwaitable - { - /// <summary> - /// The task. - /// </summary> - private readonly Task _task; - - /// <summary> - /// A value indicating whether the continuation should be scheduled on the current sync context. - /// </summary> - private readonly bool _captureContext; - - /// <summary> - /// Initializes a new instance of the <see cref="NoThrowTaskAwaitable" /> struct. - /// </summary> - /// <param name="task">The task.</param> - /// <param name="captureContext">Whether the continuation should be scheduled on the current sync context.</param> - public NoThrowTaskAwaitable(Task task, bool captureContext) - { - if (task is null) - { - throw new InvalidOperationException(nameof(task)); - } - _task = task; - _captureContext = captureContext; - } - - /// <summary> - /// Gets the awaiter. - /// </summary> - /// <returns>The awaiter.</returns> - public NoThrowTaskAwaiter GetAwaiter() - { - return new NoThrowTaskAwaiter(_task, _captureContext); - } - } - - /// <summary> - /// An awaiter that wraps a task and never throws an exception when waited on. - /// </summary> - public readonly struct NoThrowTaskAwaiter : ICriticalNotifyCompletion - { - /// <summary> - /// The task. - /// </summary> - private readonly Task _task; - - /// <summary> - /// A value indicating whether the continuation should be scheduled on the current sync context. - /// </summary> - private readonly bool _captureContext; - - /// <summary> - /// Initializes a new instance of the <see cref="NoThrowTaskAwaiter"/> struct. - /// </summary> - /// <param name="task">The task.</param> - /// <param name="captureContext">if set to <c>true</c> [capture context].</param> - public NoThrowTaskAwaiter(Task task, bool captureContext) - { - if (task is null) - { - throw new InvalidOperationException(nameof(task)); - } - _task = task; - _captureContext = captureContext; - } - - /// <summary> - /// Gets a value indicating whether the task has completed. - /// </summary> - public bool IsCompleted - { - get { return _task.IsCompleted; } - } - - /// <summary> - /// Schedules a delegate for execution at the conclusion of a task's execution. - /// </summary> - /// <param name="continuation">The action.</param> - public void OnCompleted(Action continuation) - { - _task.ConfigureAwait(_captureContext).GetAwaiter().OnCompleted(continuation); - } - - /// <summary> - /// Schedules a delegate for execution at the conclusion of a task's execution - /// without capturing the ExecutionContext. - /// </summary> - /// <param name="continuation">The action.</param> - public void UnsafeOnCompleted(Action continuation) - { - _task.ConfigureAwait(_captureContext).GetAwaiter().UnsafeOnCompleted(continuation); - } - - /// <summary> - /// Does nothing. - /// </summary> -#pragma warning disable CA1822 // Mark members as static - public void GetResult() -#pragma warning restore CA1822 // Mark members as static - { - // Never throw here. - } - } - } -} diff --git a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs index 8368934227e02..312d339036f45 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs @@ -14,10 +14,10 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; +using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; using static Microsoft.VisualStudio.Threading.ThreadingTools; @@ -307,11 +307,11 @@ static async Task WaitForHighPriorityTasksAsync(CancellationToken cancellationTo if (task.IsCompleted) { // Make sure to yield so continuations of 'task' can make progress. - await Task.Yield().ConfigureAwait(false); + await AwaitExtensions.ConfigureAwait(Task.Yield(), false); } else { - await task.WithCancellation(cancellationToken).NoThrowAwaitableInternal(false); + await task.WithCancellation(cancellationToken).NoThrowAwaitable(false); } } }