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

Merge pull request #16604 from heejaechang/fixperfRegression

removed additional cost I can find from trace compared to code before…
...@@ -13,7 +13,10 @@ namespace Microsoft.VisualStudio.LanguageServices ...@@ -13,7 +13,10 @@ namespace Microsoft.VisualStudio.LanguageServices
/// </summary> /// </summary>
internal sealed class SolutionEventMonitor : IDisposable internal sealed class SolutionEventMonitor : IDisposable
{ {
private GlobalOperationNotificationService _notificationService; private const string SolutionBuilding = "Solution Building";
private const string SolutionOpening = "Solution Opening";
private IGlobalOperationNotificationService _notificationService;
private Dictionary<string, GlobalOperationRegistration> _operations = new Dictionary<string, GlobalOperationRegistration>(); private Dictionary<string, GlobalOperationRegistration> _operations = new Dictionary<string, GlobalOperationRegistration>();
public SolutionEventMonitor(VisualStudioWorkspace workspace) public SolutionEventMonitor(VisualStudioWorkspace workspace)
...@@ -21,8 +24,23 @@ public SolutionEventMonitor(VisualStudioWorkspace workspace) ...@@ -21,8 +24,23 @@ public SolutionEventMonitor(VisualStudioWorkspace workspace)
var notificationService = workspace.Services.GetService<IGlobalOperationNotificationService>() as GlobalOperationNotificationService; var notificationService = workspace.Services.GetService<IGlobalOperationNotificationService>() as GlobalOperationNotificationService;
if (notificationService != null) if (notificationService != null)
{ {
// subscribe to events only if it is normal service. if it is one from unit test or other, don't bother to subscribe
_notificationService = notificationService; _notificationService = notificationService;
// make sure we set initial state correctly. otherwise, we can get into a race where we might miss the very first events
if (KnownUIContexts.SolutionBuildingContext.IsActive)
{
ContextChanged(active: true, operation: SolutionBuilding);
}
KnownUIContexts.SolutionBuildingContext.UIContextChanged += SolutionBuildingContextChanged; KnownUIContexts.SolutionBuildingContext.UIContextChanged += SolutionBuildingContextChanged;
// make sure we set initial state correctly. otherwise, we can get into a race where we might miss the very first events
if (KnownUIContexts.SolutionOpeningContext.IsActive)
{
ContextChanged(active: true, operation: SolutionOpening);
}
KnownUIContexts.SolutionOpeningContext.UIContextChanged += SolutionOpeningContextChanged; KnownUIContexts.SolutionOpeningContext.UIContextChanged += SolutionOpeningContextChanged;
} }
} }
...@@ -46,15 +64,15 @@ public void Dispose() ...@@ -46,15 +64,15 @@ public void Dispose()
private void SolutionBuildingContextChanged(object sender, UIContextChangedEventArgs e) private void SolutionBuildingContextChanged(object sender, UIContextChangedEventArgs e)
{ {
ContextChangedWorker(e, "Solution Building"); ContextChanged(e.Activated, SolutionBuilding);
} }
private void SolutionOpeningContextChanged(object sender, UIContextChangedEventArgs e) private void SolutionOpeningContextChanged(object sender, UIContextChangedEventArgs e)
{ {
ContextChangedWorker(e, "Solution Opening"); ContextChanged(e.Activated, SolutionOpening);
} }
private void ContextChangedWorker(UIContextChangedEventArgs e, string operation) private void ContextChanged(bool active, string operation)
{ {
if (_notificationService == null) if (_notificationService == null)
{ {
...@@ -63,7 +81,7 @@ private void ContextChangedWorker(UIContextChangedEventArgs e, string operation) ...@@ -63,7 +81,7 @@ private void ContextChangedWorker(UIContextChangedEventArgs e, string operation)
TryCancelPendingNotification(operation); TryCancelPendingNotification(operation);
if (e.Activated) if (active)
{ {
_operations[operation] = _notificationService.Start(operation); _operations[operation] = _notificationService.Start(operation);
} }
...@@ -75,6 +93,7 @@ private void TryCancelPendingNotification(string operation) ...@@ -75,6 +93,7 @@ private void TryCancelPendingNotification(string operation)
{ {
globalOperation.Done(); globalOperation.Done();
globalOperation.Dispose(); globalOperation.Dispose();
_operations.Remove(operation); _operations.Remove(operation);
} }
} }
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Remote;
using Roslyn.Test.Utilities; using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Roslyn.VisualStudio.Next.UnitTests.Mocks; using Roslyn.VisualStudio.Next.UnitTests.Mocks;
using Xunit; using Xunit;
...@@ -114,7 +115,7 @@ public async Task TestProjectSynchronization() ...@@ -114,7 +115,7 @@ public async Task TestProjectSynchronization()
var source = new TestAssetSource(storage, sessionId, map); var source = new TestAssetSource(storage, sessionId, map);
var service = new AssetService(sessionId, storage); var service = new AssetService(sessionId, storage);
await service.SynchronizeProjectAssetsAsync(await project.State.GetChecksumAsync(CancellationToken.None), CancellationToken.None); await service.SynchronizeProjectAssetsAsync(SpecializedCollections.SingletonEnumerable(await project.State.GetChecksumAsync(CancellationToken.None)), CancellationToken.None);
object data; object data;
foreach (var kv in map) foreach (var kv in map)
......
...@@ -86,7 +86,7 @@ public async Task SynchronizeSolutionAssetsAsync(Checksum solutionChecksum, Canc ...@@ -86,7 +86,7 @@ public async Task SynchronizeSolutionAssetsAsync(Checksum solutionChecksum, Canc
} }
} }
public async Task SynchronizeProjectAssetsAsync(Checksum projectChecksum, CancellationToken cancellationToken) public async Task SynchronizeProjectAssetsAsync(IEnumerable<Checksum> projectChecksums, CancellationToken cancellationToken)
{ {
// this will pull in assets that belong to the given project checksum to this remote host. // this will pull in assets that belong to the given project checksum to this remote host.
// this one is not supposed to be used for functionality but only for perf. that is why it doesn't return anything. // this one is not supposed to be used for functionality but only for perf. that is why it doesn't return anything.
...@@ -96,10 +96,10 @@ public async Task SynchronizeProjectAssetsAsync(Checksum projectChecksum, Cancel ...@@ -96,10 +96,10 @@ public async Task SynchronizeProjectAssetsAsync(Checksum projectChecksum, Cancel
// one can call this method to make cache hot for all assets that belong to the project checksum so that GetAssetAsync call will most likely cache hit. // one can call this method to make cache hot for all assets that belong to the project checksum so that GetAssetAsync call will most likely cache hit.
// it is most likely since we might change cache hueristic in future which make data to live a lot shorter in the cache, and the data might get expired // it is most likely since we might change cache hueristic in future which make data to live a lot shorter in the cache, and the data might get expired
// before one actually consume the data. // before one actually consume the data.
using (Logger.LogBlock(FunctionId.AssetService_SynchronizeProjectAssetsAsync, Checksum.GetChecksumLogInfo, projectChecksum, cancellationToken)) using (Logger.LogBlock(FunctionId.AssetService_SynchronizeProjectAssetsAsync, Checksum.GetChecksumsLogInfo, projectChecksums, cancellationToken))
{ {
var syncer = new ChecksumSynchronizer(this); var syncer = new ChecksumSynchronizer(this);
await syncer.SynchronizeProjectAssetsAsync(projectChecksum, cancellationToken).ConfigureAwait(false); await syncer.SynchronizeProjectAssetsAsync(projectChecksums, cancellationToken).ConfigureAwait(false);
} }
} }
......
...@@ -23,10 +23,8 @@ public ChecksumSynchronizer(AssetService assetService) ...@@ -23,10 +23,8 @@ public ChecksumSynchronizer(AssetService assetService)
public async Task SynchronizeAssetsAsync(IEnumerable<Checksum> checksums, CancellationToken cancellationToken) public async Task SynchronizeAssetsAsync(IEnumerable<Checksum> checksums, CancellationToken cancellationToken)
{ {
using (await s_gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) using (await s_gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
using (var pooledObject = SharedPools.Default<HashSet<Checksum>>().GetPooledObject())
{ {
AddIfNeeded(pooledObject.Object, checksums); await SynchronizeAssets_NoLockAsync(checksums, cancellationToken).ConfigureAwait(false);
await _assetService.SynchronizeAssetsAsync(pooledObject.Object, cancellationToken).ConfigureAwait(false);
} }
} }
...@@ -40,100 +38,77 @@ public async Task SynchronizeSolutionAssetsAsync(Checksum solutionChecksum, Canc ...@@ -40,100 +38,77 @@ public async Task SynchronizeSolutionAssetsAsync(Checksum solutionChecksum, Canc
var solutionChecksumObject = await _assetService.GetAssetAsync<SolutionStateChecksums>(solutionChecksum, cancellationToken).ConfigureAwait(false); var solutionChecksumObject = await _assetService.GetAssetAsync<SolutionStateChecksums>(solutionChecksum, cancellationToken).ConfigureAwait(false);
// second, get direct children of the solution // second, get direct children of the solution
await SynchronizeSolutionAsync(solutionChecksumObject, cancellationToken).ConfigureAwait(false); await SynchronizeAssets_NoLockAsync(solutionChecksumObject.Children, cancellationToken).ConfigureAwait(false);
// third, get direct children for all projects in the solution
await SynchronizeProjectsAsync(solutionChecksumObject, cancellationToken).ConfigureAwait(false);
// last, get direct children for all documents in the solution // third and last get direct children for all projects and documents in the solution
await SynchronizeDocumentsAsync(solutionChecksumObject, cancellationToken).ConfigureAwait(false); await SynchronizeProjectAssets_NoLockAsync(solutionChecksumObject.Projects, cancellationToken).ConfigureAwait(false);
} }
} }
public async Task SynchronizeProjectAssetsAsync(Checksum projectChecksum, CancellationToken cancellationToken) public async Task SynchronizeProjectAssetsAsync(IEnumerable<Checksum> projectChecksums, CancellationToken cancellationToken)
{ {
using (await s_gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) using (await s_gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{
await SynchronizeProjectAssets_NoLockAsync(projectChecksums, cancellationToken).ConfigureAwait(false);
}
}
private async Task SynchronizeProjectAssets_NoLockAsync(IEnumerable<Checksum> projectChecksums, CancellationToken cancellationToken)
{
// get children of project checksum objects at once
await SynchronizeProjectsAsync(projectChecksums, cancellationToken).ConfigureAwait(false);
// get children of document checksum objects at once
using (var pooledObject = SharedPools.Default<HashSet<Checksum>>().GetPooledObject()) using (var pooledObject = SharedPools.Default<HashSet<Checksum>>().GetPooledObject())
{ {
var checksums = pooledObject.Object; var checksums = pooledObject.Object;
var projectChecksumObject = await _assetService.GetAssetAsync<ProjectStateChecksums>(projectChecksum, cancellationToken).ConfigureAwait(false); foreach (var projectChecksum in projectChecksums)
AddIfNeeded(checksums, projectChecksumObject.Children);
foreach (var checksum in projectChecksumObject.Documents)
{ {
var documentChecksumObject = await _assetService.GetAssetAsync<DocumentStateChecksums>(checksum, cancellationToken).ConfigureAwait(false); var projectChecksumObject = await _assetService.GetAssetAsync<ProjectStateChecksums>(projectChecksum, cancellationToken).ConfigureAwait(false);
AddIfNeeded(checksums, documentChecksumObject.Children);
}
foreach (var checksum in projectChecksumObject.AdditionalDocuments) await CollectChecksumChildrenAsync(checksums, projectChecksumObject.Documents, cancellationToken).ConfigureAwait(false);
{ await CollectChecksumChildrenAsync(checksums, projectChecksumObject.AdditionalDocuments, cancellationToken).ConfigureAwait(false);
var documentChecksumObject = await _assetService.GetAssetAsync<DocumentStateChecksums>(checksum, cancellationToken).ConfigureAwait(false);
AddIfNeeded(checksums, documentChecksumObject.Children);
} }
await _assetService.SynchronizeAssetsAsync(checksums, cancellationToken).ConfigureAwait(false); await _assetService.SynchronizeAssetsAsync(checksums, cancellationToken).ConfigureAwait(false);
} }
} }
private async Task SynchronizeSolutionAsync(SolutionStateChecksums solutionChecksumObject, CancellationToken cancellationToken) private async Task SynchronizeProjectsAsync(IEnumerable<Checksum> projectChecksums, CancellationToken cancellationToken)
{ {
// get children of solution checksum object at once // get children of project checksum objects at once
using (var pooledObject = SharedPools.Default<HashSet<Checksum>>().GetPooledObject()) using (var pooledObject = SharedPools.Default<HashSet<Checksum>>().GetPooledObject())
{ {
var solutionChecksums = pooledObject.Object; var checksums = pooledObject.Object;
AddIfNeeded(solutionChecksums, solutionChecksumObject.Children); await CollectChecksumChildrenAsync(checksums, projectChecksums, cancellationToken).ConfigureAwait(false);
await _assetService.SynchronizeAssetsAsync(solutionChecksums, cancellationToken).ConfigureAwait(false); await _assetService.SynchronizeAssetsAsync(checksums, cancellationToken).ConfigureAwait(false);
} }
} }
private async Task SynchronizeProjectsAsync(SolutionStateChecksums solutionChecksumObject, CancellationToken cancellationToken) private async Task SynchronizeAssets_NoLockAsync(IEnumerable<object> checksumOrCollections, CancellationToken cancellationToken)
{ {
// get children of project checksum objects at once // get children of solution checksum object at once
using (var pooledObject = SharedPools.Default<HashSet<Checksum>>().GetPooledObject()) using (var pooledObject = SharedPools.Default<HashSet<Checksum>>().GetPooledObject())
{ {
var projectChecksums = pooledObject.Object; var checksums = pooledObject.Object;
foreach (var projectChecksum in solutionChecksumObject.Projects)
{
var projectChecksumObject = await _assetService.GetAssetAsync<ProjectStateChecksums>(projectChecksum, cancellationToken).ConfigureAwait(false);
AddIfNeeded(projectChecksums, projectChecksumObject.Children);
}
await _assetService.SynchronizeAssetsAsync(projectChecksums, cancellationToken).ConfigureAwait(false); AddIfNeeded(checksums, checksumOrCollections);
await _assetService.SynchronizeAssetsAsync(checksums, cancellationToken).ConfigureAwait(false);
} }
} }
private async Task SynchronizeDocumentsAsync(SolutionStateChecksums solutionChecksumObject, CancellationToken cancellationToken) private async Task CollectChecksumChildrenAsync(HashSet<Checksum> set, IEnumerable<Checksum> checksums, CancellationToken cancellationToken)
{ {
// get children of document checksum objects at once foreach (var checksum in checksums)
using (var pooledObject = SharedPools.Default<HashSet<Checksum>>().GetPooledObject())
{ {
var documentChecksums = pooledObject.Object; var checksumObject = await _assetService.GetAssetAsync<ChecksumWithChildren>(checksum, cancellationToken).ConfigureAwait(false);
AddIfNeeded(set, checksumObject.Children);
foreach (var projectChecksum in solutionChecksumObject.Projects)
{
var projectChecksumObject = await _assetService.GetAssetAsync<ProjectStateChecksums>(projectChecksum, cancellationToken).ConfigureAwait(false);
foreach (var checksum in projectChecksumObject.Documents)
{
var documentChecksumObject = await _assetService.GetAssetAsync<DocumentStateChecksums>(checksum, cancellationToken).ConfigureAwait(false);
AddIfNeeded(documentChecksums, documentChecksumObject.Children);
}
foreach (var checksum in projectChecksumObject.AdditionalDocuments)
{
var documentChecksumObject = await _assetService.GetAssetAsync<DocumentStateChecksums>(checksum, cancellationToken).ConfigureAwait(false);
AddIfNeeded(documentChecksums, documentChecksumObject.Children);
}
}
await _assetService.SynchronizeAssetsAsync(documentChecksums, cancellationToken).ConfigureAwait(false);
} }
} }
private void AddIfNeeded(HashSet<Checksum> checksums, IReadOnlyList<object> checksumOrCollections) private void AddIfNeeded(HashSet<Checksum> checksums, IEnumerable<object> checksumOrCollections)
{ {
foreach (var checksumOrCollection in checksumOrCollections) foreach (var checksumOrCollection in checksumOrCollections)
{ {
...@@ -155,14 +130,6 @@ private void AddIfNeeded(HashSet<Checksum> checksums, IReadOnlyList<object> chec ...@@ -155,14 +130,6 @@ private void AddIfNeeded(HashSet<Checksum> checksums, IReadOnlyList<object> chec
} }
} }
private void AddIfNeeded(HashSet<Checksum> checksums, IEnumerable<Checksum> collection)
{
foreach (var checksum in collection)
{
AddIfNeeded(checksums, checksum);
}
}
private void AddIfNeeded(HashSet<Checksum> checksums, Checksum checksum) private void AddIfNeeded(HashSet<Checksum> checksums, Checksum checksum)
{ {
if (!_assetService.EnsureCacheEntryIfExists(checksum)) if (!_assetService.EnsureCacheEntryIfExists(checksum))
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.IO; using System.IO;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
...@@ -24,6 +25,8 @@ internal class SolutionCreator ...@@ -24,6 +25,8 @@ internal class SolutionCreator
public SolutionCreator(AssetService assetService, Solution baseSolution, CancellationToken cancellationToken) public SolutionCreator(AssetService assetService, Solution baseSolution, CancellationToken cancellationToken)
{ {
Contract.ThrowIfNull(baseSolution);
_assetService = assetService; _assetService = assetService;
_baseSolution = baseSolution; _baseSolution = baseSolution;
_cancellationToken = cancellationToken; _cancellationToken = cancellationToken;
...@@ -76,8 +79,10 @@ public async Task<Solution> CreateSolutionAsync(Checksum newSolutionChecksum) ...@@ -76,8 +79,10 @@ public async Task<Solution> CreateSolutionAsync(Checksum newSolutionChecksum)
solution = await UpdateProjectsAsync(solution, oldSolutionChecksums.Projects, newSolutionChecksums.Projects).ConfigureAwait(false); solution = await UpdateProjectsAsync(solution, oldSolutionChecksums.Projects, newSolutionChecksums.Projects).ConfigureAwait(false);
} }
#if DEBUG
// make sure created solution has same checksum as given one // make sure created solution has same checksum as given one
Contract.ThrowIfFalse(newSolutionChecksum == await solution.State.GetChecksumAsync(_cancellationToken).ConfigureAwait(false)); Contract.Requires(newSolutionChecksum == await solution.State.GetChecksumAsync(_cancellationToken).ConfigureAwait(false));
#endif
return solution; return solution;
} }
...@@ -103,14 +108,14 @@ private async Task<Solution> UpdateProjectsAsync(Solution solution, HashSet<Chec ...@@ -103,14 +108,14 @@ private async Task<Solution> UpdateProjectsAsync(Solution solution, HashSet<Chec
var oldMap = await GetProjectMapAsync(solution, oldChecksums).ConfigureAwait(false); var oldMap = await GetProjectMapAsync(solution, oldChecksums).ConfigureAwait(false);
var newMap = await GetProjectMapAsync(_assetService, newChecksums).ConfigureAwait(false); var newMap = await GetProjectMapAsync(_assetService, newChecksums).ConfigureAwait(false);
// bulk sync assets
await SynchronizeAssetsAsync(solution, oldMap, newMap).ConfigureAwait(false);
// added project // added project
foreach (var kv in newMap) foreach (var kv in newMap)
{ {
if (!oldMap.ContainsKey(kv.Key)) if (!oldMap.ContainsKey(kv.Key))
{ {
// bulk sync project assets
await _assetService.SynchronizeProjectAssetsAsync(kv.Value.Checksum, _cancellationToken).ConfigureAwait(false);
var projectInfo = await CreateProjectInfoAsync(kv.Value.Checksum).ConfigureAwait(false); var projectInfo = await CreateProjectInfoAsync(kv.Value.Checksum).ConfigureAwait(false);
if (projectInfo == null) if (projectInfo == null)
{ {
...@@ -151,6 +156,25 @@ private async Task<Solution> UpdateProjectsAsync(Solution solution, HashSet<Chec ...@@ -151,6 +156,25 @@ private async Task<Solution> UpdateProjectsAsync(Solution solution, HashSet<Chec
return solution; return solution;
} }
private async Task SynchronizeAssetsAsync(Solution solution, Dictionary<ProjectId, ProjectStateChecksums> oldMap, Dictionary<ProjectId, ProjectStateChecksums> newMap)
{
using (var pooledObject = SharedPools.Default<HashSet<Checksum>>().GetPooledObject())
{
// added project
foreach (var kv in newMap)
{
if (oldMap.ContainsKey(kv.Key))
{
continue;
}
pooledObject.Object.Add(kv.Value.Checksum);
}
await _assetService.SynchronizeProjectAssetsAsync(pooledObject.Object, _cancellationToken).ConfigureAwait(false);
}
}
private async Task<Solution> UpdateProjectAsync(Project project, ProjectStateChecksums oldProjectChecksums, ProjectStateChecksums newProjectChecksums) private async Task<Solution> UpdateProjectAsync(Project project, ProjectStateChecksums oldProjectChecksums, ProjectStateChecksums newProjectChecksums)
{ {
// changed info // changed info
...@@ -266,7 +290,7 @@ private async Task<Project> UpdateDocumentsAsync(Project project, HashSet<Checks ...@@ -266,7 +290,7 @@ private async Task<Project> UpdateDocumentsAsync(Project project, HashSet<Checks
var oldMap = await GetDocumentMapAsync(project, oldChecksums, additionalText).ConfigureAwait(false); var oldMap = await GetDocumentMapAsync(project, oldChecksums, additionalText).ConfigureAwait(false);
var newMap = await GetDocumentMapAsync(_assetService, newChecksums).ConfigureAwait(false); var newMap = await GetDocumentMapAsync(_assetService, newChecksums).ConfigureAwait(false);
// added project // added document
foreach (var kv in newMap) foreach (var kv in newMap)
{ {
if (!oldMap.ContainsKey(kv.Key)) if (!oldMap.ContainsKey(kv.Key))
...@@ -276,7 +300,7 @@ private async Task<Project> UpdateDocumentsAsync(Project project, HashSet<Checks ...@@ -276,7 +300,7 @@ private async Task<Project> UpdateDocumentsAsync(Project project, HashSet<Checks
} }
} }
// changed project // changed document
foreach (var kv in newMap) foreach (var kv in newMap)
{ {
DocumentStateChecksums oldDocumentChecksums; DocumentStateChecksums oldDocumentChecksums;
...@@ -292,7 +316,7 @@ private async Task<Project> UpdateDocumentsAsync(Project project, HashSet<Checks ...@@ -292,7 +316,7 @@ private async Task<Project> UpdateDocumentsAsync(Project project, HashSet<Checks
project = await UpdateDocumentAsync(document, oldDocumentChecksums, newDocumentChecksums, additionalText).ConfigureAwait(false); project = await UpdateDocumentAsync(document, oldDocumentChecksums, newDocumentChecksums, additionalText).ConfigureAwait(false);
} }
// removed project // removed document
foreach (var kv in oldMap) foreach (var kv in oldMap)
{ {
if (!newMap.ContainsKey(kv.Key)) if (!newMap.ContainsKey(kv.Key))
...@@ -370,6 +394,8 @@ private async Task<TextDocument> UpdateDocumentInfoAsync(TextDocument document, ...@@ -370,6 +394,8 @@ private async Task<TextDocument> UpdateDocumentInfoAsync(TextDocument document,
var map = new Dictionary<DocumentId, DocumentStateChecksums>(); var map = new Dictionary<DocumentId, DocumentStateChecksums>();
var documentChecksums = await assetService.GetAssetsAsync<DocumentStateChecksums>(documents, _cancellationToken).ConfigureAwait(false); var documentChecksums = await assetService.GetAssetsAsync<DocumentStateChecksums>(documents, _cancellationToken).ConfigureAwait(false);
var infos = await assetService.GetAssetsAsync<DocumentInfo.DocumentAttributes>(documentChecksums.Select(p => p.Item2.Info), _cancellationToken).ConfigureAwait(false);
foreach (var kv in documentChecksums) foreach (var kv in documentChecksums)
{ {
var info = await assetService.GetAssetAsync<DocumentInfo.DocumentAttributes>(kv.Item2.Info, _cancellationToken).ConfigureAwait(false); var info = await assetService.GetAssetAsync<DocumentInfo.DocumentAttributes>(kv.Item2.Info, _cancellationToken).ConfigureAwait(false);
...@@ -411,6 +437,8 @@ private async Task<TextDocument> UpdateDocumentInfoAsync(TextDocument document, ...@@ -411,6 +437,8 @@ private async Task<TextDocument> UpdateDocumentInfoAsync(TextDocument document,
var map = new Dictionary<ProjectId, ProjectStateChecksums>(); var map = new Dictionary<ProjectId, ProjectStateChecksums>();
var projectChecksums = await assetService.GetAssetsAsync<ProjectStateChecksums>(projects, _cancellationToken).ConfigureAwait(false); var projectChecksums = await assetService.GetAssetsAsync<ProjectStateChecksums>(projects, _cancellationToken).ConfigureAwait(false);
var infos = await assetService.GetAssetsAsync<ProjectInfo.ProjectAttributes>(projectChecksums.Select(p => p.Item2.Info), _cancellationToken).ConfigureAwait(false);
foreach (var kv in projectChecksums) foreach (var kv in projectChecksums)
{ {
var info = await assetService.GetAssetAsync<ProjectInfo.ProjectAttributes>(kv.Item2.Info, _cancellationToken).ConfigureAwait(false); var info = await assetService.GetAssetAsync<ProjectInfo.ProjectAttributes>(kv.Item2.Info, _cancellationToken).ConfigureAwait(false);
......
...@@ -20,8 +20,8 @@ internal class SolutionService ...@@ -20,8 +20,8 @@ internal class SolutionService
private readonly AssetService _assetService; private readonly AssetService _assetService;
// TODO: make this simple cache better // this simple cache hold onto the last and primary solution created
// this simple cache hold onto the last solution created private volatile static Tuple<Checksum, Solution> s_primarySolution;
private volatile static Tuple<Checksum, Solution> s_lastSolution; private volatile static Tuple<Checksum, Solution> s_lastSolution;
public SolutionService(AssetService assetService) public SolutionService(AssetService assetService)
...@@ -31,30 +31,22 @@ public SolutionService(AssetService assetService) ...@@ -31,30 +31,22 @@ public SolutionService(AssetService assetService)
public async Task<Solution> GetSolutionAsync(Checksum solutionChecksum, CancellationToken cancellationToken) public async Task<Solution> GetSolutionAsync(Checksum solutionChecksum, CancellationToken cancellationToken)
{ {
var currentSolution = s_primaryWorkspace.CurrentSolution; var currentSolution = GetAvailableSolution(solutionChecksum);
if (currentSolution != null)
var primarySolutionChecksum = await currentSolution.State.GetChecksumAsync(cancellationToken).ConfigureAwait(false);
if (primarySolutionChecksum == solutionChecksum)
{ {
// nothing changed
return currentSolution; return currentSolution;
} }
var lastSolution = s_lastSolution;
if (lastSolution?.Item1 == solutionChecksum)
{
return lastSolution.Item2;
}
// make sure there is always only one that creates a new solution // make sure there is always only one that creates a new solution
using (await s_gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) using (await s_gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{ {
if (s_lastSolution?.Item1 == solutionChecksum) currentSolution = GetAvailableSolution(solutionChecksum);
if (currentSolution != null)
{ {
return s_lastSolution.Item2; return currentSolution;
} }
var solution = await CreateSolution_NoLockAsync(solutionChecksum, currentSolution, cancellationToken).ConfigureAwait(false); var solution = await CreateSolution_NoLockAsync(solutionChecksum, s_primaryWorkspace.CurrentSolution, cancellationToken).ConfigureAwait(false);
s_lastSolution = Tuple.Create(solutionChecksum, solution); s_lastSolution = Tuple.Create(solutionChecksum, solution);
return solution; return solution;
...@@ -63,14 +55,13 @@ public async Task<Solution> GetSolutionAsync(Checksum solutionChecksum, Cancella ...@@ -63,14 +55,13 @@ public async Task<Solution> GetSolutionAsync(Checksum solutionChecksum, Cancella
public async Task<Solution> GetSolutionAsync(Checksum solutionChecksum, OptionSet optionSet, CancellationToken cancellationToken) public async Task<Solution> GetSolutionAsync(Checksum solutionChecksum, OptionSet optionSet, CancellationToken cancellationToken)
{ {
// get solution
var baseSolution = await GetSolutionAsync(solutionChecksum, cancellationToken).ConfigureAwait(false);
if (optionSet == null) if (optionSet == null)
{ {
return await GetSolutionAsync(solutionChecksum, cancellationToken).ConfigureAwait(false); return baseSolution;
} }
// get solution
var baseSolution = await GetSolutionAsync(solutionChecksum, cancellationToken).ConfigureAwait(false);
// since options belong to workspace, we can't share solution // since options belong to workspace, we can't share solution
// create temporary workspace // create temporary workspace
var tempWorkspace = new TemporaryWorkspace(baseSolution); var tempWorkspace = new TemporaryWorkspace(baseSolution);
...@@ -95,20 +86,8 @@ public async Task UpdatePrimaryWorkspaceAsync(Checksum solutionChecksum, Cancell ...@@ -95,20 +86,8 @@ public async Task UpdatePrimaryWorkspaceAsync(Checksum solutionChecksum, Cancell
using (await s_gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) using (await s_gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{ {
var updater = new SolutionCreator(_assetService, currentSolution, cancellationToken); var solution = await UpdatePrimaryWorkspace_NoLockAsync(solutionChecksum, currentSolution, cancellationToken).ConfigureAwait(false);
s_primarySolution = Tuple.Create(solutionChecksum, solution);
if (await updater.IsIncrementalUpdateAsync(solutionChecksum).ConfigureAwait(false))
{
// solution has updated
s_primaryWorkspace.UpdateSolution(await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false));
return;
}
// new solution. bulk sync all asset for the solution
await _assetService.SynchronizeSolutionAssetsAsync(solutionChecksum, cancellationToken).ConfigureAwait(false);
s_primaryWorkspace.ClearSolution();
s_primaryWorkspace.AddSolution(await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false));
} }
} }
...@@ -139,5 +118,45 @@ private async Task<Solution> CreateSolution_NoLockAsync(Checksum solutionChecksu ...@@ -139,5 +118,45 @@ private async Task<Solution> CreateSolution_NoLockAsync(Checksum solutionChecksu
var workspace = new TemporaryWorkspace(await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false)); var workspace = new TemporaryWorkspace(await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false));
return workspace.CurrentSolution; return workspace.CurrentSolution;
} }
private async Task<Solution> UpdatePrimaryWorkspace_NoLockAsync(Checksum solutionChecksum, Solution baseSolution, CancellationToken cancellationToken)
{
var updater = new SolutionCreator(_assetService, baseSolution, cancellationToken);
if (await updater.IsIncrementalUpdateAsync(solutionChecksum).ConfigureAwait(false))
{
// solution has updated
s_primaryWorkspace.UpdateSolution(await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false));
return s_primaryWorkspace.CurrentSolution;
}
// new solution. bulk sync all asset for the solution
await _assetService.SynchronizeSolutionAssetsAsync(solutionChecksum, cancellationToken).ConfigureAwait(false);
s_primaryWorkspace.ClearSolution();
s_primaryWorkspace.AddSolution(await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false));
return s_primaryWorkspace.CurrentSolution;
}
private static Solution GetAvailableSolution(Checksum solutionChecksum)
{
var currentSolution = s_primarySolution;
if (currentSolution?.Item1 == solutionChecksum)
{
// asked about primary solution
return currentSolution.Item2;
}
var lastSolution = s_lastSolution;
if (lastSolution?.Item1 == solutionChecksum)
{
// asked about last solution
return lastSolution.Item2;
}
return null;
}
} }
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册