未验证 提交 79791f91 编写于 作者: S Sam Harwell 提交者: GitHub

Merge pull request #46346 from sharwell/updaterservice-test

Fix race condition in SolutionChecksumUpdater work queue
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Remote;
using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.VisualStudio.Threading;
using Roslyn.Utilities; using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Remote namespace Microsoft.VisualStudio.LanguageServices.Remote
...@@ -18,13 +19,13 @@ internal sealed class SolutionChecksumUpdater : GlobalOperationAwareIdleProcesso ...@@ -18,13 +19,13 @@ internal sealed class SolutionChecksumUpdater : GlobalOperationAwareIdleProcesso
{ {
private readonly Workspace _workspace; private readonly Workspace _workspace;
private readonly TaskQueue _textChangeQueue; private readonly TaskQueue _textChangeQueue;
private readonly SemaphoreSlim _event; private readonly AsyncQueue<IAsyncToken> _workQueue;
private readonly object _gate; private readonly object _gate;
private CancellationTokenSource _globalOperationCancellationSource; private CancellationTokenSource _globalOperationCancellationSource;
// hold last async token // hold the async token from WaitAsync so ExecuteAsync can complete it
private IAsyncToken _lastToken; private IAsyncToken _currentToken;
public SolutionChecksumUpdater(Workspace workspace, IAsynchronousOperationListenerProvider listenerProvider, CancellationToken shutdownToken) public SolutionChecksumUpdater(Workspace workspace, IAsynchronousOperationListenerProvider listenerProvider, CancellationToken shutdownToken)
: base(listenerProvider.GetListener(FeatureAttribute.SolutionChecksumUpdater), : base(listenerProvider.GetListener(FeatureAttribute.SolutionChecksumUpdater),
...@@ -34,7 +35,7 @@ public SolutionChecksumUpdater(Workspace workspace, IAsynchronousOperationListen ...@@ -34,7 +35,7 @@ public SolutionChecksumUpdater(Workspace workspace, IAsynchronousOperationListen
_workspace = workspace; _workspace = workspace;
_textChangeQueue = new TaskQueue(Listener, TaskScheduler.Default); _textChangeQueue = new TaskQueue(Listener, TaskScheduler.Default);
_event = new SemaphoreSlim(initialCount: 0); _workQueue = new AsyncQueue<IAsyncToken>();
_gate = new object(); _gate = new object();
// start listening workspace change event // start listening workspace change event
...@@ -52,8 +53,9 @@ protected override async Task ExecuteAsync() ...@@ -52,8 +53,9 @@ protected override async Task ExecuteAsync()
{ {
lock (_gate) lock (_gate)
{ {
_lastToken?.Dispose(); Contract.ThrowIfNull(_currentToken);
_lastToken = null; _currentToken.Dispose();
_currentToken = null;
} }
// wait for global operation to finish // wait for global operation to finish
...@@ -73,8 +75,15 @@ protected override void PauseOnGlobalOperation() ...@@ -73,8 +75,15 @@ protected override void PauseOnGlobalOperation()
CancelAndDispose(previousCancellationSource); CancelAndDispose(previousCancellationSource);
} }
protected override Task WaitAsync(CancellationToken cancellationToken) protected override async Task WaitAsync(CancellationToken cancellationToken)
=> _event.WaitAsync(cancellationToken); {
var currentToken = await _workQueue.DequeueAsync(cancellationToken).ConfigureAwait(false);
lock (_gate)
{
Contract.ThrowIfFalse(_currentToken is null);
_currentToken = currentToken;
}
}
public override void Shutdown() public override void Shutdown()
{ {
...@@ -102,17 +111,12 @@ private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e) ...@@ -102,17 +111,12 @@ private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e)
private void EnqueueChecksumUpdate() private void EnqueueChecksumUpdate()
{ {
// event will raised sequencially. no concurrency on this handler // event will raised sequencially. no concurrency on this handler
if (_event.CurrentCount > 0) if (_workQueue.TryPeek(out _))
{ {
return; return;
} }
lock (_gate) _workQueue.Enqueue(Listener.BeginAsyncOperation(nameof(SolutionChecksumUpdater)));
{
_lastToken ??= Listener.BeginAsyncOperation(nameof(SolutionChecksumUpdater));
}
_event.Release();
} }
private async Task SynchronizePrimaryWorkspaceAsync(CancellationToken cancellationToken) private async Task SynchronizePrimaryWorkspaceAsync(CancellationToken cancellationToken)
......
...@@ -30,7 +30,7 @@ public class RemoteHostClientServiceFactoryTests ...@@ -30,7 +30,7 @@ public class RemoteHostClientServiceFactoryTests
private static AdhocWorkspace CreateWorkspace() private static AdhocWorkspace CreateWorkspace()
=> new AdhocWorkspace(s_composition.GetHostServices()); => new AdhocWorkspace(s_composition.GetHostServices());
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/46255")] [Fact]
public async Task UpdaterService() public async Task UpdaterService()
{ {
var hostServices = s_composition.GetHostServices(); var hostServices = s_composition.GetHostServices();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册