提交 d1d20648 编写于 作者: H Heejae Chang 提交者: GitHub

Merge pull request #13093 from heejaechang/autochecksum

added solution checksum updating service
......@@ -247,6 +247,7 @@
<Compile Include="ReplacePropertyWithMethods\ReplacePropertyWithMethodsCodeRefactoringProvider.cs" />
<Compile Include="Shared\Utilities\PatternMatcher.Segment.cs" />
<Compile Include="Shared\Utilities\PatternMatcher.TextChunk.cs" />
<Compile Include="SolutionCrawler\GlobalOperationAwareIdleProcessor.cs" />
<Compile Include="SymbolCategorization\ISymbolCategorizer.cs" />
<Compile Include="Diagnostics\Analyzers\NamingStyles\NamingRule.cs" />
<Compile Include="Diagnostics\Analyzers\NamingStyles\NamingStylePreferencesInfo.cs" />
......@@ -612,7 +613,7 @@
<Compile Include="SolutionCrawler\WorkCoordinator.cs" />
<Compile Include="SolutionCrawler\WorkCoordinator.HighPriorityProcessor.cs" />
<Compile Include="SolutionCrawler\WorkCoordinator.IncrementalAnalyzerProcessor.cs" />
<Compile Include="SolutionCrawler\WorkCoordinator.GlobalOperationAwareIdleProcessor.cs" />
<Compile Include="SolutionCrawler\WorkCoordinator.AbstractPriorityProcessor.cs" />
<Compile Include="SolutionCrawler\WorkCoordinator.LowPriorityProcessor.cs" />
<Compile Include="SolutionCrawler\WorkCoordinator.NormalPriorityProcessor.cs" />
<Compile Include="SolutionCrawler\WorkCoordinator.SemanticChangeProcessor.cs" />
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Notification;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.SolutionCrawler
{
internal abstract class GlobalOperationAwareIdleProcessor : IdleProcessor
{
private readonly IGlobalOperationNotificationService _globalOperationNotificationService;
private TaskCompletionSource<object> _globalOperation;
private Task _globalOperationTask;
public GlobalOperationAwareIdleProcessor(
IAsynchronousOperationListener listener,
IGlobalOperationNotificationService globalOperationNotificationService,
int backOffTimeSpanInMs,
CancellationToken shutdownToken) :
base(listener, backOffTimeSpanInMs, shutdownToken)
{
_globalOperation = null;
_globalOperationTask = SpecializedTasks.EmptyTask;
_globalOperationNotificationService = globalOperationNotificationService;
_globalOperationNotificationService.Started += OnGlobalOperationStarted;
_globalOperationNotificationService.Stopped += OnGlobalOperationStopped;
}
protected Task GlobalOperationTask => _globalOperationTask;
protected abstract void PauseOnGlobalOperation();
private void OnGlobalOperationStarted(object sender, EventArgs e)
{
Contract.ThrowIfFalse(_globalOperation == null);
// events are serialized. no lock is needed
_globalOperation = new TaskCompletionSource<object>();
_globalOperationTask = _globalOperation.Task;
PauseOnGlobalOperation();
}
private void OnGlobalOperationStopped(object sender, GlobalOperationEventArgs e)
{
if (_globalOperation == null)
{
// we subscribed to the event while it is already running.
return;
}
// events are serialized. no lock is needed
_globalOperation.SetResult(null);
_globalOperation = null;
// set to empty task so that we don't need a lock
_globalOperationTask = SpecializedTasks.EmptyTask;
}
public virtual void Shutdown()
{
_globalOperationNotificationService.Started -= OnGlobalOperationStarted;
_globalOperationNotificationService.Stopped -= OnGlobalOperationStopped;
}
}
}
......@@ -6,7 +6,6 @@
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Notification;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.SolutionCrawler
{
......@@ -16,31 +15,19 @@ private sealed partial class WorkCoordinator
{
private sealed partial class IncrementalAnalyzerProcessor
{
private abstract class GlobalOperationAwareIdleProcessor : IdleProcessor
private abstract class AbstractPriorityProcessor : GlobalOperationAwareIdleProcessor
{
protected readonly IncrementalAnalyzerProcessor Processor;
private readonly IGlobalOperationNotificationService _globalOperationNotificationService;
private TaskCompletionSource<object> _globalOperation;
private Task _globalOperationTask;
public GlobalOperationAwareIdleProcessor(
public AbstractPriorityProcessor(
IAsynchronousOperationListener listener,
IncrementalAnalyzerProcessor processor,
IGlobalOperationNotificationService globalOperationNotificationService,
int backOffTimeSpanInMs,
CancellationToken shutdownToken) :
base(listener, backOffTimeSpanInMs, shutdownToken)
base(listener, globalOperationNotificationService, backOffTimeSpanInMs, shutdownToken)
{
this.Processor = processor;
_globalOperation = null;
_globalOperationTask = SpecializedTasks.EmptyTask;
_globalOperationNotificationService = globalOperationNotificationService;
_globalOperationNotificationService.Started += OnGlobalOperationStarted;
_globalOperationNotificationService.Stopped += OnGlobalOperationStopped;
if (this.Processor._documentTracker != null)
{
this.Processor._documentTracker.NonRoslynBufferTextChanged += OnNonRoslynBufferTextChanged;
......@@ -65,43 +52,9 @@ private void OnNonRoslynBufferTextChanged(object sender, EventArgs e)
this.UpdateLastAccessTime();
}
protected Task GlobalOperationTask
{
get
{
return _globalOperationTask;
}
}
protected abstract void PauseOnGlobalOperation();
private void OnGlobalOperationStarted(object sender, EventArgs e)
protected override void PauseOnGlobalOperation()
{
Contract.ThrowIfFalse(_globalOperation == null);
// events are serialized. no lock is needed
_globalOperation = new TaskCompletionSource<object>();
_globalOperationTask = _globalOperation.Task;
SolutionCrawlerLogger.LogGlobalOperation(this.Processor._logAggregator);
PauseOnGlobalOperation();
}
private void OnGlobalOperationStopped(object sender, GlobalOperationEventArgs e)
{
if (_globalOperation == null)
{
// we subscribed to the event while it is already running.
return;
}
// events are serialized. no lock is needed
_globalOperation.SetResult(null);
_globalOperation = null;
// set to empty task so that we don't need a lock
_globalOperationTask = SpecializedTasks.EmptyTask;
}
protected abstract Task HigherQueueOperationTask { get; }
......@@ -139,10 +92,9 @@ protected async Task WaitForHigherPriorityOperationsAsync()
}
}
public virtual void Shutdown()
public override void Shutdown()
{
_globalOperationNotificationService.Started -= OnGlobalOperationStarted;
_globalOperationNotificationService.Stopped -= OnGlobalOperationStopped;
base.Shutdown();
if (this.Processor._documentTracker != null)
{
......
......@@ -19,7 +19,7 @@ private sealed partial class WorkCoordinator
{
private sealed partial class IncrementalAnalyzerProcessor
{
private sealed class LowPriorityProcessor : GlobalOperationAwareIdleProcessor
private sealed class LowPriorityProcessor : AbstractPriorityProcessor
{
private readonly Lazy<ImmutableArray<IIncrementalAnalyzer>> _lazyAnalyzers;
private readonly AsyncProjectWorkItemQueue _workItemQueue;
......@@ -93,6 +93,8 @@ protected override bool HigherQueueHasWorkItem
protected override void PauseOnGlobalOperation()
{
base.PauseOnGlobalOperation();
_workItemQueue.RequestCancellationOnRunningTasks();
}
......
......@@ -22,7 +22,7 @@ private sealed partial class WorkCoordinator
{
private sealed partial class IncrementalAnalyzerProcessor
{
private sealed class NormalPriorityProcessor : GlobalOperationAwareIdleProcessor
private sealed class NormalPriorityProcessor : AbstractPriorityProcessor
{
private const int MaxHighPriorityQueueCache = 29;
......@@ -221,6 +221,8 @@ protected override bool HigherQueueHasWorkItem
protected override void PauseOnGlobalOperation()
{
base.PauseOnGlobalOperation();
_workItemQueue.RequestCancellationOnRunningTasks();
}
......
......@@ -21,6 +21,7 @@ public class RemoteHostClientService : IRemoteHostClientService
private readonly object _gate;
private SolutionChecksumUpdator _checksumUpdator;
private CancellationTokenSource _shutdownCancellationTokenSource;
private Task<RemoteHostClient> _instanceTask;
......@@ -62,6 +63,10 @@ public void Enable()
_shutdownCancellationTokenSource = new CancellationTokenSource();
var token = _shutdownCancellationTokenSource.Token;
// create solution checksum updator
_checksumUpdator = new SolutionChecksumUpdator(_workspace, token);
_instanceTask = Task.Run(() => EnableAsync(token), token);
}
}
......@@ -76,18 +81,20 @@ public void Disable()
return;
}
var instance = _instanceTask;
var instanceTask = _instanceTask;
_instanceTask = null;
RemoveGlobalAssets();
_shutdownCancellationTokenSource.Cancel();
_checksumUpdator.Shutdown();
_checksumUpdator = null;
try
{
instance.Wait(_shutdownCancellationTokenSource.Token);
instance.Result.Shutdown();
instanceTask.Wait(_shutdownCancellationTokenSource.Token);
instanceTask.Result.Shutdown();
}
catch (OperationCanceledException)
{
......@@ -112,6 +119,9 @@ public Task<RemoteHostClient> GetRemoteHostClientAsync(CancellationToken cancell
return SpecializedTasks.Default<RemoteHostClient>();
}
// ensure we have solution checksum
_checksumUpdator.EnsureSolutionChecksum(cancellationToken);
return instanceTask;
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Execution;
using Microsoft.CodeAnalysis.Notification;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.SolutionCrawler;
namespace Microsoft.VisualStudio.LanguageServices.Remote
{
internal partial class RemoteHostClientServiceFactory
{
private class SolutionChecksumUpdator : GlobalOperationAwareIdleProcessor
{
private readonly Workspace _workspace;
private readonly ISolutionChecksumService _checksumService;
private readonly SemaphoreSlim _event;
// hold onto last snapshot
private CancellationTokenSource _globalOperationCancellationSource;
private ChecksumScope _lastSnapshot;
public SolutionChecksumUpdator(
Workspace workspace,
CancellationToken shutdownToken) :
base(AggregateAsynchronousOperationListener.CreateEmptyListener(),
workspace.Services.GetService<IGlobalOperationNotificationService>(),
workspace.Options.GetOption(RemoteHostOptions.SolutionChecksumMonitorBackOffTimeSpanInMS), shutdownToken)
{
_workspace = workspace;
_checksumService = _workspace.Services.GetService<ISolutionChecksumService>();
_event = new SemaphoreSlim(initialCount: 0);
// start listening workspace change event
_workspace.WorkspaceChanged += OnWorkspaceChanged;
// create its own cancellation token source
_globalOperationCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(shutdownToken);
Start();
}
private CancellationToken ShutdownCancellationToken => CancellationToken;
protected override async Task ExecuteAsync()
{
// wait for global operation to finish
await GlobalOperationTask.ConfigureAwait(false);
// cancel updating solution checksum if a global operation (such as loading solution, building solution and etc) has started
await UpdateSolutionChecksumAsync(_globalOperationCancellationSource.Token).ConfigureAwait(false);
}
protected override void PauseOnGlobalOperation()
{
var previousCancellationSource = _globalOperationCancellationSource;
// create new cancellation token source linked with given shutdown cancellation token
_globalOperationCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(ShutdownCancellationToken);
CancelAndDispose(previousCancellationSource);
}
protected override Task WaitAsync(CancellationToken cancellationToken)
{
return _event.WaitAsync(cancellationToken);
}
public async void EnsureSolutionChecksum(CancellationToken cancellationToken)
{
if (_lastSnapshot != null)
{
// we already have one. pass
return;
}
try
{
// update solution checksum
using (var linked = CancellationTokenSource.CreateLinkedTokenSource(_globalOperationCancellationSource.Token, cancellationToken))
{
await UpdateSolutionChecksumAsync(linked.Token).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
{
// ignore cancellation
}
}
public override void Shutdown()
{
base.Shutdown();
// stop listening workspace change event
_workspace.WorkspaceChanged -= OnWorkspaceChanged;
CancelAndDispose(_globalOperationCancellationSource);
// release last snapshot
_lastSnapshot?.Dispose();
_lastSnapshot = null;
}
private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e)
{
// special initial case
if (e.Kind == WorkspaceChangeKind.SolutionAdded)
{
CreateInitialSolutionChecksum();
return;
}
// record that we are busy
UpdateLastAccessTime();
// event will raised sequencially. no concurrency on this handler
if (_event.CurrentCount > 0)
{
return;
}
_event.Release();
}
private async Task UpdateSolutionChecksumAsync(CancellationToken cancellationToken)
{
// hold onto previous snapshot
var previousSnapshot = _lastSnapshot;
// create a new one (incrementally update the snapshot)
_lastSnapshot = await _checksumService.CreateChecksumAsync(_workspace.CurrentSolution, cancellationToken).ConfigureAwait(false);
// let old one go.
previousSnapshot?.Dispose();
}
private void CreateInitialSolutionChecksum()
{
// initial solution checksum creation won't be affected by global operation.
// cancellation can only happen if it is being shutdown.
Task.Run(() => UpdateSolutionChecksumAsync(ShutdownCancellationToken), ShutdownCancellationToken);
}
private static void CancelAndDispose(CancellationTokenSource cancellationSource)
{
// cancel running tasks
cancellationSource.Cancel();
// dispose cancellation token source
cancellationSource.Dispose();
}
}
}
}
......@@ -10,5 +10,8 @@ internal static class RemoteHostOptions
[ExportOption]
public static readonly Option<bool> RemoteHost = new Option<bool>(OptionName, nameof(RemoteHost), defaultValue: false);
[ExportOption]
public static readonly Option<int> SolutionChecksumMonitorBackOffTimeSpanInMS = new Option<int>(OptionName, nameof(SolutionChecksumMonitorBackOffTimeSpanInMS), defaultValue: 10000);
}
}
......@@ -116,6 +116,7 @@
<Compile Include="Implementation\Remote\RemoteHostClient.cs" />
<Compile Include="Implementation\Remote\RemoteHostClientServiceFactory.cs" />
<Compile Include="Implementation\Remote\RemoteHostClientServiceFactory.RemoteHostClientService.cs" />
<Compile Include="Implementation\Remote\RemoteHostClientServiceFactory.SolutionChecksumUpdator.cs" />
<Compile Include="Implementation\Remote\RemoteHostOptions.cs" />
<Compile Include="Implementation\Serialization\AssemblySerializationInfoService.cs" />
<Compile Include="Implementation\SolutionSize\SolutionSizeTracker.cs" />
......
......@@ -28,9 +28,6 @@ internal class OutOfProcDiagnosticAnalyzerExecutor : IRemoteHostDiagnosticAnalyz
private readonly IDiagnosticAnalyzerService _analyzerService;
private readonly AbstractHostDiagnosticUpdateSource _hostDiagnosticUpdateSource;
// TODO: solution snapshot tracking for current solution should be its own service
private ChecksumScope _lastSnapshot;
[ImportingConstructor]
public OutOfProcDiagnosticAnalyzerExecutor(
IDiagnosticAnalyzerService analyzerService,
......@@ -78,9 +75,6 @@ internal class OutOfProcDiagnosticAnalyzerExecutor : IRemoteHostDiagnosticAnalyz
var snapshotService = solution.Workspace.Services.GetService<ISolutionChecksumService>();
// TODO: incremental build of solution snapshot should be its own service
await UpdateLastSolutionSnapshotAsync(snapshotService, solution).ConfigureAwait(false);
// TODO: this should be moved out
var hostChecksums = GetHostAnalyzerReferences(snapshotService, _analyzerService.GetHostAnalyzerReferences(), cancellationToken);
var analyzerMap = CreateAnalyzerMap(analyzerDriver.Analyzers.Where(a => !a.MustRunInProcess()));
......@@ -108,16 +102,6 @@ internal class OutOfProcDiagnosticAnalyzerExecutor : IRemoteHostDiagnosticAnalyz
}
}
private async Task UpdateLastSolutionSnapshotAsync(ISolutionChecksumService snapshotService, Solution solution)
{
// TODO: actual incremental build of solution snapshot should be its own service
// this is needed to make sure we incrementally update solution checksums. otherwise, we will always create from
// scratch which can be quite expansive for big solution
var lastSnapshot = _lastSnapshot;
_lastSnapshot = await snapshotService.CreateChecksumAsync(solution, CancellationToken.None).ConfigureAwait(false);
lastSnapshot?.Dispose();
}
private CompilationWithAnalyzers CreateAnalyzerDriver(CompilationWithAnalyzers analyzerDriver, Func<DiagnosticAnalyzer, bool> predicate)
{
var analyzers = analyzerDriver.Analyzers.Where(predicate).ToImmutableArray();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册