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

Merge pull request #13694 from heejaechang/checksumop3

make checksum build time of roslyn-internal from 1 min to 8 seconds
......@@ -129,7 +129,7 @@ public Task<RemoteHostClient> GetRemoteHostClientAsync(CancellationToken cancell
private async Task<RemoteHostClient> EnableAsync(CancellationToken cancellationToken)
{
await AddGlobalAssetsAsync(cancellationToken).ConfigureAwait(false);
AddGlobalAssets(cancellationToken);
// if we reached here, IRemoteHostClientFactory must exist.
// this will make VS.Next dll to be loaded
......@@ -139,7 +139,7 @@ private async Task<RemoteHostClient> EnableAsync(CancellationToken cancellationT
return instance;
}
private async Task AddGlobalAssetsAsync(CancellationToken cancellationToken)
private void AddGlobalAssets(CancellationToken cancellationToken)
{
using (Logger.LogBlock(FunctionId.RemoteHostClientService_AddGlobalAssetsAsync, cancellationToken))
{
......@@ -148,7 +148,7 @@ private async Task AddGlobalAssetsAsync(CancellationToken cancellationToken)
foreach (var reference in _analyzerService.GetHostAnalyzerReferences())
{
var asset = await assetBuilder.BuildAsync(reference, cancellationToken).ConfigureAwait(false);
var asset = assetBuilder.Build(reference, cancellationToken);
snapshotService.AddGlobalAsset(reference, asset, cancellationToken);
}
}
......
......@@ -24,6 +24,7 @@ private class SolutionChecksumUpdater : GlobalOperationAwareIdleProcessor
// hold onto last snapshot
private CancellationTokenSource _globalOperationCancellationSource;
private ChecksumScope _lastSnapshot;
private bool _synchronize;
public SolutionChecksumUpdater(RemoteHostClientService service, CancellationToken shutdownToken) :
base(AggregateAsynchronousOperationListener.CreateEmptyListener(),
......@@ -54,6 +55,12 @@ protected override async Task ExecuteAsync()
// cancel updating solution checksum if a global operation (such as loading solution, building solution and etc) has started
await UpdateSolutionChecksumAsync(_globalOperationCancellationSource.Token).ConfigureAwait(false);
// check whether we had bulk change that require asset synchronization
if (_synchronize)
{
await SynchronizeAssets().ConfigureAwait(false);
}
}
protected override void PauseOnGlobalOperation()
......@@ -112,16 +119,23 @@ public override void Shutdown()
private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e)
{
// special initial case
if (e.Kind == WorkspaceChangeKind.SolutionAdded)
// special bulk update case
if (e.Kind == WorkspaceChangeKind.SolutionAdded ||
e.Kind == WorkspaceChangeKind.ProjectAdded)
{
CreateInitialSolutionChecksum();
_synchronize = true;
EnqueueChecksumUpdate();
return;
}
// record that we are busy
UpdateLastAccessTime();
EnqueueChecksumUpdate();
}
private void EnqueueChecksumUpdate()
{
// event will raised sequencially. no concurrency on this handler
if (_event.CurrentCount > 0)
{
......@@ -146,27 +160,22 @@ private async Task UpdateSolutionChecksumAsync(CancellationToken cancellationTok
}
}
private void CreateInitialSolutionChecksum()
private async Task SynchronizeAssets()
{
// initial solution checksum creation won't be affected by global operation.
// cancellation can only happen if it is being shutdown.
Task.Run(async () =>
{
await UpdateSolutionChecksumAsync(ShutdownCancellationToken).ConfigureAwait(false);
_synchronize = false;
var remoteHostClient = await _service.GetRemoteHostClientAsync(ShutdownCancellationToken).ConfigureAwait(false);
if (remoteHostClient == null)
{
return;
}
var remoteHostClient = await _service.GetRemoteHostClientAsync(ShutdownCancellationToken).ConfigureAwait(false);
if (remoteHostClient == null)
{
return;
}
var solution = _service.Workspace.CurrentSolution;
using (var session = await remoteHostClient.CreateServiceSessionAsync(WellKnownRemoteHostServices.RemoteHostService, solution, ShutdownCancellationToken).ConfigureAwait(false))
{
// ask remote host to sync initial asset
await session.InvokeAsync(WellKnownRemoteHostServices.RemoteHostService_SynchronizeAsync).ConfigureAwait(false);
}
}, ShutdownCancellationToken);
var solution = _service.Workspace.CurrentSolution;
using (var session = await remoteHostClient.CreateServiceSessionAsync(WellKnownRemoteHostServices.RemoteHostService, solution, ShutdownCancellationToken).ConfigureAwait(false))
{
// ask remote host to sync initial asset
await session.InvokeAsync(WellKnownRemoteHostServices.RemoteHostService_SynchronizeAsync).ConfigureAwait(false);
}
}
private static void CancelAndDispose(CancellationTokenSource cancellationSource)
......
......@@ -66,6 +66,12 @@ internal class OutOfProcDiagnosticAnalyzerExecutor : IRemoteHostDiagnosticAnalyz
private async Task<DiagnosticAnalysisResultMap<DiagnosticAnalyzer, DiagnosticAnalysisResult>> AnalyzeInProcAsync(CompilationWithAnalyzers analyzerDriver, Project project, CancellationToken cancellationToken)
{
if (analyzerDriver == null)
{
// no analyzers for in proc process
return DiagnosticAnalysisResultMap.Create(ImmutableDictionary<DiagnosticAnalyzer, DiagnosticAnalysisResult>.Empty, ImmutableDictionary<DiagnosticAnalyzer, AnalyzerTelemetryInfo>.Empty);
}
return await InProcCodeAnalysisDiagnosticAnalyzerExecutor.Instance.AnalyzeAsync(analyzerDriver, project, cancellationToken).ConfigureAwait(false);
}
......@@ -106,6 +112,12 @@ internal class OutOfProcDiagnosticAnalyzerExecutor : IRemoteHostDiagnosticAnalyz
private CompilationWithAnalyzers CreateAnalyzerDriver(CompilationWithAnalyzers analyzerDriver, Func<DiagnosticAnalyzer, bool> predicate)
{
var analyzers = analyzerDriver.Analyzers.Where(predicate).ToImmutableArray();
if (analyzers.Length == 0)
{
// we can't create analyzer driver with 0 analyzers
return null;
}
return analyzerDriver.Compilation.WithAnalyzers(analyzers, analyzerDriver.AnalysisOptions);
}
......
......@@ -24,112 +24,117 @@ public AssetBuilder(IChecksumTreeNode checksumTree)
_serializer = checksumTree.Serializer;
}
public Task<Asset> BuildAsync(SolutionState solutionState, CancellationToken cancellationToken)
public Asset Build(SolutionState solutionState, CancellationToken cancellationToken)
{
return _checksumTree.GetOrCreateAssetAsync(solutionState, GetInfo(solutionState), WellKnownChecksumObjects.SolutionChecksumObjectInfo, CreateSolutionChecksumObjectInfoAsync, cancellationToken);
return _checksumTree.GetOrCreateAsset(solutionState, GetInfo(solutionState), WellKnownChecksumObjects.SolutionChecksumObjectInfo, CreateSolutionChecksumObjectInfo, cancellationToken);
}
public Task<Asset> BuildAsync(ProjectState projectState, CancellationToken cancellationToken)
public Asset Build(ProjectState projectState, CancellationToken cancellationToken)
{
return _checksumTree.GetOrCreateAssetAsync(projectState, GetInfo(projectState), WellKnownChecksumObjects.ProjectChecksumObjectInfo, CreateProjectChecksumObjectInfoAsync, cancellationToken);
return _checksumTree.GetOrCreateAsset(projectState, GetInfo(projectState), WellKnownChecksumObjects.ProjectChecksumObjectInfo, CreateProjectChecksumObjectInfo, cancellationToken);
}
public Task<Asset> BuildAsync(TextDocumentState document, CancellationToken cancellationToken)
public Asset Build(TextDocumentState document, CancellationToken cancellationToken)
{
return _checksumTree.GetOrCreateAssetAsync(document, GetInfo(document), WellKnownChecksumObjects.DocumentChecksumObjectInfo, CreateDocumentChecksumObjectInfoAsync, cancellationToken);
return _checksumTree.GetOrCreateAsset(document, GetInfo(document), WellKnownChecksumObjects.DocumentChecksumObjectInfo, CreateDocumentChecksumObjectInfo, cancellationToken);
}
public Task<Asset> BuildAsync(ProjectState projectState, CompilationOptions compilationOptions, CancellationToken cancellationToken)
public Asset Build(ProjectState projectState, CompilationOptions compilationOptions, CancellationToken cancellationToken)
{
return _checksumTree.GetOrCreateAssetAsync(compilationOptions, projectState, WellKnownChecksumObjects.CompilationOptions, CreateCompilationOptionsAsync, cancellationToken);
return _checksumTree.GetOrCreateAsset(compilationOptions, projectState, WellKnownChecksumObjects.CompilationOptions, CreateCompilationOptions, cancellationToken);
}
public Task<Asset> BuildAsync(ProjectState projectState, ParseOptions parseOptions, CancellationToken cancellationToken)
public Asset Build(ProjectState projectState, ParseOptions parseOptions, CancellationToken cancellationToken)
{
return _checksumTree.GetOrCreateAssetAsync(parseOptions, projectState, WellKnownChecksumObjects.ParseOptions, CreateParseOptionsAsync, cancellationToken);
return _checksumTree.GetOrCreateAsset(parseOptions, projectState, WellKnownChecksumObjects.ParseOptions, CreateParseOptions, cancellationToken);
}
public Task<Asset> BuildAsync(ProjectReference reference, CancellationToken cancellationToken)
public Asset Build(ProjectReference reference, CancellationToken cancellationToken)
{
return _checksumTree.GetOrCreateAssetAsync(reference, reference, WellKnownChecksumObjects.ProjectReference, CreateProjectReferenceAsync, cancellationToken);
return _checksumTree.GetOrCreateAsset(reference, reference, WellKnownChecksumObjects.ProjectReference, CreateProjectReference, cancellationToken);
}
public Task<Asset> BuildAsync(MetadataReference reference, CancellationToken cancellationToken)
public Asset Build(MetadataReference reference, CancellationToken cancellationToken)
{
return _checksumTree.GetOrCreateAssetAsync(reference, reference, WellKnownChecksumObjects.MetadataReference, CreateMetadataReferenceAsync, cancellationToken);
return _checksumTree.GetOrCreateAsset(reference, reference, WellKnownChecksumObjects.MetadataReference, CreateMetadataReference, cancellationToken);
}
public Task<Asset> BuildAsync(AnalyzerReference reference, CancellationToken cancellationToken)
public Asset Build(AnalyzerReference reference, CancellationToken cancellationToken)
{
return _checksumTree.GetOrCreateAssetAsync(reference, reference, WellKnownChecksumObjects.AnalyzerReference, CreateAnalyzerReferenceAsync, cancellationToken);
return _checksumTree.GetOrCreateAsset(reference, reference, WellKnownChecksumObjects.AnalyzerReference, CreateAnalyzerReference, cancellationToken);
}
public Task<Asset> BuildAsync(TextDocumentState state, SourceText unused, CancellationToken cancellationToken)
public Asset Build(TextDocumentState state, SourceText text, CancellationToken cancellationToken)
{
// TODO: currently this is a bit wierd not to hold onto source text.
// it would be nice if SourceText is changed like how recoverable syntax tree work.
return _checksumTree.GetOrCreateAssetAsync(state, state, WellKnownChecksumObjects.SourceText, CreateSourceTextAsync, cancellationToken);
var asset = _checksumTree.GetOrCreateAsset(state, state, WellKnownChecksumObjects.SourceText, CreateSourceText, cancellationToken);
// make sure we keep text alive. this is to make sure we don't do any async call in asset builder
GC.KeepAlive(text);
return asset;
}
private Task<Asset> CreateSolutionChecksumObjectInfoAsync(SolutionChecksumObjectInfo info, string kind, CancellationToken cancellationToken)
private Asset CreateSolutionChecksumObjectInfo(SolutionChecksumObjectInfo info, string kind, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult<Asset>(new Asset<SolutionChecksumObjectInfo>(info, kind, _serializer.SerializeSolutionChecksumObjectInfo));
return new Asset<SolutionChecksumObjectInfo>(info, kind, _serializer.SerializeSolutionChecksumObjectInfo);
}
private Task<Asset> CreateProjectChecksumObjectInfoAsync(ProjectChecksumObjectInfo info, string kind, CancellationToken cancellationToken)
private Asset CreateProjectChecksumObjectInfo(ProjectChecksumObjectInfo info, string kind, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult<Asset>(new Asset<ProjectChecksumObjectInfo>(info, kind, _serializer.SerializeProjectChecksumObjectInfo));
return new Asset<ProjectChecksumObjectInfo>(info, kind, _serializer.SerializeProjectChecksumObjectInfo);
}
private Task<Asset> CreateDocumentChecksumObjectInfoAsync(DocumentChecksumObjectInfo info, string kind, CancellationToken cancellationToken)
private Asset CreateDocumentChecksumObjectInfo(DocumentChecksumObjectInfo info, string kind, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult<Asset>(new Asset<DocumentChecksumObjectInfo>(info, kind, _serializer.SerializeDocumentChecksumObjectInfo));
return new Asset<DocumentChecksumObjectInfo>(info, kind, _serializer.SerializeDocumentChecksumObjectInfo);
}
private Task<Asset> CreateCompilationOptionsAsync(ProjectState projectState, string kind, CancellationToken cancellationToken)
private Asset CreateCompilationOptions(ProjectState projectState, string kind, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult<Asset>(new LanguageSpecificAsset<CompilationOptions>(projectState.Language, projectState.CompilationOptions, kind, _serializer.SerializeCompilationOptions));
return new LanguageSpecificAsset<CompilationOptions>(projectState.Language, projectState.CompilationOptions, kind, _serializer.SerializeCompilationOptions);
}
private Task<Asset> CreateParseOptionsAsync(ProjectState projectState, string kind, CancellationToken cancellationToken)
private Asset CreateParseOptions(ProjectState projectState, string kind, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult<Asset>(new LanguageSpecificAsset<ParseOptions>(projectState.Language, projectState.ParseOptions, kind, _serializer.SerializeParseOptions));
return new LanguageSpecificAsset<ParseOptions>(projectState.Language, projectState.ParseOptions, kind, _serializer.SerializeParseOptions);
}
private Task<Asset> CreateProjectReferenceAsync(ProjectReference reference, string kind, CancellationToken cancellationToken)
private Asset CreateProjectReference(ProjectReference reference, string kind, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult<Asset>(new Asset<ProjectReference>(reference, kind, _serializer.SerializeProjectReference));
return new Asset<ProjectReference>(reference, kind, _serializer.SerializeProjectReference);
}
private Task<Asset> CreateMetadataReferenceAsync(MetadataReference reference, string kind, CancellationToken cancellationToken)
private Asset CreateMetadataReference(MetadataReference reference, string kind, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var checksum = _serializer.HostSerializationService.CreateChecksum(reference, cancellationToken);
return Task.FromResult<Asset>(new MetadataReferenceAsset(_serializer, reference, checksum, kind));
return new MetadataReferenceAsset(_serializer, reference, checksum, kind);
}
private Task<Asset> CreateAnalyzerReferenceAsync(AnalyzerReference reference, string kind, CancellationToken cancellationToken)
private Asset CreateAnalyzerReference(AnalyzerReference reference, string kind, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var checksum = _serializer.HostSerializationService.CreateChecksum(reference, cancellationToken);
return Task.FromResult<Asset>(new AnalyzerReferenceAsset(_serializer, reference, checksum, kind));
return new AnalyzerReferenceAsset(_serializer, reference, checksum, kind);
}
private async Task<Asset> CreateSourceTextAsync(TextDocumentState state, string kind, CancellationToken cancellationToken)
private Asset CreateSourceText(TextDocumentState state, string kind, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var text = await state.GetTextAsync(cancellationToken).ConfigureAwait(false);
var checksum = new Checksum(text.GetChecksum());
SourceText text;
Contract.ThrowIfFalse(state.TryGetText(out text));
var checksum = new Checksum(text.GetChecksum());
return new SourceTextAsset(_serializer, state, checksum, kind);
}
......@@ -182,13 +187,20 @@ public AssetOnlyTreeNode(Solution solution)
public Serializer Serializer { get; }
public Task<TResult> GetOrCreateAssetAsync<TKey, TValue, TResult>(
TKey key, TValue value, string kind, Func<TValue, string, CancellationToken, Task<TResult>> valueGetterAsync, CancellationToken cancellationToken)
public Asset GetOrCreateAsset<TKey, TValue>(
TKey key, TValue value, string kind, Func<TValue, string, CancellationToken, Asset> valueGetter, CancellationToken cancellationToken)
where TKey : class
where TResult : Asset
{
Contract.ThrowIfNull(key);
return valueGetterAsync(value, kind, cancellationToken);
return valueGetter(value, kind, cancellationToken);
}
public TResult GetOrCreateChecksumObjectWithChildren<TKey, TValue, TResult>(
TKey key, TValue value, string kind, Func<TKey, TValue, string, CancellationToken, TResult> valueGetter, CancellationToken cancellationToken)
where TKey : class
where TResult : ChecksumObjectWithChildren
{
return Contract.FailWithReturn<TResult>("shouldn't be called");
}
public Task<TResult> GetOrCreateChecksumObjectWithChildrenAsync<TKey, TValue, TResult>(
......
......@@ -60,7 +60,8 @@ internal abstract class ChecksumObjectWithChildren : ChecksumObject
public override Task WriteObjectToAsync(ObjectWriter writer, CancellationToken cancellationToken)
{
return _serializer.SerializeChecksumObjectWithChildrenAsync(this, writer, cancellationToken);
_serializer.SerializeChecksumObjectWithChildren(this, writer, cancellationToken);
return SpecializedTasks.EmptyTask;
}
private static Checksum CreateChecksum(string kind, object[] children)
......
......@@ -47,25 +47,25 @@ private Task<ChecksumCollection> BuildAsync<TState>(ImmutableDictionary<Document
return _checksumTree.GetOrCreateChecksumObjectWithChildrenAsync(key, documentStates, kind, CreateDocumentChecksumObjectAsync, cancellationToken);
}
private Task<ChecksumCollection> BuildAsync(IReadOnlyList<ProjectReference> key, IEnumerable<ProjectReference> projectReferences, CancellationToken cancellationToken)
private ChecksumCollection Build(IReadOnlyList<ProjectReference> key, IEnumerable<ProjectReference> projectReferences, CancellationToken cancellationToken)
{
return _checksumTree.GetOrCreateChecksumObjectWithChildrenAsync(key, projectReferences, WellKnownChecksumObjects.ProjectReferences, CreateChecksumCollectionsAsync, cancellationToken);
return _checksumTree.GetOrCreateChecksumObjectWithChildren(key, projectReferences, WellKnownChecksumObjects.ProjectReferences, CreateChecksumCollections, cancellationToken);
}
private Task<ChecksumCollection> BuildAsync(IReadOnlyList<MetadataReference> key, IEnumerable<MetadataReference> metadataReferences, CancellationToken cancellationToken)
private ChecksumCollection Build(IReadOnlyList<MetadataReference> key, IEnumerable<MetadataReference> metadataReferences, CancellationToken cancellationToken)
{
return _checksumTree.GetOrCreateChecksumObjectWithChildrenAsync(key, metadataReferences, WellKnownChecksumObjects.MetadataReferences, CreateChecksumCollectionsAsync, cancellationToken);
return _checksumTree.GetOrCreateChecksumObjectWithChildren(key, metadataReferences, WellKnownChecksumObjects.MetadataReferences, CreateChecksumCollections, cancellationToken);
}
private Task<ChecksumCollection> BuildAsync(IReadOnlyList<AnalyzerReference> key, IEnumerable<AnalyzerReference> analyzerReferences, CancellationToken cancellationToken)
private ChecksumCollection Build(IReadOnlyList<AnalyzerReference> key, IEnumerable<AnalyzerReference> analyzerReferences, CancellationToken cancellationToken)
{
return _checksumTree.GetOrCreateChecksumObjectWithChildrenAsync(key, analyzerReferences, WellKnownChecksumObjects.AnalyzerReferences, CreateChecksumCollectionsAsync, cancellationToken);
return _checksumTree.GetOrCreateChecksumObjectWithChildren(key, analyzerReferences, WellKnownChecksumObjects.AnalyzerReferences, CreateChecksumCollections, cancellationToken);
}
private async Task<SolutionChecksumObject> CreateSolutionChecksumObjectAsync(SolutionState key, SolutionState solutionState, string kind, CancellationToken cancellationToken)
{
var assetBuilder = new AssetBuilder(_checksumTree);
var info = await assetBuilder.BuildAsync(solutionState, cancellationToken).ConfigureAwait(false);
var info = assetBuilder.Build(solutionState, cancellationToken);
var subTreeNode = _checksumTree.GetOrCreateSubTreeNode(key);
var subSnapshotBuilder = new ChecksumTreeBuilder(subTreeNode);
......@@ -77,20 +77,20 @@ private async Task<SolutionChecksumObject> CreateSolutionChecksumObjectAsync(Sol
private async Task<ProjectChecksumObject> CreateProjectChecksumObjectAsync(ProjectState key, ProjectState projectState, string kind, CancellationToken cancellationToken)
{
var assetBuilder = new AssetBuilder(_checksumTree);
var info = await assetBuilder.BuildAsync(projectState, cancellationToken).ConfigureAwait(false);
var info = assetBuilder.Build(projectState, cancellationToken);
var subTreeNode = _checksumTree.GetOrCreateSubTreeNode(key);
var subSnapshotBuilder = new ChecksumTreeBuilder(subTreeNode);
var documents = await subSnapshotBuilder.BuildAsync(projectState.DocumentStates, projectState.DocumentIds.Select(id => projectState.DocumentStates[id]), WellKnownChecksumObjects.Documents, cancellationToken).ConfigureAwait(false);
var projectReferences = await subSnapshotBuilder.BuildAsync(projectState.ProjectReferences, projectState.ProjectReferences, cancellationToken).ConfigureAwait(false);
var metadataReferences = await subSnapshotBuilder.BuildAsync(projectState.MetadataReferences, projectState.MetadataReferences, cancellationToken).ConfigureAwait(false);
var analyzerReferences = await subSnapshotBuilder.BuildAsync(projectState.AnalyzerReferences, projectState.AnalyzerReferences, cancellationToken).ConfigureAwait(false);
var projectReferences = subSnapshotBuilder.Build(projectState.ProjectReferences, projectState.ProjectReferences, cancellationToken);
var metadataReferences = subSnapshotBuilder.Build(projectState.MetadataReferences, projectState.MetadataReferences, cancellationToken);
var analyzerReferences = subSnapshotBuilder.Build(projectState.AnalyzerReferences, projectState.AnalyzerReferences, cancellationToken);
var additionalDocuments = await subSnapshotBuilder.BuildAsync(projectState.AdditionalDocumentStates, projectState.AdditionalDocumentIds.Select(id => projectState.AdditionalDocumentStates[id]), WellKnownChecksumObjects.TextDocuments, cancellationToken).ConfigureAwait(false);
var subAssetBuilder = new AssetBuilder(subTreeNode);
var compilationOptions = await subAssetBuilder.BuildAsync(projectState, projectState.CompilationOptions, cancellationToken).ConfigureAwait(false);
var parseOptions = await subAssetBuilder.BuildAsync(projectState, projectState.ParseOptions, cancellationToken).ConfigureAwait(false);
var compilationOptions = subAssetBuilder.Build(projectState, projectState.CompilationOptions, cancellationToken);
var parseOptions = subAssetBuilder.Build(projectState, projectState.ParseOptions, cancellationToken);
return new ProjectChecksumObject(
_serializer, info.Checksum, compilationOptions.Checksum, parseOptions.Checksum,
......@@ -100,10 +100,11 @@ private async Task<ProjectChecksumObject> CreateProjectChecksumObjectAsync(Proje
private async Task<DocumentChecksumObject> CreateDocumentChecksumObjectAsync(TextDocumentState key, TextDocumentState documentState, string kind, CancellationToken cancellationToken)
{
var assetBuilder = new AssetBuilder(_checksumTree);
var info = await assetBuilder.BuildAsync(documentState, cancellationToken).ConfigureAwait(false);
var info = assetBuilder.Build(documentState, cancellationToken);
// TODO: think of a way to skip getting text
var sourceText = await key.GetTextAsync(cancellationToken).ConfigureAwait(false);
var text = await assetBuilder.BuildAsync(key, sourceText, cancellationToken).ConfigureAwait(false);
var text = assetBuilder.Build(key, sourceText, cancellationToken);
return new DocumentChecksumObject(_serializer, info.Checksum, text.Checksum);
}
......@@ -133,58 +134,93 @@ private async Task<DocumentChecksumObject> CreateDocumentChecksumObjectAsync(Tex
return CreateChecksumCollectionsAsync(documentStates, kind, snapshotBuilder.BuildAsync, cancellationToken);
}
private Task<ChecksumCollection> CreateChecksumCollectionsAsync(
private async Task<ChecksumCollection> CreateChecksumCollectionsAsync<TValue, TChecksumObject>(
IEnumerable<TValue> items, string kind, Func<TValue, CancellationToken, Task<TChecksumObject>> buildAsync, CancellationToken cancellationToken) where TChecksumObject : ChecksumObject
{
using (var pooledObject = Creator.CreateList<Task<TChecksumObject>>())
{
// create asyn checksums concurrently
var tasks = pooledObject.Object;
foreach (var item in items)
{
tasks.Add(buildAsync(item, cancellationToken));
}
await Task.WhenAll(tasks).ConfigureAwait(false);
var checksums = new Checksum[tasks.Count];
for (var i = 0; i < tasks.Count; i++)
{
var task = tasks[i];
// we use await here to make sure when exception is raised, especially cancellation exception,
// right exception is raised from task. if we use .Result directly, it will raise aggregated exception
// rather than cancellation exception.
// since task is already completed, when there is no exception for the task. await will be no-op
checksums[i] = (await task.ConfigureAwait(false)).Checksum;
}
return new ChecksumCollection(_serializer, kind, checksums);
}
}
private ChecksumCollection CreateChecksumCollections(
IReadOnlyList<ProjectReference> key, IEnumerable<ProjectReference> references, string kind, CancellationToken cancellationToken)
{
if (key.Count == 0)
{
return GetEmptyChecksumCollectionTask(kind);
return GetEmptyChecksumCollection(kind);
}
var assetBuilder = new AssetBuilder(_checksumTree.GetOrCreateSubTreeNode(key));
return CreateChecksumCollectionsAsync(references, kind, assetBuilder.BuildAsync, cancellationToken);
return CreateChecksumCollections(references, kind, assetBuilder.Build, cancellationToken);
}
private Task<ChecksumCollection> CreateChecksumCollectionsAsync(
private ChecksumCollection CreateChecksumCollections(
IReadOnlyList<MetadataReference> key, IEnumerable<MetadataReference> references, string kind, CancellationToken cancellationToken)
{
if (key.Count == 0)
{
return GetEmptyChecksumCollectionTask(kind);
return GetEmptyChecksumCollection(kind);
}
var assetBuilder = new AssetBuilder(_checksumTree.GetOrCreateSubTreeNode(key));
return CreateChecksumCollectionsAsync(references, kind, assetBuilder.BuildAsync, cancellationToken);
return CreateChecksumCollections(references, kind, assetBuilder.Build, cancellationToken);
}
private Task<ChecksumCollection> CreateChecksumCollectionsAsync(
private ChecksumCollection CreateChecksumCollections(
IReadOnlyList<AnalyzerReference> key, IEnumerable<AnalyzerReference> references, string kind, CancellationToken cancellationToken)
{
if (key.Count == 0)
{
return GetEmptyChecksumCollectionTask(kind);
return GetEmptyChecksumCollection(kind);
}
var assetBuilder = new AssetBuilder(_checksumTree.GetOrCreateSubTreeNode(key));
return CreateChecksumCollectionsAsync(references, kind, assetBuilder.BuildAsync, cancellationToken);
return CreateChecksumCollections(references, kind, assetBuilder.Build, cancellationToken);
}
private async Task<ChecksumCollection> CreateChecksumCollectionsAsync<TValue, TChecksumObject>(
IEnumerable<TValue> items, string kind, Func<TValue, CancellationToken, Task<TChecksumObject>> buildAsync, CancellationToken cancellationToken) where TChecksumObject : ChecksumObject
private ChecksumCollection CreateChecksumCollections<TValue>(IEnumerable<TValue> items, string kind, Func<TValue, CancellationToken, ChecksumObject> build, CancellationToken cancellationToken)
{
var list = new List<Checksum>();
foreach (var item in items)
{
var checksumObject = await buildAsync(item, cancellationToken).ConfigureAwait(false);
var checksumObject = build(item, cancellationToken);
list.Add(checksumObject.Checksum);
}
return new ChecksumCollection(_serializer, kind, list.ToArray());
}
private Task<ChecksumCollection> GetEmptyChecksumCollectionTask(string kind)
private ChecksumCollection GetEmptyChecksumCollection(string kind)
{
return ChecksumTreeCollection.GetOrCreateEmptyChecksumCollection(_serializer, kind);
}
private Task<ChecksumCollection> GetEmptyChecksumCollectionTask(string kind)
{
return ChecksumTreeCollection.GetOrCreateEmptyChecksumCollectionTask(_serializer, kind);
}
}
}
......@@ -252,13 +252,21 @@ public IChecksumTreeNode GetOrCreateSubTreeNode<TKey>(TKey key)
return await GetOrCreateChecksumObjectAsync(key, value, kind, (v, k, c) => valueGetterAsync(key, v, k, c), cancellationToken).ConfigureAwait(false);
}
public Task<TAsset> GetOrCreateAssetAsync<TKey, TValue, TAsset>(
public TChecksumObject GetOrCreateChecksumObjectWithChildren<TKey, TValue, TChecksumObject>(
TKey key, TValue value, string kind,
Func<TValue, string, CancellationToken, Task<TAsset>> valueGetterAsync, CancellationToken cancellationToken)
Func<TKey, TValue, string, CancellationToken, TChecksumObject> valueGetter, CancellationToken cancellationToken)
where TKey : class
where TChecksumObject : ChecksumObjectWithChildren
{
return GetOrCreateChecksumObject(key, value, kind, (v, k, c) => valueGetter(key, v, k, c), cancellationToken);
}
public Asset GetOrCreateAsset<TKey, TValue>(
TKey key, TValue value, string kind,
Func<TValue, string, CancellationToken, Asset> valueGetter, CancellationToken cancellationToken)
where TKey : class
where TAsset : Asset
{
return GetOrCreateChecksumObjectAsync(key, value, kind, valueGetterAsync, cancellationToken);
return GetOrCreateChecksumObject(key, value, kind, valueGetter, cancellationToken);
}
protected static void AppendChecksumObjects(
......@@ -266,7 +274,7 @@ public IChecksumTreeNode GetOrCreateSubTreeNode<TKey>(TKey key)
HashSet<Checksum> searchingChecksumsLeft,
int currentNodeChecksumCount,
IEnumerable<Checksum> currentNodeChecksums,
Func<Checksum, ChecksumObject> checksumGetterForCurrentNode,
Func<Checksum, ChecksumObject> checksumGetterForCurrentNode,
CancellationToken cancellationToken)
{
// this will iterate through candidate checksums to see whether that checksum exists in both
......@@ -362,6 +370,43 @@ private ChecksumObject GetChecksumObjectFromTreeNode(ChecksumObjectCache cache,
}
}
private TChecksumObject GetOrCreateChecksumObject<TKey, TValue, TChecksumObject>(
TKey key, TValue value, string kind,
Func<TValue, string, CancellationToken, TChecksumObject> valueGetter, CancellationToken cancellationToken)
where TKey : class where TChecksumObject : ChecksumObject
{
using (Logger.LogBlock(FunctionId.ChecksumTreeNode_GetOrCreateChecksumObject, CreateLogMessage, key, kind, cancellationToken))
{
Contract.ThrowIfNull(key);
// ask myself
ChecksumObject checksumObject;
var entry = TryGetChecksumObjectEntry(key, kind, cancellationToken);
if (entry != null && entry.TryGetValue(kind, out checksumObject))
{
return (TChecksumObject)SaveAndReturn(key, checksumObject, entry);
}
// ask owner
entry = _owner.TryGetChecksumObjectEntry(key, kind, cancellationToken);
if (entry == null)
{
// owner doesn't have it, create one.
checksumObject = valueGetter(value, kind, cancellationToken);
}
else if (!entry.TryGetValue(kind, out checksumObject))
{
// owner doesn't have this particular kind, create one.
checksumObject = valueGetter(value, kind, cancellationToken);
}
// record local copy (reference) and return it.
// REVIEW: we can go ref count route rather than this (local copy). but then we need to make sure there is no leak.
// for now, we go local copy route since overhead is small (just duplicated reference pointer), but reduce complexity a lot.
return (TChecksumObject)SaveAndReturn(key, checksumObject, entry);
}
}
private ChecksumObject SaveAndReturn(object key, ChecksumObject checksumObject, ChecksumObjectCache entry = null)
{
// create new entry if it is not already given
......
......@@ -17,7 +17,8 @@ internal partial class ChecksumTreeCollection
{
// serializer and empty checksum collection task cache - this is to reduce allocations
private readonly static ConditionalWeakTable<HostWorkspaceServices, Serializer> s_serializerCache = new ConditionalWeakTable<HostWorkspaceServices, Serializer>();
private readonly static ConditionalWeakTable<Serializer, ConcurrentDictionary<string, Task<ChecksumCollection>>> s_emptyChecksumCollectionTaskCache = new ConditionalWeakTable<Serializer, ConcurrentDictionary<string, Task<ChecksumCollection>>>();
private readonly static ConditionalWeakTable<Serializer, ConcurrentDictionary<string, ChecksumCollection>> s_emptyChecksumCollectionCache = new ConditionalWeakTable<Serializer, ConcurrentDictionary<string, ChecksumCollection>>();
private readonly static ConditionalWeakTable<ChecksumCollection, Task<ChecksumCollection>> s_emptyChecksumCollectionTaskCache = new ConditionalWeakTable<ChecksumCollection, Task<ChecksumCollection>>();
/// <summary>
/// global asset is an asset which life time is same as host
......@@ -166,24 +167,34 @@ public void UnregisterSnapshot(ChecksumScope snapshot)
}
private static readonly ConditionalWeakTable<HostWorkspaceServices, Serializer>.CreateValueCallback s_serializerCallback = s => new Serializer(s);
public static Serializer GetOrCreateSerializer(HostWorkspaceServices services)
{
return s_serializerCache.GetValue(services, s_serializerCallback);
}
private static readonly ConditionalWeakTable<Serializer, ConcurrentDictionary<string, Task<ChecksumCollection>>>.CreateValueCallback s_emptyChecksumCollectionCallback =
s => new ConcurrentDictionary<string, Task<ChecksumCollection>>(concurrencyLevel: 2, capacity: 20);
public static Task<ChecksumCollection> GetOrCreateEmptyChecksumCollection(Serializer serializer, string kind)
private static readonly ConditionalWeakTable<Serializer, ConcurrentDictionary<string, ChecksumCollection>>.CreateValueCallback s_emptyChecksumCollectionCallback =
s => new ConcurrentDictionary<string, ChecksumCollection>(concurrencyLevel: 2, capacity: 20);
public static ChecksumCollection GetOrCreateEmptyChecksumCollection(Serializer serializer, string kind)
{
var map = s_emptyChecksumCollectionTaskCache.GetValue(serializer, s_emptyChecksumCollectionCallback);
var map = s_emptyChecksumCollectionCache.GetValue(serializer, s_emptyChecksumCollectionCallback);
Task<ChecksumCollection> task;
if (map.TryGetValue(kind, out task))
ChecksumCollection collection;
if (map.TryGetValue(kind, out collection))
{
return task;
return collection;
}
return map.GetOrAdd(kind, _ => Task.FromResult(new ChecksumCollection(serializer, kind, SpecializedCollections.EmptyArray<object>())));
return map.GetOrAdd(kind, _ => new ChecksumCollection(serializer, kind, SpecializedCollections.EmptyArray<object>()));
}
private static readonly ConditionalWeakTable<ChecksumCollection, Task<ChecksumCollection>>.CreateValueCallback s_emptyChecksumCollectionTaskCallback = c => Task.FromResult(c);
public static Task<ChecksumCollection> GetOrCreateEmptyChecksumCollectionTask(Serializer serializer, string kind)
{
var collection = GetOrCreateEmptyChecksumCollection(serializer, kind);
return s_emptyChecksumCollectionTaskCache.GetValue(collection, s_emptyChecksumCollectionTaskCallback);
}
}
}
......@@ -29,7 +29,6 @@ internal interface IChecksumTreeNode
IChecksumTreeNode GetOrCreateSubTreeNode<TKey>(TKey key);
// TResult since Task doesn't allow covariant
Task<TResult> GetOrCreateChecksumObjectWithChildrenAsync<TKey, TValue, TResult>(
TKey key, TValue value, string kind,
Func<TKey, TValue, string, CancellationToken, Task<TResult>> valueGetterAsync,
......@@ -37,12 +36,17 @@ internal interface IChecksumTreeNode
where TKey : class
where TResult : ChecksumObjectWithChildren;
// TResult since Task doesn't allow covariant
Task<TResult> GetOrCreateAssetAsync<TKey, TValue, TResult>(
TResult GetOrCreateChecksumObjectWithChildren<TKey, TValue, TResult>(
TKey key, TValue value, string kind,
Func<TValue, string, CancellationToken, Task<TResult>> valueGetterAsync,
Func<TKey, TValue, string, CancellationToken, TResult> valueGetter,
CancellationToken cancellationToken)
where TKey : class
where TResult : Asset;
where TResult : ChecksumObjectWithChildren;
Asset GetOrCreateAsset<TKey, TValue>(
TKey key, TValue value, string kind,
Func<TValue, string, CancellationToken, Asset> valueGetter,
CancellationToken cancellationToken)
where TKey : class;
}
}
......@@ -3,7 +3,6 @@
using System;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Execution
......@@ -19,7 +18,7 @@ internal partial class Serializer
private static readonly ImmutableDictionary<string, Func<Serializer, string, object[], ChecksumObjectWithChildren>> s_creatorMap = CreateCreatorMap();
public async Task SerializeChecksumObjectWithChildrenAsync(ChecksumObjectWithChildren checksumObject, ObjectWriter writer, CancellationToken cancellationToken)
public void SerializeChecksumObjectWithChildren(ChecksumObjectWithChildren checksumObject, ObjectWriter writer, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
......@@ -41,7 +40,7 @@ public async Task SerializeChecksumObjectWithChildrenAsync(ChecksumObjectWithChi
if (checksumCollection != null)
{
writer.WriteByte(ChecksumCollectionKind);
await SerializeChecksumObjectWithChildrenAsync(checksumCollection, writer, cancellationToken).ConfigureAwait(false);
SerializeChecksumObjectWithChildren(checksumCollection, writer, cancellationToken);
continue;
}
......
......@@ -350,5 +350,6 @@ internal enum FunctionId
AssetService_SynchronizeAssetsAsync,
AssetService_SynchronizeSolutionAssetsAsync,
SolutionChecksumServiceFactory_GetChecksumObjects,
ChecksumTreeNode_GetOrCreateChecksumObject,
}
}
......@@ -311,7 +311,7 @@ public async Task MetadataReference_RoundTrip_Test()
var trees = new ChecksumTreeCollection();
var assetBuilder = new AssetBuilder(trees.CreateRootTreeNode(workspace.CurrentSolution.State));
var assetFromFile = await assetBuilder.BuildAsync(reference, CancellationToken.None).ConfigureAwait(false);
var assetFromFile = assetBuilder.Build(reference, CancellationToken.None);
var assetFromStorage = await CloneAssetAsync(serializer, assetBuilder, assetFromFile).ConfigureAwait(false);
var assetFromStorage2 = await CloneAssetAsync(serializer, assetBuilder, assetFromStorage).ConfigureAwait(false);
}
......@@ -482,7 +482,7 @@ private static async Task<Asset> CloneAssetAsync(Serializer serializer, AssetBui
using (var reader = new ObjectReader(stream))
{
var recovered = serializer.Deserialize<MetadataReference>(WellKnownChecksumObjects.MetadataReference, reader, CancellationToken.None);
var assetFromStorage = await assetBuilder.BuildAsync(recovered, CancellationToken.None).ConfigureAwait(false);
var assetFromStorage = assetBuilder.Build(recovered, CancellationToken.None);
Assert.Equal(asset.Checksum, assetFromStorage.Checksum);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册