From 7b022072e3b89404a1d4744acf40254288bdcc75 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 8 Apr 2024 14:35:06 -0700 Subject: [PATCH 01/11] avoid more arrays --- .../Remote/Core/AbstractAssetProvider.cs | 18 +++++-- .../Remote/ServiceHub/Host/AssetProvider.cs | 54 ++++++++----------- .../Host/RemoteWorkspace.SolutionCreator.cs | 30 +++++------ 3 files changed, 52 insertions(+), 50 deletions(-) diff --git a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs index 5feb89710575b..54319427bd5f2 100644 --- a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs +++ b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs @@ -2,8 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; @@ -22,7 +24,7 @@ internal abstract class AbstractAssetProvider /// return data of type T whose checksum is the given checksum /// public abstract ValueTask GetAssetAsync(AssetPath assetPath, Checksum checksum, CancellationToken cancellationToken); - public abstract ValueTask> GetAssetsAsync(AssetPath assetPath, HashSet checksums, CancellationToken cancellationToken); + public abstract ValueTask GetAssetsAsync(AssetPath assetPath, HashSet checksums, Action callback, CancellationToken cancellationToken); public async Task CreateSolutionInfoAsync(Checksum solutionChecksum, CancellationToken cancellationToken) { @@ -110,7 +112,17 @@ public async Task> GetAssetsAsync( using var _ = PooledHashSet.GetInstance(out var checksumSet); checksumSet.AddAll(checksums.Children); - var results = await this.GetAssetsAsync(assetPath, checksumSet, cancellationToken).ConfigureAwait(false); - return results.SelectAsArray(static t => t.asset); + var results = new T[checksumSet.Count]; + var index = 0; + + await this.GetAssetsAsync(assetPath, checksumSet, (_, asset) => + { + results[index] = asset; + index++; + }, + cancellationToken).ConfigureAwait(false); + Contract.ThrowIfTrue(index != checksumSet.Count); + + return ImmutableCollectionsMarshal.AsImmutableArray(results); } } diff --git a/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs b/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs index 86e4f47afd5f9..bd6d4026a6cef 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs @@ -39,28 +39,17 @@ public override async ValueTask GetAssetAsync( using var _1 = PooledHashSet.GetInstance(out var checksums); checksums.Add(checksum); - using var _2 = PooledDictionary.GetInstance(out var results); - await this.SynchronizeAssetsAsync(assetPath, checksums, results, cancellationToken).ConfigureAwait(false); + T? result = default; + await this.SynchronizeAssetsAsync(assetPath, checksums, (checksum, asset) => result = asset, cancellationToken).ConfigureAwait(false); + Contract.ThrowIfNull((object?)result); - return results[checksum]; + return result; } - public override async ValueTask> GetAssetsAsync( - AssetPath assetPath, HashSet checksums, CancellationToken cancellationToken) + public override async ValueTask GetAssetsAsync( + AssetPath assetPath, HashSet checksums, Action callback, CancellationToken cancellationToken) { - using var _ = PooledDictionary.GetInstance(out var results); - - await this.SynchronizeAssetsAsync(assetPath, checksums, results, cancellationToken).ConfigureAwait(false); - - var result = new (Checksum checksum, T asset)[checksums.Count]; - var index = 0; - foreach (var (checksum, assetObject) in results) - { - result[index] = (checksum, assetObject); - index++; - } - - return ImmutableCollectionsMarshal.AsImmutableArray(result); + await this.SynchronizeAssetsAsync(assetPath, checksums, callback, cancellationToken).ConfigureAwait(false); } public async ValueTask SynchronizeSolutionAssetsAsync(Checksum solutionChecksum, CancellationToken cancellationToken) @@ -88,17 +77,15 @@ public async ValueTask SynchronizeSolutionAssetsAsync(Checksum solutionChecksum, async ValueTask SynchronizeSolutionAssetsWorkerAsync() { - using var _1 = PooledDictionary.GetInstance(out var checksumToObjects); - // first, get top level solution state for the given solution checksum var compilationStateChecksums = await this.GetAssetAsync( assetPath: AssetPath.SolutionOnly, solutionChecksum, cancellationToken).ConfigureAwait(false); - using var _2 = PooledHashSet.GetInstance(out var checksums); + using var _1 = PooledHashSet.GetInstance(out var checksums); // second, get direct children of the solution compilation state. compilationStateChecksums.AddAllTo(checksums); - await this.SynchronizeAssetsAsync(assetPath: AssetPath.SolutionOnly, checksums, results: null, cancellationToken).ConfigureAwait(false); + await this.SynchronizeAssetsAsync(assetPath: AssetPath.SolutionOnly, checksums, callback: null, cancellationToken).ConfigureAwait(false); // third, get direct children of the solution state. var stateChecksums = await this.GetAssetAsync( @@ -108,7 +95,11 @@ async ValueTask SynchronizeSolutionAssetsWorkerAsync() // the project states and we want to get that all in one batch. checksums.Clear(); stateChecksums.AddAllTo(checksums); - await this.SynchronizeAssetsAsync(assetPath: AssetPath.SolutionAndTopLevelProjectsOnly, checksums, checksumToObjects, cancellationToken).ConfigureAwait(false); + + using var _2 = PooledDictionary.GetInstance(out var checksumToObjects); + + await this.SynchronizeAssetsAsync( + assetPath: AssetPath.SolutionAndTopLevelProjectsOnly, checksums, checksumToObjects.Add, cancellationToken).ConfigureAwait(false); // fourth, get all projects and documents in the solution foreach (var (projectChecksum, _) in stateChecksums.Projects) @@ -152,7 +143,7 @@ async ValueTask SynchronizeProjectAssetsWorkerAsync() // First synchronize all the top-level info about this project. await this.SynchronizeAssetsAsync( - assetPath: AssetPath.ProjectAndDocuments(projectChecksums.ProjectId), checksums, results: null, cancellationToken).ConfigureAwait(false); + assetPath: AssetPath.ProjectAndDocuments(projectChecksums.ProjectId), checksums, callback: null, cancellationToken).ConfigureAwait(false); checksums.Clear(); @@ -162,7 +153,7 @@ await this.SynchronizeAssetsAsync( await CollectChecksumChildrenAsync(checksums, projectChecksums.AnalyzerConfigDocuments).ConfigureAwait(false); await this.SynchronizeAssetsAsync( - assetPath: AssetPath.ProjectAndDocuments(projectChecksums.ProjectId), checksums, results: null, cancellationToken).ConfigureAwait(false); + assetPath: AssetPath.ProjectAndDocuments(projectChecksums.ProjectId), checksums, callback: null, cancellationToken).ConfigureAwait(false); } async ValueTask CollectChecksumChildrenAsync(HashSet checksums, ChecksumsAndIds collection) @@ -186,7 +177,7 @@ static void AddAll(HashSet checksums, ChecksumCollection checksumColle } public async ValueTask SynchronizeAssetsAsync( - AssetPath assetPath, HashSet checksums, Dictionary? results, CancellationToken cancellationToken) + AssetPath assetPath, HashSet checksums, Action? callback, CancellationToken cancellationToken) { Contract.ThrowIfTrue(checksums.Contains(Checksum.Null)); if (checksums.Count == 0) @@ -239,18 +230,20 @@ public async ValueTask SynchronizeAssetsAsync( { var missingChecksumsMemory = new ReadOnlyMemory(missingChecksums, 0, missingChecksumsCount); - var lastIndexNotification = -1; + var indexExpectation = 0; await RequestAssetsAsync(assetPath, missingChecksumsMemory, (int index, T missingAsset) => { - lastIndexNotification = index; + Contract.ThrowIfTrue(indexExpectation != index); var missingChecksum = missingChecksums[index]; AddResult(missingChecksum, missingAsset); _assetCache.GetOrAdd(missingChecksum, missingAsset!); + + indexExpectation++; }, cancellationToken).ConfigureAwait(false); - Contract.ThrowIfTrue(lastIndexNotification != missingChecksumsCount - 1); + Contract.ThrowIfTrue(indexExpectation != missingChecksumsCount); } if (usePool) @@ -261,8 +254,7 @@ await RequestAssetsAsync(assetPath, missingChecksumsMemory, (int index, T missin void AddResult(Checksum checksum, T result) { - if (results != null) - results[checksum] = result; + callback?.Invoke(checksum, result); } } diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs index e8fadaeeeab01..ec53e3cedce4f 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs @@ -218,14 +218,13 @@ private async Task UpdateProjectsAsync( using var _5 = PooledHashSet.GetInstance(out var newChecksumsToSync); newChecksumsToSync.AddRange(newProjectIdToChecksum.Values); - var newProjectStateChecksums = await _assetProvider.GetAssetsAsync( - assetPath: AssetPath.SolutionAndTopLevelProjectsOnly, newChecksumsToSync, cancellationToken).ConfigureAwait(false); - - foreach (var (checksum, newProjectStateChecksum) in newProjectStateChecksums) - { - Contract.ThrowIfTrue(checksum != newProjectStateChecksum.Checksum); - newProjectIdToStateChecksums.Add(newProjectStateChecksum.ProjectId, newProjectStateChecksum); - } + await _assetProvider.GetAssetsAsync( + assetPath: AssetPath.SolutionAndTopLevelProjectsOnly, newChecksumsToSync, (checksum, newProjectStateChecksum) => + { + Contract.ThrowIfTrue(checksum != newProjectStateChecksum.Checksum); + newProjectIdToStateChecksums.Add(newProjectStateChecksum.ProjectId, newProjectStateChecksum); + }, + cancellationToken).ConfigureAwait(false); // Now that we've collected the old and new project state checksums, we can actually process them to // determine what to remove, what to add, and what to change. @@ -503,14 +502,13 @@ private async Task UpdateDocumentsAsync( using var _5 = PooledHashSet.GetInstance(out var newChecksumsToSync); newChecksumsToSync.AddRange(newDocumentIdToChecksum.Values); - var documentStateChecksums = await _assetProvider.GetAssetsAsync( - assetPath: AssetPath.ProjectAndDocuments(project.Id), newChecksumsToSync, cancellationToken).ConfigureAwait(false); - - foreach (var (checksum, documentStateChecksum) in documentStateChecksums) - { - Contract.ThrowIfTrue(checksum != documentStateChecksum.Checksum); - newDocumentIdToStateChecksums.Add(documentStateChecksum.DocumentId, documentStateChecksum); - } + await _assetProvider.GetAssetsAsync( + assetPath: AssetPath.ProjectAndDocuments(project.Id), newChecksumsToSync, (checksum, documentStateChecksum) => + { + Contract.ThrowIfTrue(checksum != documentStateChecksum.Checksum); + newDocumentIdToStateChecksums.Add(documentStateChecksum.DocumentId, documentStateChecksum); + }, + cancellationToken).ConfigureAwait(false); // If more than two documents changed during a single update, perform a bulk synchronization on the // project to avoid large numbers of small synchronization calls during document updates. From 8573ebd00fd004d734b80120194db2176219caa2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 8 Apr 2024 14:47:29 -0700 Subject: [PATCH 02/11] Avoid arrays in more cases --- .../Remote/Core/AbstractAssetProvider.cs | 15 +++---- .../Core/RemoteHostAssetSerialization.cs | 19 ++++----- .../Remote/ServiceHub/Host/AssetProvider.cs | 40 +++++++++++-------- .../Remote/ServiceHub/Host/IAssetSource.cs | 6 ++- .../Host/RemoteWorkspace.SolutionCreator.cs | 12 ++++-- 5 files changed, 50 insertions(+), 42 deletions(-) diff --git a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs index 54319427bd5f2..12eaa0855009a 100644 --- a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs +++ b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs @@ -24,7 +24,7 @@ internal abstract class AbstractAssetProvider /// return data of type T whose checksum is the given checksum /// public abstract ValueTask GetAssetAsync(AssetPath assetPath, Checksum checksum, CancellationToken cancellationToken); - public abstract ValueTask GetAssetsAsync(AssetPath assetPath, HashSet checksums, Action callback, CancellationToken cancellationToken); + public abstract ValueTask GetAssetsAsync(AssetPath assetPath, HashSet checksums, Action callback, TArg arg, CancellationToken cancellationToken); public async Task CreateSolutionInfoAsync(Checksum solutionChecksum, CancellationToken cancellationToken) { @@ -113,15 +113,12 @@ public async Task> GetAssetsAsync( checksumSet.AddAll(checksums.Children); var results = new T[checksumSet.Count]; - var index = 0; - await this.GetAssetsAsync(assetPath, checksumSet, (_, asset) => - { - results[index] = asset; - index++; - }, - cancellationToken).ConfigureAwait(false); - Contract.ThrowIfTrue(index != checksumSet.Count); + await this.GetAssetsAsync( + assetPath, checksumSet, + static (checksum, index, asset, results) => results[index] = asset, + results, + cancellationToken).ConfigureAwait(false); return ImmutableCollectionsMarshal.AsImmutableArray(results); } diff --git a/src/Workspaces/Remote/Core/RemoteHostAssetSerialization.cs b/src/Workspaces/Remote/Core/RemoteHostAssetSerialization.cs index 0cd163a39781d..ba87b9308a31a 100644 --- a/src/Workspaces/Remote/Core/RemoteHostAssetSerialization.cs +++ b/src/Workspaces/Remote/Core/RemoteHostAssetSerialization.cs @@ -4,14 +4,11 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.IO.Pipelines; -using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Serialization; using Nerdbank.Streams; using Roslyn.Utilities; @@ -67,8 +64,8 @@ static void WriteAsset(ObjectWriter writer, ISerializerService serializer, Solut } } - public static ValueTask ReadDataAsync( - PipeReader pipeReader, Checksum solutionChecksum, int objectCount, ISerializerService serializerService, Action callback, CancellationToken cancellationToken) + public static ValueTask ReadDataAsync( + PipeReader pipeReader, Checksum solutionChecksum, int objectCount, ISerializerService serializerService, Action callback, TArg arg, CancellationToken cancellationToken) { // Suppress ExecutionContext flow for asynchronous operations operate on the pipe. In addition to avoiding // ExecutionContext allocations, this clears the LogicalCallContext and avoids the need to clone data set by @@ -77,18 +74,18 @@ public static ValueTask ReadDataAsync( // ⚠ DO NOT AWAIT INSIDE THE USING. The Dispose method that restores ExecutionContext flow must run on the // same thread where SuppressFlow was originally run. using var _ = FlowControlHelper.TrySuppressFlow(); - return ReadDataSuppressedFlowAsync(pipeReader, solutionChecksum, objectCount, serializerService, callback, cancellationToken); + return ReadDataSuppressedFlowAsync(pipeReader, solutionChecksum, objectCount, serializerService, callback, arg, cancellationToken); static async ValueTask ReadDataSuppressedFlowAsync( - PipeReader pipeReader, Checksum solutionChecksum, int objectCount, ISerializerService serializerService, Action callback, CancellationToken cancellationToken) + PipeReader pipeReader, Checksum solutionChecksum, int objectCount, ISerializerService serializerService, Action callback, TArg arg, CancellationToken cancellationToken) { using var stream = await pipeReader.AsPrebufferedStreamAsync(cancellationToken).ConfigureAwait(false); - ReadData(stream, solutionChecksum, objectCount, serializerService, callback, cancellationToken); + ReadData(stream, solutionChecksum, objectCount, serializerService, callback, arg, cancellationToken); } } - public static void ReadData( - Stream stream, Checksum solutionChecksum, int objectCount, ISerializerService serializerService, Action callback, CancellationToken cancellationToken) + public static void ReadData( + Stream stream, Checksum solutionChecksum, int objectCount, ISerializerService serializerService, Action callback, TArg arg, CancellationToken cancellationToken) { using var reader = ObjectReader.GetReader(stream, leaveOpen: true, cancellationToken); @@ -104,7 +101,7 @@ public static void ReadData( // in service hub, cancellation means simply closed stream var result = serializerService.Deserialize(kind, reader, cancellationToken); Contract.ThrowIfNull(result); - callback(i, (T)result); + callback((T)result, arg); } } } diff --git a/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs b/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs index bd6d4026a6cef..99fc3ecc3d1e8 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs @@ -39,17 +39,23 @@ public override async ValueTask GetAssetAsync( using var _1 = PooledHashSet.GetInstance(out var checksums); checksums.Add(checksum); + var called = false; T? result = default; - await this.SynchronizeAssetsAsync(assetPath, checksums, (checksum, asset) => result = asset, cancellationToken).ConfigureAwait(false); + await this.SynchronizeAssetsAsync(assetPath, checksums, (_, _, asset, _) => + { + Contract.ThrowIfTrue(called); + called = true; + result = asset; + }, default, cancellationToken).ConfigureAwait(false); Contract.ThrowIfNull((object?)result); return result; } - public override async ValueTask GetAssetsAsync( - AssetPath assetPath, HashSet checksums, Action callback, CancellationToken cancellationToken) + public override async ValueTask GetAssetsAsync( + AssetPath assetPath, HashSet checksums, Action callback, TArg arg, CancellationToken cancellationToken) { - await this.SynchronizeAssetsAsync(assetPath, checksums, callback, cancellationToken).ConfigureAwait(false); + await this.SynchronizeAssetsAsync(assetPath, checksums, callback, arg, cancellationToken).ConfigureAwait(false); } public async ValueTask SynchronizeSolutionAssetsAsync(Checksum solutionChecksum, CancellationToken cancellationToken) @@ -85,7 +91,7 @@ async ValueTask SynchronizeSolutionAssetsWorkerAsync() // second, get direct children of the solution compilation state. compilationStateChecksums.AddAllTo(checksums); - await this.SynchronizeAssetsAsync(assetPath: AssetPath.SolutionOnly, checksums, callback: null, cancellationToken).ConfigureAwait(false); + await this.SynchronizeAssetsAsync(assetPath: AssetPath.SolutionOnly, checksums, callback: null, arg: default, cancellationToken).ConfigureAwait(false); // third, get direct children of the solution state. var stateChecksums = await this.GetAssetAsync( @@ -98,8 +104,10 @@ async ValueTask SynchronizeSolutionAssetsWorkerAsync() using var _2 = PooledDictionary.GetInstance(out var checksumToObjects); - await this.SynchronizeAssetsAsync( - assetPath: AssetPath.SolutionAndTopLevelProjectsOnly, checksums, checksumToObjects.Add, cancellationToken).ConfigureAwait(false); + await this.SynchronizeAssetsAsync>( + assetPath: AssetPath.SolutionAndTopLevelProjectsOnly, checksums, + static (checksum, index, asset, checksumToObjects) => checksumToObjects.Add(checksum, asset), + arg: checksumToObjects, cancellationToken).ConfigureAwait(false); // fourth, get all projects and documents in the solution foreach (var (projectChecksum, _) in stateChecksums.Projects) @@ -142,8 +150,8 @@ async ValueTask SynchronizeProjectAssetsWorkerAsync() AddAll(checksums, projectChecksums.AnalyzerConfigDocuments.Checksums); // First synchronize all the top-level info about this project. - await this.SynchronizeAssetsAsync( - assetPath: AssetPath.ProjectAndDocuments(projectChecksums.ProjectId), checksums, callback: null, cancellationToken).ConfigureAwait(false); + await this.SynchronizeAssetsAsync( + assetPath: AssetPath.ProjectAndDocuments(projectChecksums.ProjectId), checksums, callback: null, arg: default, cancellationToken).ConfigureAwait(false); checksums.Clear(); @@ -152,8 +160,8 @@ await this.SynchronizeAssetsAsync( await CollectChecksumChildrenAsync(checksums, projectChecksums.AdditionalDocuments).ConfigureAwait(false); await CollectChecksumChildrenAsync(checksums, projectChecksums.AnalyzerConfigDocuments).ConfigureAwait(false); - await this.SynchronizeAssetsAsync( - assetPath: AssetPath.ProjectAndDocuments(projectChecksums.ProjectId), checksums, callback: null, cancellationToken).ConfigureAwait(false); + await this.SynchronizeAssetsAsync( + assetPath: AssetPath.ProjectAndDocuments(projectChecksums.ProjectId), checksums, callback: null, arg: default, cancellationToken).ConfigureAwait(false); } async ValueTask CollectChecksumChildrenAsync(HashSet checksums, ChecksumsAndIds collection) @@ -176,8 +184,8 @@ static void AddAll(HashSet checksums, ChecksumCollection checksumColle } } - public async ValueTask SynchronizeAssetsAsync( - AssetPath assetPath, HashSet checksums, Action? callback, CancellationToken cancellationToken) + public async ValueTask SynchronizeAssetsAsync( + AssetPath assetPath, HashSet checksums, Action? callback, TArg? arg, CancellationToken cancellationToken) { Contract.ThrowIfTrue(checksums.Contains(Checksum.Null)); if (checksums.Count == 0) @@ -231,7 +239,7 @@ public async ValueTask SynchronizeAssetsAsync( var missingChecksumsMemory = new ReadOnlyMemory(missingChecksums, 0, missingChecksumsCount); var indexExpectation = 0; - await RequestAssetsAsync(assetPath, missingChecksumsMemory, (int index, T missingAsset) => + await RequestAssetsAsync(assetPath, missingChecksumsMemory, static (int index, T missingAsset) => { Contract.ThrowIfTrue(indexExpectation != index); @@ -258,8 +266,8 @@ void AddResult(Checksum checksum, T result) } } - private async ValueTask RequestAssetsAsync( - AssetPath assetPath, ReadOnlyMemory checksums, Action callback, CancellationToken cancellationToken) + private async ValueTask RequestAssetsAsync( + AssetPath assetPath, ReadOnlyMemory checksums, Action callback, CancellationToken cancellationToken) { #if NETCOREAPP Contract.ThrowIfTrue(checksums.Span.Contains(Checksum.Null)); diff --git a/src/Workspaces/Remote/ServiceHub/Host/IAssetSource.cs b/src/Workspaces/Remote/ServiceHub/Host/IAssetSource.cs index d8cb321c59da7..30d3fe84dae88 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/IAssetSource.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/IAssetSource.cs @@ -14,11 +14,13 @@ namespace Microsoft.CodeAnalysis.Remote; /// internal interface IAssetSource { - ValueTask GetAssetsAsync( + /// Will be called back once per checksum in in the exact order of that array. + ValueTask GetAssetsAsync( Checksum solutionChecksum, AssetPath assetPath, ReadOnlyMemory checksums, ISerializerService serializerService, - Action callback, + Action callback, + TArg arg, CancellationToken cancellationToken); } diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs index ec53e3cedce4f..fb0ecc76ed3a9 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs @@ -218,12 +218,14 @@ private async Task UpdateProjectsAsync( using var _5 = PooledHashSet.GetInstance(out var newChecksumsToSync); newChecksumsToSync.AddRange(newProjectIdToChecksum.Values); - await _assetProvider.GetAssetsAsync( - assetPath: AssetPath.SolutionAndTopLevelProjectsOnly, newChecksumsToSync, (checksum, newProjectStateChecksum) => + await _assetProvider.GetAssetsAsync>( + assetPath: AssetPath.SolutionAndTopLevelProjectsOnly, newChecksumsToSync, + static (checksum, _, newProjectStateChecksum, newProjectIdToStateChecksums) => { Contract.ThrowIfTrue(checksum != newProjectStateChecksum.Checksum); newProjectIdToStateChecksums.Add(newProjectStateChecksum.ProjectId, newProjectStateChecksum); }, + arg: newProjectIdToStateChecksums, cancellationToken).ConfigureAwait(false); // Now that we've collected the old and new project state checksums, we can actually process them to @@ -502,12 +504,14 @@ private async Task UpdateDocumentsAsync( using var _5 = PooledHashSet.GetInstance(out var newChecksumsToSync); newChecksumsToSync.AddRange(newDocumentIdToChecksum.Values); - await _assetProvider.GetAssetsAsync( - assetPath: AssetPath.ProjectAndDocuments(project.Id), newChecksumsToSync, (checksum, documentStateChecksum) => + await _assetProvider.GetAssetsAsync>( + assetPath: AssetPath.ProjectAndDocuments(project.Id), newChecksumsToSync, + static (checksum, _, documentStateChecksum, newDocumentIdToStateChecksums) => { Contract.ThrowIfTrue(checksum != documentStateChecksum.Checksum); newDocumentIdToStateChecksums.Add(documentStateChecksum.DocumentId, documentStateChecksum); }, + arg: newDocumentIdToStateChecksums, cancellationToken).ConfigureAwait(false); // If more than two documents changed during a single update, perform a bulk synchronization on the From f5fadb201a56b9322315b92ff3c83730aaa8f37f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 8 Apr 2024 14:52:50 -0700 Subject: [PATCH 03/11] Not working --- .../Remote/Core/AbstractAssetProvider.cs | 6 +++--- .../Remote/ServiceHub/Host/AssetProvider.cs | 14 +++++++------- .../Remote/ServiceHub/Host/SolutionAssetSource.cs | 7 ++++--- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs index 12eaa0855009a..012958caed5b6 100644 --- a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs +++ b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs @@ -24,7 +24,7 @@ internal abstract class AbstractAssetProvider /// return data of type T whose checksum is the given checksum /// public abstract ValueTask GetAssetAsync(AssetPath assetPath, Checksum checksum, CancellationToken cancellationToken); - public abstract ValueTask GetAssetsAsync(AssetPath assetPath, HashSet checksums, Action callback, TArg arg, CancellationToken cancellationToken); + public abstract ValueTask GetAssetsAsync(AssetPath assetPath, HashSet checksums, Action callback, TArg arg, CancellationToken cancellationToken); public async Task CreateSolutionInfoAsync(Checksum solutionChecksum, CancellationToken cancellationToken) { @@ -114,9 +114,9 @@ public async Task> GetAssetsAsync( var results = new T[checksumSet.Count]; - await this.GetAssetsAsync( + await this.GetAssetsAsync( assetPath, checksumSet, - static (checksum, index, asset, results) => results[index] = asset, + (checksum, asset, results) => results[index] = asset, results, cancellationToken).ConfigureAwait(false); diff --git a/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs b/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs index 99fc3ecc3d1e8..d1fbfb54ba1a5 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs @@ -41,7 +41,7 @@ public override async ValueTask GetAssetAsync( var called = false; T? result = default; - await this.SynchronizeAssetsAsync(assetPath, checksums, (_, _, asset, _) => + await this.SynchronizeAssetsAsync(assetPath, checksums, (_, asset, _) => { Contract.ThrowIfTrue(called); called = true; @@ -53,7 +53,7 @@ await this.SynchronizeAssetsAsync(assetPath, checksums, (_, _, as } public override async ValueTask GetAssetsAsync( - AssetPath assetPath, HashSet checksums, Action callback, TArg arg, CancellationToken cancellationToken) + AssetPath assetPath, HashSet checksums, Action callback, TArg arg, CancellationToken cancellationToken) { await this.SynchronizeAssetsAsync(assetPath, checksums, callback, arg, cancellationToken).ConfigureAwait(false); } @@ -106,7 +106,7 @@ async ValueTask SynchronizeSolutionAssetsWorkerAsync() await this.SynchronizeAssetsAsync>( assetPath: AssetPath.SolutionAndTopLevelProjectsOnly, checksums, - static (checksum, index, asset, checksumToObjects) => checksumToObjects.Add(checksum, asset), + static (checksum, asset, checksumToObjects) => checksumToObjects.Add(checksum, asset), arg: checksumToObjects, cancellationToken).ConfigureAwait(false); // fourth, get all projects and documents in the solution @@ -185,7 +185,7 @@ static void AddAll(HashSet checksums, ChecksumCollection checksumColle } public async ValueTask SynchronizeAssetsAsync( - AssetPath assetPath, HashSet checksums, Action? callback, TArg? arg, CancellationToken cancellationToken) + AssetPath assetPath, HashSet checksums, Action? callback, TArg? arg, CancellationToken cancellationToken) { Contract.ThrowIfTrue(checksums.Contains(Checksum.Null)); if (checksums.Count == 0) @@ -210,7 +210,7 @@ public async ValueTask SynchronizeAssetsAsync( { if (_assetCache.TryGetAsset(checksum, out var existing)) { - AddResult(checksum, existing); + callback?.Invoke(checksum, existing, arg!); } else { @@ -267,7 +267,7 @@ void AddResult(Checksum checksum, T result) } private async ValueTask RequestAssetsAsync( - AssetPath assetPath, ReadOnlyMemory checksums, Action callback, CancellationToken cancellationToken) + AssetPath assetPath, ReadOnlyMemory checksums, Action callback, TArg arg, CancellationToken cancellationToken) { #if NETCOREAPP Contract.ThrowIfTrue(checksums.Span.Contains(Checksum.Null)); @@ -278,6 +278,6 @@ private async ValueTask RequestAssetsAsync( if (checksums.Length == 0) return; - await _assetSource.GetAssetsAsync(_solutionChecksum, assetPath, checksums, _serializerService, callback, cancellationToken).ConfigureAwait(false); + await _assetSource.GetAssetsAsync(_solutionChecksum, assetPath, checksums, _serializerService, callback, arg, cancellationToken).ConfigureAwait(false); } } diff --git a/src/Workspaces/Remote/ServiceHub/Host/SolutionAssetSource.cs b/src/Workspaces/Remote/ServiceHub/Host/SolutionAssetSource.cs index feacfe445672b..abfbee862b8cb 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/SolutionAssetSource.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/SolutionAssetSource.cs @@ -16,12 +16,13 @@ internal sealed class SolutionAssetSource(ServiceBrokerClient client) : IAssetSo { private readonly ServiceBrokerClient _client = client; - public async ValueTask GetAssetsAsync( + public async ValueTask GetAssetsAsync( Checksum solutionChecksum, AssetPath assetPath, ReadOnlyMemory checksums, ISerializerService serializerService, - Action assetCallback, + Action assetCallback, + TArg arg, CancellationToken cancellationToken) { // Make sure we are on the thread pool to avoid UI thread dependencies if external code uses ConfigureAwait(true) @@ -32,7 +33,7 @@ await RemoteCallback.InvokeServiceAsync( SolutionAssetProvider.ServiceDescriptor, (callback, cancellationToken) => callback.InvokeAsync( (proxy, pipeWriter, cancellationToken) => proxy.WriteAssetsAsync(pipeWriter, solutionChecksum, assetPath, checksums, cancellationToken), - (pipeReader, cancellationToken) => RemoteHostAssetSerialization.ReadDataAsync(pipeReader, solutionChecksum, checksums.Length, serializerService, assetCallback, cancellationToken), + (pipeReader, cancellationToken) => RemoteHostAssetSerialization.ReadDataAsync(pipeReader, solutionChecksum, checksums.Length, serializerService, assetCallback, arg, cancellationToken), cancellationToken), cancellationToken).ConfigureAwait(false); } From 17a6af402f444c16330489032ce8986a8f1d6ab8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 8 Apr 2024 14:56:06 -0700 Subject: [PATCH 04/11] Closer --- src/Workspaces/Remote/Core/AbstractAssetProvider.cs | 8 ++++---- .../Remote/Core/RemoteHostAssetSerialization.cs | 8 ++++---- src/Workspaces/Remote/ServiceHub/Host/IAssetSource.cs | 2 +- .../Remote/ServiceHub/Host/SolutionAssetSource.cs | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs index 012958caed5b6..50771cb97760c 100644 --- a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs +++ b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs @@ -112,14 +112,14 @@ public async Task> GetAssetsAsync( using var _ = PooledHashSet.GetInstance(out var checksumSet); checksumSet.AddAll(checksums.Children); - var results = new T[checksumSet.Count]; + var results = ImmutableArray.CreateBuilder(checksumSet.Count); - await this.GetAssetsAsync( + await this.GetAssetsAsync.Builder>( assetPath, checksumSet, - (checksum, asset, results) => results[index] = asset, + static (checksum, asset, results) => results.Add(asset), results, cancellationToken).ConfigureAwait(false); - return ImmutableCollectionsMarshal.AsImmutableArray(results); + return results.MoveToImmutable(); } } diff --git a/src/Workspaces/Remote/Core/RemoteHostAssetSerialization.cs b/src/Workspaces/Remote/Core/RemoteHostAssetSerialization.cs index ba87b9308a31a..055a8d641142b 100644 --- a/src/Workspaces/Remote/Core/RemoteHostAssetSerialization.cs +++ b/src/Workspaces/Remote/Core/RemoteHostAssetSerialization.cs @@ -65,7 +65,7 @@ static void WriteAsset(ObjectWriter writer, ISerializerService serializer, Solut } public static ValueTask ReadDataAsync( - PipeReader pipeReader, Checksum solutionChecksum, int objectCount, ISerializerService serializerService, Action callback, TArg arg, CancellationToken cancellationToken) + PipeReader pipeReader, Checksum solutionChecksum, int objectCount, ISerializerService serializerService, Action callback, TArg arg, CancellationToken cancellationToken) { // Suppress ExecutionContext flow for asynchronous operations operate on the pipe. In addition to avoiding // ExecutionContext allocations, this clears the LogicalCallContext and avoids the need to clone data set by @@ -77,7 +77,7 @@ public static ValueTask ReadDataAsync( return ReadDataSuppressedFlowAsync(pipeReader, solutionChecksum, objectCount, serializerService, callback, arg, cancellationToken); static async ValueTask ReadDataSuppressedFlowAsync( - PipeReader pipeReader, Checksum solutionChecksum, int objectCount, ISerializerService serializerService, Action callback, TArg arg, CancellationToken cancellationToken) + PipeReader pipeReader, Checksum solutionChecksum, int objectCount, ISerializerService serializerService, Action callback, TArg arg, CancellationToken cancellationToken) { using var stream = await pipeReader.AsPrebufferedStreamAsync(cancellationToken).ConfigureAwait(false); ReadData(stream, solutionChecksum, objectCount, serializerService, callback, arg, cancellationToken); @@ -85,7 +85,7 @@ static async ValueTask ReadDataSuppressedFlowAsync( } public static void ReadData( - Stream stream, Checksum solutionChecksum, int objectCount, ISerializerService serializerService, Action callback, TArg arg, CancellationToken cancellationToken) + Stream stream, Checksum solutionChecksum, int objectCount, ISerializerService serializerService, Action callback, TArg arg, CancellationToken cancellationToken) { using var reader = ObjectReader.GetReader(stream, leaveOpen: true, cancellationToken); @@ -101,7 +101,7 @@ public static void ReadData( // in service hub, cancellation means simply closed stream var result = serializerService.Deserialize(kind, reader, cancellationToken); Contract.ThrowIfNull(result); - callback((T)result, arg); + callback(i, (T)result, arg); } } } diff --git a/src/Workspaces/Remote/ServiceHub/Host/IAssetSource.cs b/src/Workspaces/Remote/ServiceHub/Host/IAssetSource.cs index 30d3fe84dae88..018cb7f73e72d 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/IAssetSource.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/IAssetSource.cs @@ -20,7 +20,7 @@ ValueTask GetAssetsAsync( AssetPath assetPath, ReadOnlyMemory checksums, ISerializerService serializerService, - Action callback, + Action callback, TArg arg, CancellationToken cancellationToken); } diff --git a/src/Workspaces/Remote/ServiceHub/Host/SolutionAssetSource.cs b/src/Workspaces/Remote/ServiceHub/Host/SolutionAssetSource.cs index abfbee862b8cb..a46e6f905385e 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/SolutionAssetSource.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/SolutionAssetSource.cs @@ -21,7 +21,7 @@ public async ValueTask GetAssetsAsync( AssetPath assetPath, ReadOnlyMemory checksums, ISerializerService serializerService, - Action assetCallback, + Action assetCallback, TArg arg, CancellationToken cancellationToken) { From 5b5baaf0045be92a0675b84e0bd9a6fe26616267 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 8 Apr 2024 15:05:17 -0700 Subject: [PATCH 05/11] Fallout --- .../Remote/SerializationValidator.cs | 8 ++--- .../Fakes/SimpleAssetSource.cs | 6 ++-- .../Remote/ServiceHub/Host/AssetProvider.cs | 33 ++++++++----------- .../Host/RemoteWorkspace.SolutionCreator.cs | 4 +-- 4 files changed, 21 insertions(+), 30 deletions(-) diff --git a/src/VisualStudio/Core/Test.Next/Remote/SerializationValidator.cs b/src/VisualStudio/Core/Test.Next/Remote/SerializationValidator.cs index 7f6957f670a6a..fe56b10fe74d0 100644 --- a/src/VisualStudio/Core/Test.Next/Remote/SerializationValidator.cs +++ b/src/VisualStudio/Core/Test.Next/Remote/SerializationValidator.cs @@ -27,17 +27,13 @@ private sealed class AssetProvider(SerializationValidator validator) : AbstractA public override async ValueTask GetAssetAsync(AssetPath assetPath, Checksum checksum, CancellationToken cancellationToken) => await validator.GetValueAsync(checksum).ConfigureAwait(false); - public override async ValueTask> GetAssetsAsync(AssetPath assetPath, HashSet checksums, CancellationToken cancellationToken) + public override async ValueTask GetAssetsAsync(AssetPath assetPath, HashSet checksums, Action callback, TArg arg, CancellationToken cancellationToken) { - using var _ = ArrayBuilder<(Checksum checksum, T asset)>.GetInstance(out var result); - foreach (var checksum in checksums) { var value = await GetAssetAsync(assetPath, checksum, cancellationToken).ConfigureAwait(false); - result.Add((checksum, value)); + callback(checksum, value, arg); } - - return result.ToImmutable(); } } diff --git a/src/Workspaces/CoreTestUtilities/Fakes/SimpleAssetSource.cs b/src/Workspaces/CoreTestUtilities/Fakes/SimpleAssetSource.cs index 4805445160b9d..66af4956e40c8 100644 --- a/src/Workspaces/CoreTestUtilities/Fakes/SimpleAssetSource.cs +++ b/src/Workspaces/CoreTestUtilities/Fakes/SimpleAssetSource.cs @@ -18,8 +18,8 @@ namespace Microsoft.CodeAnalysis.Remote.Testing; /// internal sealed class SimpleAssetSource(ISerializerService serializerService, IReadOnlyDictionary map) : IAssetSource { - public ValueTask GetAssetsAsync( - Checksum solutionChecksum, AssetPath assetPath, ReadOnlyMemory checksums, ISerializerService deserializerService, Action callback, CancellationToken cancellationToken) + public ValueTask GetAssetsAsync( + Checksum solutionChecksum, AssetPath assetPath, ReadOnlyMemory checksums, ISerializerService deserializerService, Action callback, TArg arg, CancellationToken cancellationToken) { var index = 0; foreach (var checksum in checksums.Span) @@ -38,7 +38,7 @@ public ValueTask GetAssetsAsync( using var reader = ObjectReader.GetReader(stream, leaveOpen: true, cancellationToken); var asset = deserializerService.Deserialize(data.GetWellKnownSynchronizationKind(), reader, cancellationToken); Contract.ThrowIfNull(asset); - callback(index, (T)asset); + callback(index, (T)asset, arg); index++; } diff --git a/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs b/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs index d1fbfb54ba1a5..f7b8127370a9d 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs @@ -238,20 +238,20 @@ public async ValueTask SynchronizeAssetsAsync( { var missingChecksumsMemory = new ReadOnlyMemory(missingChecksums, 0, missingChecksumsCount); - var indexExpectation = 0; - await RequestAssetsAsync(assetPath, missingChecksumsMemory, static (int index, T missingAsset) => - { - Contract.ThrowIfTrue(indexExpectation != index); - - var missingChecksum = missingChecksums[index]; - - AddResult(missingChecksum, missingAsset); - _assetCache.GetOrAdd(missingChecksum, missingAsset!); - - indexExpectation++; - }, cancellationToken).ConfigureAwait(false); + await RequestAssetsAsync( + assetPath, missingChecksumsMemory, + static ( + int index, + T missingAsset, + (AssetProvider assetProvider, Checksum[] missingChecksums, Action? callback, TArg? arg) tuple) => + { + var missingChecksum = tuple.missingChecksums[index]; - Contract.ThrowIfTrue(indexExpectation != missingChecksumsCount); + tuple.callback?.Invoke(missingChecksum, missingAsset, tuple.arg!); + tuple.assetProvider._assetCache.GetOrAdd(missingChecksum, missingAsset!); + }, + (this, missingChecksums, callback, arg), + cancellationToken).ConfigureAwait(false); } if (usePool) @@ -259,15 +259,10 @@ await RequestAssetsAsync(assetPath, missingChecksumsMemory, static (int index, T } return; - - void AddResult(Checksum checksum, T result) - { - callback?.Invoke(checksum, result); - } } private async ValueTask RequestAssetsAsync( - AssetPath assetPath, ReadOnlyMemory checksums, Action callback, TArg arg, CancellationToken cancellationToken) + AssetPath assetPath, ReadOnlyMemory checksums, Action callback, TArg arg, CancellationToken cancellationToken) { #if NETCOREAPP Contract.ThrowIfTrue(checksums.Span.Contains(Checksum.Null)); diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs index fb0ecc76ed3a9..6824139372aeb 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs @@ -220,7 +220,7 @@ private async Task UpdateProjectsAsync( await _assetProvider.GetAssetsAsync>( assetPath: AssetPath.SolutionAndTopLevelProjectsOnly, newChecksumsToSync, - static (checksum, _, newProjectStateChecksum, newProjectIdToStateChecksums) => + static (checksum, newProjectStateChecksum, newProjectIdToStateChecksums) => { Contract.ThrowIfTrue(checksum != newProjectStateChecksum.Checksum); newProjectIdToStateChecksums.Add(newProjectStateChecksum.ProjectId, newProjectStateChecksum); @@ -506,7 +506,7 @@ private async Task UpdateDocumentsAsync( await _assetProvider.GetAssetsAsync>( assetPath: AssetPath.ProjectAndDocuments(project.Id), newChecksumsToSync, - static (checksum, _, documentStateChecksum, newDocumentIdToStateChecksums) => + static (checksum, documentStateChecksum, newDocumentIdToStateChecksums) => { Contract.ThrowIfTrue(checksum != documentStateChecksum.Checksum); newDocumentIdToStateChecksums.Add(documentStateChecksum.DocumentId, documentStateChecksum); From 888e25a64545120e832fd057df481d795aa5ed4c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 8 Apr 2024 15:09:18 -0700 Subject: [PATCH 06/11] tests --- .../Core/Test.Next/Services/AssetProviderTests.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/VisualStudio/Core/Test.Next/Services/AssetProviderTests.cs b/src/VisualStudio/Core/Test.Next/Services/AssetProviderTests.cs index bbc1a1c351225..c80be5d4721fe 100644 --- a/src/VisualStudio/Core/Test.Next/Services/AssetProviderTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/AssetProviderTests.cs @@ -56,8 +56,9 @@ private static async Task TestAssetAsync(object data) var stored = await provider.GetAssetAsync(AssetPath.FullLookupForTesting, checksum, CancellationToken.None); Assert.Equal(data, stored); - var stored2 = await provider.GetAssetsAsync(AssetPath.FullLookupForTesting, new HashSet { checksum }, CancellationToken.None); - Assert.Equal(1, stored2.Length); + var stored2 = new List<(Checksum, object)>(); + await provider.GetAssetsAsync(AssetPath.FullLookupForTesting, new HashSet { checksum }, (checksum, asset, _) => stored2.Add((checksum, asset)), default, CancellationToken.None); + Assert.Equal(1, stored2.Count); Assert.Equal(checksum, stored2[0].Item1); Assert.Equal(data, stored2[0].Item2); @@ -83,7 +84,7 @@ public async Task TestAssetSynchronization() var assetSource = new SimpleAssetSource(workspace.Services.GetService(), map); var service = new AssetProvider(sessionId, storage, assetSource, remoteWorkspace.Services.GetService()); - await service.SynchronizeAssetsAsync(AssetPath.FullLookupForTesting, new HashSet(map.Keys), results: null, CancellationToken.None); + await service.SynchronizeAssetsAsync(AssetPath.FullLookupForTesting, new HashSet(map.Keys), callback: null, arg: default, CancellationToken.None); foreach (var kv in map) { From 5c4831f135c8a3b521317f0a6c62d0a39fb05703 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 8 Apr 2024 16:53:40 -0700 Subject: [PATCH 07/11] Add assrt --- src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs b/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs index f7b8127370a9d..0add2708a94a6 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs @@ -47,6 +47,8 @@ await this.SynchronizeAssetsAsync(assetPath, checksums, (_, asset called = true; result = asset; }, default, cancellationToken).ConfigureAwait(false); + + Contract.ThrowIfFalse(called); Contract.ThrowIfNull((object?)result); return result; From 8cdec8d5698f3c11ec24ea7e34db414594023382 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 8 Apr 2024 18:00:59 -0700 Subject: [PATCH 08/11] remove --- src/Workspaces/Remote/Core/AbstractAssetProvider.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs index 50771cb97760c..21a7a7fce7c21 100644 --- a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs +++ b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; From a5a76055a3781786b39ce44cf494729bff2b2666 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 8 Apr 2024 18:03:31 -0700 Subject: [PATCH 09/11] array builder --- src/Workspaces/Remote/Core/AbstractAssetProvider.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs index 21a7a7fce7c21..899a64d51885f 100644 --- a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs +++ b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs @@ -108,17 +108,17 @@ public async Task CreateDocumentInfoAsync( public async Task> GetAssetsAsync( AssetPath assetPath, ChecksumCollection checksums, CancellationToken cancellationToken) where T : class { - using var _ = PooledHashSet.GetInstance(out var checksumSet); + using var _1 = PooledHashSet.GetInstance(out var checksumSet); checksumSet.AddAll(checksums.Children); - var results = ImmutableArray.CreateBuilder(checksumSet.Count); + using var _2 = ArrayBuilder.GetInstance(checksumSet.Count, out var builder); - await this.GetAssetsAsync.Builder>( + await this.GetAssetsAsync>( assetPath, checksumSet, - static (checksum, asset, results) => results.Add(asset), - results, + static (checksum, asset, builder) => builder.Add(asset), + builder, cancellationToken).ConfigureAwait(false); - return results.MoveToImmutable(); + return builder.ToImmutableAndClear(); } } From 0896aa6fb5a34aa28cc107152dfb10a130c84d3b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 8 Apr 2024 18:05:15 -0700 Subject: [PATCH 10/11] static lambda --- .../Remote/ServiceHub/Host/AssetProvider.cs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs b/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs index 0add2708a94a6..ea4b5ec54278d 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs @@ -39,19 +39,15 @@ public override async ValueTask GetAssetAsync( using var _1 = PooledHashSet.GetInstance(out var checksums); checksums.Add(checksum); - var called = false; - T? result = default; - await this.SynchronizeAssetsAsync(assetPath, checksums, (_, asset, _) => - { - Contract.ThrowIfTrue(called); - called = true; - result = asset; - }, default, cancellationToken).ConfigureAwait(false); + using var _2 = ArrayBuilder.GetInstance(1, out var builder); + await this.SynchronizeAssetsAsync>( + assetPath, checksums, + static (_, asset, builder) => builder.Add(asset), + builder, cancellationToken).ConfigureAwait(false); - Contract.ThrowIfFalse(called); - Contract.ThrowIfNull((object?)result); + Contract.ThrowIfTrue(builder.Count != 1); - return result; + return builder[0]; } public override async ValueTask GetAssetsAsync( From b54747d4d3b1fa3f80e1ad04c174ac2f7d26eb6c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 8 Apr 2024 18:05:54 -0700 Subject: [PATCH 11/11] wrap --- src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs b/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs index ea4b5ec54278d..96fea39e0c652 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/AssetProvider.cs @@ -103,7 +103,8 @@ async ValueTask SynchronizeSolutionAssetsWorkerAsync() using var _2 = PooledDictionary.GetInstance(out var checksumToObjects); await this.SynchronizeAssetsAsync>( - assetPath: AssetPath.SolutionAndTopLevelProjectsOnly, checksums, + assetPath: AssetPath.SolutionAndTopLevelProjectsOnly, + checksums, static (checksum, asset, checksumToObjects) => checksumToObjects.Add(checksum, asset), arg: checksumToObjects, cancellationToken).ConfigureAwait(false);