diff --git a/src/VisualStudio/Core/Def/Implementation/Remote/SolutionChecksumUpdater.cs b/src/VisualStudio/Core/Def/Implementation/Remote/SolutionChecksumUpdater.cs index 25d5c76fbd176c8ee09afcf52b9a1c79ba5f1969..1c54e9506dcf2d4ec748512ee790d14765f32fe0 100644 --- a/src/VisualStudio/Core/Def/Implementation/Remote/SolutionChecksumUpdater.cs +++ b/src/VisualStudio/Core/Def/Implementation/Remote/SolutionChecksumUpdater.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SolutionCrawler; +using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Remote @@ -18,13 +19,13 @@ internal sealed class SolutionChecksumUpdater : GlobalOperationAwareIdleProcesso { private readonly Workspace _workspace; private readonly TaskQueue _textChangeQueue; - private readonly SemaphoreSlim _event; + private readonly AsyncQueue _workQueue; private readonly object _gate; private CancellationTokenSource _globalOperationCancellationSource; - // hold last async token - private IAsyncToken _lastToken; + // hold the async token from WaitAsync so ExecuteAsync can complete it + private IAsyncToken _currentToken; public SolutionChecksumUpdater(Workspace workspace, IAsynchronousOperationListenerProvider listenerProvider, CancellationToken shutdownToken) : base(listenerProvider.GetListener(FeatureAttribute.SolutionChecksumUpdater), @@ -34,7 +35,7 @@ public SolutionChecksumUpdater(Workspace workspace, IAsynchronousOperationListen _workspace = workspace; _textChangeQueue = new TaskQueue(Listener, TaskScheduler.Default); - _event = new SemaphoreSlim(initialCount: 0); + _workQueue = new AsyncQueue(); _gate = new object(); // start listening workspace change event @@ -52,8 +53,9 @@ protected override async Task ExecuteAsync() { lock (_gate) { - _lastToken?.Dispose(); - _lastToken = null; + Contract.ThrowIfNull(_currentToken); + _currentToken.Dispose(); + _currentToken = null; } // wait for global operation to finish @@ -73,8 +75,15 @@ protected override void PauseOnGlobalOperation() CancelAndDispose(previousCancellationSource); } - protected override Task WaitAsync(CancellationToken cancellationToken) - => _event.WaitAsync(cancellationToken); + protected override async Task WaitAsync(CancellationToken cancellationToken) + { + var currentToken = await _workQueue.DequeueAsync(cancellationToken).ConfigureAwait(false); + lock (_gate) + { + Contract.ThrowIfFalse(_currentToken is null); + _currentToken = currentToken; + } + } public override void Shutdown() { @@ -102,17 +111,12 @@ private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e) private void EnqueueChecksumUpdate() { // event will raised sequencially. no concurrency on this handler - if (_event.CurrentCount > 0) + if (_workQueue.TryPeek(out _)) { return; } - lock (_gate) - { - _lastToken ??= Listener.BeginAsyncOperation(nameof(SolutionChecksumUpdater)); - } - - _event.Release(); + _workQueue.Enqueue(Listener.BeginAsyncOperation(nameof(SolutionChecksumUpdater))); } private async Task SynchronizePrimaryWorkspaceAsync(CancellationToken cancellationToken) diff --git a/src/VisualStudio/Core/Test.Next/Remote/RemoteHostClientServiceFactoryTests.cs b/src/VisualStudio/Core/Test.Next/Remote/RemoteHostClientServiceFactoryTests.cs index 083a38b522c5ab58464f99ad0d9f80a22457378b..4a43a50bc8389f060e3cb53c89e549c0c72dc3e7 100644 --- a/src/VisualStudio/Core/Test.Next/Remote/RemoteHostClientServiceFactoryTests.cs +++ b/src/VisualStudio/Core/Test.Next/Remote/RemoteHostClientServiceFactoryTests.cs @@ -30,7 +30,7 @@ public class RemoteHostClientServiceFactoryTests private static AdhocWorkspace CreateWorkspace() => new AdhocWorkspace(s_composition.GetHostServices()); - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/46255")] + [Fact] public async Task UpdaterService() { var hostServices = s_composition.GetHostServices();